diff --git a/src/applications/phriction/storage/PhrictionDocument.php b/src/applications/phriction/storage/PhrictionDocument.php index f2060af2bf..0c553fdfab 100644 --- a/src/applications/phriction/storage/PhrictionDocument.php +++ b/src/applications/phriction/storage/PhrictionDocument.php @@ -1,102 +1,106 @@ true, self::CONFIG_TIMESTAMPS => false, ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorPHIDConstants::PHID_TYPE_WIKI); } public static function getSlugURI($slug, $type = 'document') { static $types = array( 'document' => '/w/', 'history' => '/phriction/history/', ); if (empty($types[$type])) { throw new Exception("Unknown URI type '{$type}'!"); } $prefix = $types[$type]; if ($slug == '/') { return $prefix; } else { + // NOTE: The effect here is to escape non-latin characters, since modern + // browsers deal with escaped UTF8 characters in a reasonable way (showing + // the user a readable URI) but older programs may not. + $slug = phutil_escape_uri($slug); return $prefix.$slug; } } public function setSlug($slug) { $this->slug = PhabricatorSlug::normalize($slug); $this->depth = PhabricatorSlug::getDepth($slug); return $this; } public function attachContent(PhrictionContent $content) { $this->contentObject = $content; return $this; } public function getContent() { if (!$this->contentObject) { throw new Exception("Attach content with attachContent() first."); } return $this->contentObject; } public static function isProjectSlug($slug) { $slug = PhabricatorSlug::normalize($slug); $prefix = 'projects/'; if ($slug == $prefix) { // The 'projects/' document is not itself a project slug. return false; } return !strncmp($slug, $prefix, strlen($prefix)); } public static function getProjectSlugIdentifier($slug) { if (!self::isProjectSlug($slug)) { throw new Exception("Slug '{$slug}' is not a project slug!"); } $slug = PhabricatorSlug::normalize($slug); $parts = explode('/', $slug); return $parts[1].'/'; } // TODO: Customize this? Copypasta from PhabricatorPaste. public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { return PhabricatorPolicies::POLICY_USER; } public function hasAutomaticCapability($capability, PhabricatorUser $user) { return false; } } diff --git a/src/infrastructure/util/PhabricatorSlug.php b/src/infrastructure/util/PhabricatorSlug.php index 0a95579218..9dfff16251 100644 --- a/src/infrastructure/util/PhabricatorSlug.php +++ b/src/infrastructure/util/PhabricatorSlug.php @@ -1,61 +1,58 @@ ]+@", '_', $slug); + $slug = preg_replace('@_+@', '_', $slug); $slug = trim($slug, '_'); return $slug.'/'; } public static function getDefaultTitle($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'); + $default_title = phutil_utf8_ucwords($default_title); + $default_title = nonempty($default_title, pht('Untitled Document')); return $default_title; } public static function getAncestry($slug) { $slug = self::normalize($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 getDepth($slug) { $slug = self::normalize($slug); if ($slug == '/') { return 0; } else { return substr_count($slug, '/'); } } } diff --git a/src/infrastructure/util/__tests__/PhabricatorSlugTestCase.php b/src/infrastructure/util/__tests__/PhabricatorSlugTestCase.php index 735727ac01..03b95f239c 100644 --- a/src/infrastructure/util/__tests__/PhabricatorSlugTestCase.php +++ b/src/infrastructure/util/__tests__/PhabricatorSlugTestCase.php @@ -1,58 +1,60 @@ '/', '/' => '/', '//' => '/', '&&&' => '/', '/derp/' => 'derp/', 'derp' => 'derp/', 'derp//derp' => 'derp/derp/', 'DERP//DERP' => 'derp/derp/', 'a B c' => 'a_b_c/', - '-1~2.3abcd' => '1_2_3abcd/', - "T\x95O\x95D\x95O" => 't_o_d_o/', + '-1~2.3abcd' => '-1~2.3abcd/', + "T\x00O\x00D\x00O" => "t_o_d_o/", + 'x#%&+=\\?<> y' => 'x_y/', + "\xE2\x98\x83" => "\xE2\x98\x83/", ); foreach ($slugs as $slug => $normal) { $this->assertEqual( $normal, PhabricatorSlug::normalize($slug), "Normalization of '{$slug}'"); } } public function testSlugAncestry() { $slugs = array( '/' => array(), 'pokemon/' => array('/'), 'pokemon/squirtle/' => array('/', 'pokemon/'), ); foreach ($slugs as $slug => $ancestry) { $this->assertEqual( $ancestry, PhabricatorSlug::getAncestry($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, PhabricatorSlug::getDepth($slug), "Depth of '{$slug}'"); } } }