From 9e56293931457eb70d03a949729ebd2b394a5b03 Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Wed, 12 May 2010 05:28:12 +0000 Subject: [PATCH] ENHANCEMENT: Macrons, umlauts, etc, are now transliterated when inserted into URLS. API CHANGE: Added Transliterator class, which uses iconv() or strtr() to convert characters with diacritical marks to their ASCII equivalents. API CHANGE: Added Extension hook updateURLSegment for SiteeTree. git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/branches/2.4@104671 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- core/model/SiteTree.php | 16 ++++++++-- core/model/Transliterator.php | 56 +++++++++++++++++++++++++++++++++++ javascript/UpdateURL.js | 12 ++------ 3 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 core/model/Transliterator.php diff --git a/core/model/SiteTree.php b/core/model/SiteTree.php index 40fb8b547..518a8fc9d 100755 --- a/core/model/SiteTree.php +++ b/core/model/SiteTree.php @@ -1496,11 +1496,18 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid /** * Generate a URL segment based on the title provided. + * + * If {@link Extension}s wish to alter URL segment generation, they can do so by defining + * updateURLSegment(&$url, $title). $url will be passed by reference and should be modified. + * $title will contain the title that was originally used as the source of this generated URL. + * This lets decorators either start from scratch, or incrementally modify the generated URL. + * * @param string $title Page title. * @return string Generated url segment */ function generateURLSegment($title){ - $t = strtolower($title); + $t = mb_strtolower($title); + $t = Object::create('Transliterator')->toASCII($title); $t = str_replace('&','-and-',$t); $t = str_replace('&','-and-',$t); $t = ereg_replace('[^A-Za-z0-9]+','-',$t); @@ -1508,7 +1515,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid if(!$t || $t == '-' || $t == '-1') { $t = "page-$this->ID"; } - return trim($t, '-'); + $t = trim($t, '-'); + + // Hook for decorators + $this->extend('updateURLSegment', $t, $title); + + return $t; } /** diff --git a/core/model/Transliterator.php b/core/model/Transliterator.php new file mode 100644 index 000000000..2e9e413c0 --- /dev/null +++ b/core/model/Transliterator.php @@ -0,0 +1,56 @@ +toASCII($unicode); + */ +class Transliterator { + /** + * Allow the use of iconv() to perform transliteration. Set to false to disable. + * Even if this variable is true, iconv() won't be used if it's not installed. + */ + static $use_iconv = false; + + function __construct() { + // A constructor is necessary for Object::create() to work + } + + /** + * Convert the given utf8 string to a safe ASCII source + */ + function toASCII($source) { + if(function_exists('iconv') && self::$use_iconv) return $this->useIconv($source); + else return $this->useStrTr($source); + } + + /** + * Transliteration using strtr() and a lookup table + */ + protected function useStrTr($source) { + $table = array( + 'Š'=>'S', 'š'=>'s', 'Đ'=>'Dj', 'đ'=>'dj', 'Ž'=>'Z', 'ž'=>'z', 'Č'=>'C', 'č'=>'c', 'Ć'=>'C', 'ć'=>'c', + 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E', + 'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O', + 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'B', 'ß'=>'Ss', + 'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'ae', 'ç'=>'c', 'è'=>'e', 'é'=>'e', + 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o', + 'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ý'=>'y', 'ý'=>'y', 'þ'=>'b', + 'ÿ'=>'y', 'Ŕ'=>'R', 'ŕ'=>'r', + 'Ā'=>'A', 'ā'=>'a', 'Ē'=>'E', 'ē'=>'e', 'Ī'=>'I', 'ī'=>'i', 'Ō'=>'O', 'ō'=>'o', 'Ū'=>'U', 'ū'=>'u', + 'œ'=>'oe', 'ß'=>'ss', 'ij'=>'ij', + ); + + return strtr($source, $table); + } + + /** + * Transliteration using iconv() + */ + protected function useIconv($source) { + return iconv("utf-8", "us-ascii//IGNORE//TRANSLIT", $source); + } +} \ No newline at end of file diff --git a/javascript/UpdateURL.js b/javascript/UpdateURL.js index 36ddee2a9..33311266a 100755 --- a/javascript/UpdateURL.js +++ b/javascript/UpdateURL.js @@ -8,20 +8,14 @@ Behaviour.register({ if(!$('Form_EditForm_URLSegment')) return; var urlSegmentField = $('Form_EditForm_URLSegment'); - var newSuggestion = urlSegmentField.suggestNewValue( this.value.toLowerCase() ); var isNew = urlSegmentField.value.indexOf("new") == 0; var confirmMessage = ss.i18n.sprintf( - ss.i18n._t('UPDATEURL.CONFIRM', 'Would you like me to change the URL to:\n\n%s/\n\nClick Ok to change the URL, click Cancel to leave it as:\n\n%s'), - newSuggestion, + ss.i18n._t('UPDATEURL.CONFIRM_V2', 'Would you like me to change the URL to match this page name?\n\nClick Ok to change the URL, click Cancel to leave it as:\n\n%s'), urlSegmentField.value ); - if( - newSuggestion == urlSegmentField.value - || isNew - || confirm(confirmMessage) - ) { - urlSegmentField.value = newSuggestion; + if(isNew || confirm(confirmMessage)) { + urlSegmentField.value = ''; } // If you type in Page name, the Navigation Label and Meta Title should automatically update the first time // @todo: Change file name from UpdateURL to something more geneneric since we now do more than update the URL.