diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 74353d4215..71113b0984 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -586,6 +586,7 @@ phutil_register_library_map(array( 'PhrictionDAO' => 'applications/phriction/storage/base', 'PhrictionDocument' => 'applications/phriction/storage/document', 'PhrictionDocumentController' => 'applications/phriction/controller/document', + 'PhrictionDocumentTestCase' => 'applications/phriction/storage/document/__tests__', 'PhrictionEditController' => 'applications/phriction/controller/edit', 'PhrictionHistoryController' => 'applications/phriction/controller/history', 'PhrictionListController' => 'applications/phriction/controller/list', @@ -1085,6 +1086,7 @@ phutil_register_library_map(array( 'PhrictionDAO' => 'PhabricatorLiskDAO', 'PhrictionDocument' => 'PhrictionDAO', 'PhrictionDocumentController' => 'PhrictionController', + 'PhrictionDocumentTestCase' => 'PhabricatorTestCase', 'PhrictionEditController' => 'PhrictionController', 'PhrictionHistoryController' => 'PhrictionController', 'PhrictionListController' => 'PhrictionController', diff --git a/src/applications/phriction/controller/document/PhrictionDocumentController.php b/src/applications/phriction/controller/document/PhrictionDocumentController.php index 2da85b1d31..01e68e1568 100644 --- a/src/applications/phriction/controller/document/PhrictionDocumentController.php +++ b/src/applications/phriction/controller/document/PhrictionDocumentController.php @@ -42,6 +42,8 @@ class PhrictionDocumentController 'slug = %s', $slug); + $breadcrumbs = $this->renderBreadcrumbs($slug); + if (!$document) { $create_uri = '/phriction/edit/?slug='.$slug; @@ -112,6 +114,7 @@ class PhrictionDocumentController '
'. $button. '

'.phutil_escape_html($page_title).'

'. + $breadcrumbs. '
'. $page_content; @@ -124,4 +127,57 @@ class PhrictionDocumentController } + private function renderBreadcrumbs($slug) { + + $ancestor_handles = array(); + $ancestral_slugs = PhrictionDocument::getSlugAncestry($slug); + $ancestral_slugs[] = $slug; + if ($ancestral_slugs) { + $empty_slugs = array_fill_keys($ancestral_slugs, null); + $ancestors = id(new PhrictionDocument())->loadAllWhere( + 'slug IN (%Ls)', + $ancestral_slugs); + $ancestors = mpull($ancestors, null, 'getSlug'); + + $ancestor_phids = mpull($ancestors, 'getPHID'); + $handles = array(); + if ($ancestor_phids) { + $handles = id(new PhabricatorObjectHandleData($ancestor_phids)) + ->loadHandles(); + } + + $ancestor_handles = array(); + foreach ($ancestral_slugs as $slug) { + if (isset($ancestors[$slug])) { + $ancestor_handles[] = $handles[$ancestors[$slug]->getPHID()]; + } else { + $handle = new PhabricatorObjectHandle(); + $handle->setName(PhrictionDocument::getDefaultSlugTitle($slug)); + $handle->setURI(PhrictionDocument::getSlugURI($slug)); + $ancestor_handles[] = $handle; + } + } + } + + $breadcrumbs = array(); + foreach ($ancestor_handles as $ancestor_handle) { + $breadcrumbs[] = $ancestor_handle->renderLink(); + } + + $list = phutil_render_tag( + 'a', + array( + 'href' => '/phriction/', + ), + 'Document Index'); + + return + '
'. + $list.' · '. + ''. + implode(" \xC2\xBB ", $breadcrumbs). + ''. + '
'; + } + } diff --git a/src/applications/phriction/controller/document/__init__.php b/src/applications/phriction/controller/document/__init__.php index 6d7452f7b7..17fc281f88 100644 --- a/src/applications/phriction/controller/document/__init__.php +++ b/src/applications/phriction/controller/document/__init__.php @@ -8,6 +8,7 @@ phutil_require_module('phabricator', 'aphront/response/redirect'); phutil_require_module('phabricator', 'applications/markup/engine'); +phutil_require_module('phabricator', 'applications/phid/handle'); phutil_require_module('phabricator', 'applications/phid/handle/data'); phutil_require_module('phabricator', 'applications/phriction/controller/base'); phutil_require_module('phabricator', 'applications/phriction/storage/content'); diff --git a/src/applications/phriction/controller/edit/PhrictionEditController.php b/src/applications/phriction/controller/edit/PhrictionEditController.php index f7bad2cf12..0f7d7d2f4a 100644 --- a/src/applications/phriction/controller/edit/PhrictionEditController.php +++ b/src/applications/phriction/controller/edit/PhrictionEditController.php @@ -40,12 +40,9 @@ class PhrictionEditController } $content = id(new PhrictionContent())->load($document->getContentID()); } else if ($slug) { - $document = null; - if ($slug) { - $document = id(new PhrictionDocument())->loadOneWhere( - 'slug = %s', - $slug); - } + $document = id(new PhrictionDocument())->loadOneWhere( + 'slug = %s', + $slug); if ($document) { $content = id(new PhrictionContent())->load($document->getContentID()); @@ -56,14 +53,7 @@ class PhrictionEditController $content = new PhrictionContent(); $content->setSlug($slug); - $default_title = null; - if ($slug) { - $parts = explode('/', trim($slug, '/')); - $default_title = end($parts); - $default_title = str_replace('_', ' ', $default_title); - $default_title = ucwords($default_title); - } - $default_title = nonempty($default_title, 'New Document'); + $default_title = PhrictionDocument::getDefaultSlugTitle($slug); $content->setTitle($default_title); } } else { diff --git a/src/applications/phriction/storage/document/PhrictionDocument.php b/src/applications/phriction/storage/document/PhrictionDocument.php index d65fcec63f..8dc1cb164f 100644 --- a/src/applications/phriction/storage/document/PhrictionDocument.php +++ b/src/applications/phriction/storage/document/PhrictionDocument.php @@ -57,7 +57,6 @@ class PhrictionDocument extends PhrictionDAO { } } - public static function normalizeSlug($slug) { // TODO: We need to deal with unicode at some point, this is just a very @@ -72,9 +71,51 @@ class PhrictionDocument extends PhrictionDAO { return $slug.'/'; } + public static function getDefaultSlugTitle($slug) { + $parts = explode('/', trim($slug, '/')); + $default_title = end($parts); + $default_title = str_replace('_', ' ', $default_title); + $default_title = ucwords($default_title); + $default_title = nonempty($default_title, 'Untitled Document'); + return $default_title; + } + + public static function getSlugAncestry($slug) { + $slug = self::normalizeSlug($slug); + + if ($slug == '/') { + return array(); + } + + $ancestors = array( + '/', + ); + + $slug = explode('/', $slug); + array_pop($slug); + array_pop($slug); + + $accumulate = ''; + foreach ($slug as $part) { + $accumulate .= $part.'/'; + $ancestors[] = $accumulate; + } + + return $ancestors; + } + + public static function getSlugDepth($slug) { + $slug = self::normalizeSlug($slug); + if ($slug == '/') { + return 0; + } else { + return substr_count($slug, '/'); + } + } + public function setSlug($slug) { $this->slug = self::normalizeSlug($slug); - $this->depth = substr_count($this->slug, '/'); + $this->depth = self::getSlugDepth($slug); return $this; } diff --git a/src/applications/phriction/storage/document/__init__.php b/src/applications/phriction/storage/document/__init__.php index bf16f121cd..f1fb50f9da 100644 --- a/src/applications/phriction/storage/document/__init__.php +++ b/src/applications/phriction/storage/document/__init__.php @@ -10,5 +10,7 @@ phutil_require_module('phabricator', 'applications/phid/constants'); phutil_require_module('phabricator', 'applications/phid/storage/phid'); phutil_require_module('phabricator', 'applications/phriction/storage/base'); +phutil_require_module('phutil', 'utils'); + phutil_require_source('PhrictionDocument.php'); diff --git a/src/applications/phriction/storage/document/__tests__/PhrictionDocumentTestCase.php b/src/applications/phriction/storage/document/__tests__/PhrictionDocumentTestCase.php new file mode 100644 index 0000000000..fb25732e02 --- /dev/null +++ b/src/applications/phriction/storage/document/__tests__/PhrictionDocumentTestCase.php @@ -0,0 +1,75 @@ + '/', + '/' => '/', + '//' => '/', + '/derp/' => 'derp/', + 'derp' => 'derp/', + 'derp//derp' => 'derp/derp/', + 'DERP//DERP' => 'derp/derp/', + 'a B c' => 'a_b_c/', + ); + + foreach ($slugs as $slug => $normal) { + $this->assertEqual( + $normal, + PhrictionDocument::normalizeSlug($slug), + "Normalization of '{$slug}'"); + } + } + + public function testSlugAncestry() { + $slugs = array( + '/' => array(), + 'pokemon/' => array('/'), + 'pokemon/squirtle/' => array('/', 'pokemon/'), + ); + + foreach ($slugs as $slug => $ancestry) { + $this->assertEqual( + $ancestry, + PhrictionDocument::getSlugAncestry($slug), + "Ancestry of '{$slug}'"); + } + } + + public function testSlugDepth() { + $slugs = array( + '/' => 0, + 'a/' => 1, + 'a/b/' => 2, + 'a////b/' => 2, + ); + + foreach ($slugs as $slug => $depth) { + $this->assertEqual( + $depth, + PhrictionDocument::getSlugDepth($slug), + "Depth of '{$slug}'"); + } + } + +} diff --git a/src/applications/phriction/storage/document/__tests__/__init__.php b/src/applications/phriction/storage/document/__tests__/__init__.php new file mode 100644 index 0000000000..aaec2c0220 --- /dev/null +++ b/src/applications/phriction/storage/document/__tests__/__init__.php @@ -0,0 +1,13 @@ +