1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
5 * This is alot simpler now...
7 * Mostly just dumps out raw text from files..
13 require_once 'MTrack/Attachment.php';
14 require_once 'MTrackWeb.php';
16 class MTrackWeb_Wiki extends MTrackWeb
21 var $hasHistory = false;
25 return parent::getAuth();
30 $this->pi = empty($pi) ? 'WikiStart' : ($pi . $this->bootLoader->ext);
32 if (!isset($_REQUEST['ajax_body'])) {
33 $this->title = "Browse: " . $this->pi;
36 $this->masterTemplate = 'wiki.html';
39 $this->edit = isset($_REQUEST['edit']) ? (int)$_REQUEST['edit'] : 0;
42 $this->hasHistory = 0;
45 $this->doc = new MTrack_Wiki_Item($this->pi);
46 $this->canEdit = $this->hasPerm('MTrack.Wiki','E');
48 // we might add more perms based on project later..
50 if (!$this->canEdit && $this->edit) {
51 return HTML_FlexyFramework::run('Noperm');
54 $this->hasHistory = $this->doc->file ? 1: 0;
56 // blank doc.. on edit..
57 if ($this->doc->file === null && $this->edit) {
58 $this->doc = new MTrack_Wiki_Item($this->pi);
59 $this->doc->content = " = {$this->pi} =\n";
65 $this->title = $this->pi;
67 $this->title .= " (edit)";
70 $this->canEdit = $this->edit ? false : $this->canEdit ; // if they are editing remove that permission..
73 if ($this->doc->file) {
74 $this->evt = $this->doc->file->getChangeEvent();
75 if (!strlen($this->evt->changelog)) {
76 $this->evt->changelog = 'Changed';
80 if (!$this->edit && !$this->hasHistory) {
88 if (isset($_POST['preview'])) {
89 $this->showPreview = 1;
90 $this->preview = MTrackWiki::format_to_html($content);
92 $this->doc->contentb64= base64_encode($this->doc->content);
93 $this->doc->content = isset($_POST['content']) ? $_POST['content'] : $this->doc->content;
94 $this->comment = isset($_POST['comment']) ? $_POST['comment'] : '';
98 $action = isset($_GET['action']) ? $_GET['action'] : 'view';
102 $this->actionView = 1;
106 $this->actionList = 1;
109 $this->build_help_tree($htree, dirname(__FILE__) . '/../defaults/help');
110 $this->helptree = $htree;
113 /* get the page names into a tree format */
116 $root = MTrack_Wiki_Item::getRepoAndRoot($repo);
117 $suf = MTrackConfig::get('core', 'wikifilenamesuffix'); // fixme!!!!
118 build_tree($tree, $repo, $root, $suf);
124 $this->actionRecent = 1;
125 $root = MTrack_Wiki_Item::getRepoAndRoot($repo);
126 $this->recent = $repo->history(null, 100) ;
127 foreach ($this->recent as $e) {
128 list($e->page) = $e->files;
130 $e->page = substr($e->page, strlen($root)+1);
137 //echo '<PRE>';print_r($this);echo '</PRE>';
140 function is_content_conflicted($content)
142 if (preg_match("/^(<+)\s+(mine|theirs|original)\s*$/m", $content)) {
147 function normalize_text($text) {
148 return rtrim($text) . "\n";
154 if (isset($_POST['cancel'])) {
155 header("Location: {$this->baseURL}/Wiki/{$this->pi}");
160 if (!MTrackCaptcha::check('wiki')) {
161 $this->message = 'CAPTCHA failed, please try again';
165 /* to avoid annoying "you lose because someone else edited" errors,
166 * we compute the diff from the original content we had, and apply
167 * that to the current content of the object */
171 $orig = base64_decode($_POST['orig']);
172 $content = $_POST['content'];
174 /* for consistency, we always want a newline at the end, otherwise
175 * we can end up with some weird output from diff3 */
176 $orig = normalize_text($orig);
177 $content = normalize_text($content);
178 $this->doc->content = normalize_text($this->doc->content);
179 $this->conflicted = is_content_conflicted($content);
180 $tempdir = sys_get_temp_dir();
182 if (!$this->conflicted) {
183 $ofile = tempnam($tempdir, "mtrack");
184 $nfile = tempnam($tempdir, "mtrack");
185 $tfile = tempnam($tempdir, "mtrack");
186 $pfile = tempnam($tempdir, "mtrack");
188 require_once 'System.php';
189 $diff3 = System::which('diff3');
194 file_put_contents($ofile, $orig);
195 file_put_contents($nfile, $content);
196 file_put_contents($tfile, $this->doc->content);
198 if (PHP_OS == 'SunOS') { // seriously does anyone use SunOS anymore???
199 exec("($diff3 -X $nfile $ofile $tfile ; echo '1,\$p') | ed - $nfile > $pfile",
200 $output = array(), $retval = 0);
202 exec("$diff3 -mX --label mine $nfile --label original $ofile --label theirs $tfile > $pfile",
203 $output = array(), $retval = 0);
207 /* see if there were merge conflicts */
209 $mine = preg_quote($nfile, '/');
210 $theirs = preg_quote($tfile, '/');
211 $orig = preg_quote($ofile, '/');
212 $content = file_get_contents($pfile);
214 if (PHP_OS == 'SunOS') {
215 $content = str_replace($nfile, 'mine', $content);
216 $content = str_replace($ofile, 'original', $content);
217 $content = str_replace($tfile, 'theirs', $content);
225 $this->conflicted = is_content_conflicted($content);
228 /* keep the merged version for editing purposes */
229 $_POST['content'] = $content;
230 /* our concept of the the original content is now what
231 * is currently saved */
232 $_POST['orig'] = base64_encode($this->doc->content);
234 if ($this->conflicted) {
235 $this->message = "Conflicting edits were detected; please correct them before saving";
237 $this->doc->content = $content;
239 $cs = MTrackChangeset::begin("wiki:{$this->pi}", $_POST['comment']);
240 $this->doc->save($cs);
241 if (is_array($_FILES['attachments'])) {
242 foreach ($_FILES['attachments']['name'] as $fileid => $name) {
244 switch ($_FILES['attachments']['error'][$fileid]) {
248 case UPLOAD_ERR_NO_FILE:
250 case UPLOAD_ERR_INI_SIZE:
251 case UPLOAD_ERR_FORM_SIZE:
252 $this->message = "Attachment(s) exceed the upload file size limit";
254 case UPLOAD_ERR_PARTIAL:
255 case UPLOAD_ERR_CANT_WRITE:
256 $this->message = "Attachment file upload failed";
258 case UPLOAD_ERR_NO_TMP_DIR:
259 $this->message = "Server configuration prevents upload due to missing temporary dir";
261 case UPLOAD_ERR_EXTENSION:
262 $this->message = "An extension prevented an upload from running";
264 if ($this->message !== null) {
265 throw new Exception($this->message);
268 MTrackAttachment::add("wiki:{$this->pi}",
269 $_FILES['attachments']['tmp_name'][$fileid],
270 $_FILES['attachments']['name'][$fileid],
275 MTrackAttachment::process_delete("wiki:{$this->pi}", $cs);
277 MTrack_Wiki_Item::commitNow();
279 } catch (Exception $e) {
280 $this->message = $e->getMessage();
285 /* we're good; go back to view mode */
286 header("Location: {$this->baseURL}/Wiki/{$this->pi}");
291 function build_help_tree(&$tree, $dir) {
292 foreach (scandir($dir) as $ent) {
293 if ($ent[0] == '.') {
296 $full = $dir . DIRECTORY_SEPARATOR . $ent;
299 $this->build_help_tree($kid, $full);
302 $tree[$ent] = array();
306 function build_tree(&$tree, $repo, $dir, $suf)
308 $items = $repo->readdir($dir);
309 foreach ($items as $file) {
310 $label = basename($file->name);
313 $this->build_tree($kid, $repo, $file->name, $suf);
314 $tree[$label] = $kid;
316 if ($suf && substr($label, -strlen($suf)) == $suf) {
317 $label = substr($label, 0, strlen($label) - strlen($suf));
319 $tree[$label] = array();
323 function emit_tree($root, $phppage,$parent='')
326 if (strlen($parent)) {
329 echo "<ul class='wikitree'>\n";
331 $knames = array_keys($root);
332 usort($knames, 'strnatcasecmp');
333 foreach ($knames as $key) {
335 $n = htmlentities($key, ENT_QUOTES, 'utf-8');
339 emit_tree($kids, $phppage,"$parent$key/");
341 echo "<a href=\"{$this->baseURL}/$phppage/$parent$n\">$n</a>";
349 function renderDeleteList() {
350 //return MTrackAttachment::renderDeleteList("wiki:{$this->pi}");
352 function renderList() {
353 //return MTrackAttachment::renderList("wiki:{$this->pi}");
358 return MTrackCaptcha::emit('wiki');