[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class PhrictionDocumentController 4 extends PhrictionController { 5 6 private $slug; 7 8 public function shouldAllowPublic() { 9 return true; 10 } 11 12 public function willProcessRequest(array $data) { 13 $this->slug = $data['slug']; 14 } 15 16 public function processRequest() { 17 $request = $this->getRequest(); 18 $user = $request->getUser(); 19 20 $slug = PhabricatorSlug::normalize($this->slug); 21 if ($slug != $this->slug) { 22 $uri = PhrictionDocument::getSlugURI($slug); 23 // Canonicalize pages to their one true URI. 24 return id(new AphrontRedirectResponse())->setURI($uri); 25 } 26 27 require_celerity_resource('phriction-document-css'); 28 29 $document = id(new PhrictionDocumentQuery()) 30 ->setViewer($user) 31 ->withSlugs(array($slug)) 32 ->executeOne(); 33 34 $version_note = null; 35 $core_content = ''; 36 $move_notice = ''; 37 $properties = null; 38 39 if (!$document) { 40 41 $document = PhrictionDocument::initializeNewDocument($user, $slug); 42 43 $create_uri = '/phriction/edit/?slug='.$slug; 44 45 $notice = new AphrontErrorView(); 46 $notice->setSeverity(AphrontErrorView::SEVERITY_NODATA); 47 $notice->setTitle(pht('No content here!')); 48 $notice->appendChild( 49 pht( 50 'No document found at %s. You can <strong>'. 51 '<a href="%s">create a new document here</a></strong>.', 52 phutil_tag('tt', array(), $slug), 53 $create_uri)); 54 $core_content = $notice; 55 56 $page_title = pht('Page Not Found'); 57 } else { 58 $version = $request->getInt('v'); 59 if ($version) { 60 $content = id(new PhrictionContent())->loadOneWhere( 61 'documentID = %d AND version = %d', 62 $document->getID(), 63 $version); 64 if (!$content) { 65 return new Aphront404Response(); 66 } 67 68 if ($content->getID() != $document->getContentID()) { 69 $vdate = phabricator_datetime($content->getDateCreated(), $user); 70 $version_note = new AphrontErrorView(); 71 $version_note->setSeverity(AphrontErrorView::SEVERITY_NOTICE); 72 $version_note->appendChild( 73 pht('You are viewing an older version of this document, as it '. 74 'appeared on %s.', $vdate)); 75 } 76 } else { 77 $content = id(new PhrictionContent())->load($document->getContentID()); 78 } 79 $page_title = $content->getTitle(); 80 81 $properties = $this 82 ->buildPropertyListView($document, $content, $slug); 83 84 $doc_status = $document->getStatus(); 85 $current_status = $content->getChangeType(); 86 if ($current_status == PhrictionChangeType::CHANGE_EDIT || 87 $current_status == PhrictionChangeType::CHANGE_MOVE_HERE) { 88 89 $core_content = $content->renderContent($user); 90 } else if ($current_status == PhrictionChangeType::CHANGE_DELETE) { 91 $notice = new AphrontErrorView(); 92 $notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE); 93 $notice->setTitle(pht('Document Deleted')); 94 $notice->appendChild( 95 pht('This document has been deleted. You can edit it to put new '. 96 'content here, or use history to revert to an earlier version.')); 97 $core_content = $notice->render(); 98 } else if ($current_status == PhrictionChangeType::CHANGE_STUB) { 99 $notice = new AphrontErrorView(); 100 $notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE); 101 $notice->setTitle(pht('Empty Document')); 102 $notice->appendChild( 103 pht('This document is empty. You can edit it to put some proper '. 104 'content here.')); 105 $core_content = $notice->render(); 106 } else if ($current_status == PhrictionChangeType::CHANGE_MOVE_AWAY) { 107 $new_doc_id = $content->getChangeRef(); 108 109 $slug_uri = null; 110 111 // If the new document exists and the viewer can see it, provide a link 112 // to it. Otherwise, render a generic message. 113 $new_docs = id(new PhrictionDocumentQuery()) 114 ->setViewer($user) 115 ->withIDs(array($new_doc_id)) 116 ->execute(); 117 if ($new_docs) { 118 $new_doc = head($new_docs); 119 $slug_uri = PhrictionDocument::getSlugURI($new_doc->getSlug()); 120 } 121 122 $notice = id(new AphrontErrorView()) 123 ->setSeverity(AphrontErrorView::SEVERITY_NOTICE); 124 125 if ($slug_uri) { 126 $notice->appendChild( 127 phutil_tag( 128 'p', 129 array(), 130 pht( 131 'This document has been moved to %s. You can edit it to put '. 132 'new content here, or use history to revert to an earlier '. 133 'version.', 134 phutil_tag('a', array('href' => $slug_uri), $slug_uri)))); 135 } else { 136 $notice->appendChild( 137 phutil_tag( 138 'p', 139 array(), 140 pht( 141 'This document has been moved. You can edit it to put new '. 142 'contne here, or use history to revert to an earlier '. 143 'version.'))); 144 } 145 146 $core_content = $notice->render(); 147 } else { 148 throw new Exception("Unknown document status '{$doc_status}'!"); 149 } 150 151 $move_notice = null; 152 if ($current_status == PhrictionChangeType::CHANGE_MOVE_HERE) { 153 $from_doc_id = $content->getChangeRef(); 154 155 $slug_uri = null; 156 157 // If the old document exists and is visible, provide a link to it. 158 $from_docs = id(new PhrictionDocumentQuery()) 159 ->setViewer($user) 160 ->withIDs(array($from_doc_id)) 161 ->execute(); 162 if ($from_docs) { 163 $from_doc = head($from_docs); 164 $slug_uri = PhrictionDocument::getSlugURI($from_doc->getSlug()); 165 } 166 167 $move_notice = id(new AphrontErrorView()) 168 ->setSeverity(AphrontErrorView::SEVERITY_NOTICE); 169 170 if ($slug_uri) { 171 $move_notice->appendChild( 172 pht( 173 'This document was moved from %s.', 174 phutil_tag('a', array('href' => $slug_uri), $slug_uri))); 175 } else { 176 // Render this for consistency, even though it's a bit silly. 177 $move_notice->appendChild( 178 pht('This document was moved from elsewhere.')); 179 } 180 } 181 } 182 183 $children = $this->renderDocumentChildren($slug); 184 185 $actions = $this->buildActionView($user, $document); 186 187 $crumbs = $this->buildApplicationCrumbs(); 188 $crumbs->setActionList($actions); 189 $crumb_views = $this->renderBreadcrumbs($slug); 190 foreach ($crumb_views as $view) { 191 $crumbs->addCrumb($view); 192 } 193 194 $header = id(new PHUIHeaderView()) 195 ->setUser($user) 196 ->setPolicyObject($document) 197 ->setHeader($page_title); 198 199 $prop_list = null; 200 if ($properties) { 201 $prop_list = new PHUIPropertyGroupView(); 202 $prop_list->addPropertyList($properties); 203 } 204 205 $page_content = id(new PHUIDocumentView()) 206 ->setOffset(true) 207 ->setFontKit(PHUIDocumentView::FONT_SOURCE_SANS) 208 ->setHeader($header) 209 ->appendChild( 210 array( 211 $actions, 212 $prop_list, 213 $version_note, 214 $move_notice, 215 $core_content, 216 )); 217 218 $core_page = phutil_tag( 219 'div', 220 array( 221 'class' => 'phriction-offset', 222 ), 223 array( 224 $page_content, 225 $children, 226 )); 227 228 return $this->buildApplicationPage( 229 array( 230 $crumbs->render(), 231 $core_page, 232 ), 233 array( 234 'pageObjects' => array($document->getPHID()), 235 'title' => $page_title, 236 )); 237 238 } 239 240 private function buildPropertyListView( 241 PhrictionDocument $document, 242 PhrictionContent $content, 243 $slug) { 244 245 $viewer = $this->getRequest()->getUser(); 246 $view = id(new PHUIPropertyListView()) 247 ->setUser($viewer) 248 ->setObject($document); 249 250 $phids = array($content->getAuthorPHID()); 251 252 $this->loadHandles($phids); 253 254 $view->addProperty( 255 pht('Last Author'), 256 $this->getHandle($content->getAuthorPHID())->renderLink()); 257 258 $age = time() - $content->getDateCreated(); 259 $age = floor($age / (60 * 60 * 24)); 260 if ($age < 1) { 261 $when = pht('Today'); 262 } else if ($age == 1) { 263 $when = pht('Yesterday'); 264 } else { 265 $when = pht('%d Days Ago', $age); 266 } 267 $view->addProperty(pht('Last Updated'), $when); 268 269 return $view; 270 } 271 272 private function buildActionView( 273 PhabricatorUser $user, 274 PhrictionDocument $document) { 275 $can_edit = PhabricatorPolicyFilter::hasCapability( 276 $user, 277 $document, 278 PhabricatorPolicyCapability::CAN_EDIT); 279 280 $slug = PhabricatorSlug::normalize($this->slug); 281 282 $action_view = id(new PhabricatorActionListView()) 283 ->setUser($user) 284 ->setObjectURI($this->getRequest()->getRequestURI()) 285 ->setObject($document); 286 287 if (!$document->getID()) { 288 return $action_view->addAction( 289 id(new PhabricatorActionView()) 290 ->setName(pht('Create This Document')) 291 ->setIcon('fa-plus-square') 292 ->setHref('/phriction/edit/?slug='.$slug)); 293 } 294 295 $action_view->addAction( 296 id(new PhabricatorActionView()) 297 ->setName(pht('Edit Document')) 298 ->setIcon('fa-pencil') 299 ->setHref('/phriction/edit/'.$document->getID().'/')); 300 301 if ($document->getStatus() == PhrictionDocumentStatus::STATUS_EXISTS) { 302 $action_view->addAction( 303 id(new PhabricatorActionView()) 304 ->setName(pht('Move Document')) 305 ->setIcon('fa-arrows') 306 ->setHref('/phriction/move/'.$document->getID().'/') 307 ->setWorkflow(true)); 308 309 $action_view->addAction( 310 id(new PhabricatorActionView()) 311 ->setName(pht('Delete Document')) 312 ->setIcon('fa-times') 313 ->setHref('/phriction/delete/'.$document->getID().'/') 314 ->setWorkflow(true)); 315 } 316 317 return 318 $action_view->addAction( 319 id(new PhabricatorActionView()) 320 ->setName(pht('View History')) 321 ->setIcon('fa-list') 322 ->setHref(PhrictionDocument::getSlugURI($slug, 'history'))); 323 } 324 325 private function renderDocumentChildren($slug) { 326 327 $d_child = PhabricatorSlug::getDepth($slug) + 1; 328 $d_grandchild = PhabricatorSlug::getDepth($slug) + 2; 329 $limit = 250; 330 331 $query = id(new PhrictionDocumentQuery()) 332 ->setViewer($this->getRequest()->getUser()) 333 ->withDepths(array($d_child, $d_grandchild)) 334 ->withSlugPrefix($slug == '/' ? '' : $slug) 335 ->withStatuses(array( 336 PhrictionDocumentStatus::STATUS_EXISTS, 337 PhrictionDocumentStatus::STATUS_STUB, 338 )) 339 ->setLimit($limit) 340 ->setOrder(PhrictionDocumentQuery::ORDER_HIERARCHY) 341 ->needContent(true); 342 343 $children = $query->execute(); 344 if (!$children) { 345 return; 346 } 347 348 // We're going to render in one of three modes to try to accommodate 349 // different information scales: 350 // 351 // - If we found fewer than $limit rows, we know we have all the children 352 // and grandchildren and there aren't all that many. We can just render 353 // everything. 354 // - If we found $limit rows but the results included some grandchildren, 355 // we just throw them out and render only the children, as we know we 356 // have them all. 357 // - If we found $limit rows and the results have no grandchildren, we 358 // have a ton of children. Render them and then let the user know that 359 // this is not an exhaustive list. 360 361 if (count($children) == $limit) { 362 $more_children = true; 363 foreach ($children as $child) { 364 if ($child->getDepth() == $d_grandchild) { 365 $more_children = false; 366 } 367 } 368 $show_grandchildren = false; 369 } else { 370 $show_grandchildren = true; 371 $more_children = false; 372 } 373 374 $children_dicts = array(); 375 $grandchildren_dicts = array(); 376 foreach ($children as $key => $child) { 377 $child_dict = array( 378 'slug' => $child->getSlug(), 379 'depth' => $child->getDepth(), 380 'title' => $child->getContent()->getTitle(),); 381 if ($child->getDepth() == $d_child) { 382 $children_dicts[] = $child_dict; 383 continue; 384 } else { 385 unset($children[$key]); 386 if ($show_grandchildren) { 387 $ancestors = PhabricatorSlug::getAncestry($child->getSlug()); 388 $grandchildren_dicts[end($ancestors)][] = $child_dict; 389 } 390 } 391 } 392 393 // Fill in any missing children. 394 $known_slugs = mpull($children, null, 'getSlug'); 395 foreach ($grandchildren_dicts as $slug => $ignored) { 396 if (empty($known_slugs[$slug])) { 397 $children_dicts[] = array( 398 'slug' => $slug, 399 'depth' => $d_child, 400 'title' => PhabricatorSlug::getDefaultTitle($slug), 401 'empty' => true, 402 ); 403 } 404 } 405 406 $children_dicts = isort($children_dicts, 'title'); 407 408 $list = array(); 409 foreach ($children_dicts as $child) { 410 $list[] = hsprintf('<li>'); 411 $list[] = $this->renderChildDocumentLink($child); 412 $grand = idx($grandchildren_dicts, $child['slug'], array()); 413 if ($grand) { 414 $list[] = hsprintf('<ul>'); 415 foreach ($grand as $grandchild) { 416 $list[] = hsprintf('<li>'); 417 $list[] = $this->renderChildDocumentLink($grandchild); 418 $list[] = hsprintf('</li>'); 419 } 420 $list[] = hsprintf('</ul>'); 421 } 422 $list[] = hsprintf('</li>'); 423 } 424 if ($more_children) { 425 $list[] = phutil_tag('li', array(), pht('More...')); 426 } 427 428 $content = array( 429 phutil_tag( 430 'div', 431 array( 432 'class' => 'phriction-children-header '. 433 'sprite-gradient gradient-lightblue-header', 434 ), 435 pht('Document Hierarchy')), 436 phutil_tag( 437 'div', 438 array( 439 'class' => 'phriction-children', 440 ), 441 phutil_tag('ul', array(), $list)), 442 ); 443 444 return id(new PHUIDocumentView()) 445 ->setOffset(true) 446 ->appendChild($content); 447 } 448 449 private function renderChildDocumentLink(array $info) { 450 $title = nonempty($info['title'], pht('(Untitled Document)')); 451 $item = phutil_tag( 452 'a', 453 array( 454 'href' => PhrictionDocument::getSlugURI($info['slug']), 455 ), 456 $title); 457 458 if (isset($info['empty'])) { 459 $item = phutil_tag('em', array(), $item); 460 } 461 462 return $item; 463 } 464 465 protected function getDocumentSlug() { 466 return $this->slug; 467 } 468 469 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |