diff --git a/composer.json b/composer.json index 07525ef..6cf7a86 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ "system/includes/functions.php", "system/admin/admin.php", "system/includes/session.php", - "system/includes/opml.php" + "system/includes/opml.php", + "system/plugins/urlify/URLify.php" ] } } diff --git a/composer.lock b/composer.lock index 0cb0e2d..5cdfd99 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "8fa115690163d74447e69c8ab719948b", + "hash": "40c7e6fec1121162c6d2243840450628", "content-hash": "e98ae62bcc711211dfb7c7c70e7fdfc7", "packages": [ { diff --git a/system/admin/admin.php b/system/admin/admin.php index d68f9d6..2dee18e 100644 --- a/system/admin/admin.php +++ b/system/admin/admin.php @@ -78,13 +78,7 @@ function old_password_verify($pass, $user_enc, $user_pass) // Clean URLs function remove_accent($str) { - $a = array('À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'ß', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Ā', 'ā', 'Ă', 'ă', 'Ą', 'ą', 'Ć', 'ć', 'Ĉ', 'ĉ', 'Ċ', 'ċ', 'Č', 'č', 'Ď', 'ď', 'Đ', 'đ', 'Ē', 'ē', 'Ĕ', 'ĕ', 'Ė', 'ė', 'Ę', 'ę', 'Ě', 'ě', 'Ĝ', 'ĝ', 'Ğ', 'ğ', 'Ġ', 'ġ', 'Ģ', 'ģ', 'Ĥ', 'ĥ', 'Ħ', 'ħ', 'Ĩ', 'ĩ', 'Ī', 'ī', 'Ĭ', 'ĭ', 'Į', 'į', 'İ', 'ı', 'IJ', 'ij', 'Ĵ', 'ĵ', 'Ķ', 'ķ', 'Ĺ', 'ĺ', 'Ļ', 'ļ', 'Ľ', 'ľ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'Ń', 'ń', 'Ņ', 'ņ', 'Ň', 'ň', 'ʼn', 'Ō', 'ō', 'Ŏ', 'ŏ', 'Ő', 'ő', 'Œ', 'œ', 'Ŕ', 'ŕ', 'Ŗ', 'ŗ', 'Ř', 'ř', 'Ś', 'ś', 'Ŝ', 'ŝ', 'Ş', 'ş', 'Š', 'š', 'Ţ', 'ţ', 'Ť', 'ť', 'Ŧ', 'ŧ', 'Ũ', 'ũ', 'Ū', 'ū', 'Ŭ', 'ŭ', 'Ů', 'ů', 'Ű', 'ű', 'Ų', 'ų', 'Ŵ', 'ŵ', 'Ŷ', 'ŷ', 'Ÿ', 'Ź', 'ź', 'Ż', 'ż', 'Ž', 'ž', 'ſ', 'ƒ', 'Ơ', 'ơ', 'Ư', 'ư', 'Ǎ', 'ǎ', 'Ǐ', 'ǐ', 'Ǒ', 'ǒ', 'Ǔ', 'ǔ', 'Ǖ', 'ǖ', 'Ǘ', 'ǘ', 'Ǚ', 'ǚ', 'Ǜ', 'ǜ', 'Ǻ', 'ǻ', 'Ǽ', 'ǽ', 'Ǿ', 'ǿ'); - $b = array('A', 'A', 'A', 'A', 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 's', 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y', 'A', 'a', 'A', 'a', 'A', 'a', 'C', 'c', 'C', 'c', 'C', 'c', 'C', 'c', 'D', 'd', 'D', 'd', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'G', 'g', 'G', 'g', 'G', 'g', 'G', 'g', 'H', 'h', 'H', 'h', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'IJ', 'ij', 'J', 'j', 'K', 'k', 'L', 'l', 'L', 'l', 'L', 'l', 'L', 'l', 'l', 'l', 'N', 'n', 'N', 'n', 'N', 'n', 'n', 'O', 'o', 'O', 'o', 'O', 'o', 'OE', 'oe', 'R', 'r', 'R', 'r', 'R', 'r', 'S', 's', 'S', 's', 'S', 's', 'S', 's', 'T', 't', 'T', 't', 'T', 't', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'W', 'w', 'Y', 'y', 'Y', 'Z', 'z', 'Z', 'z', 'Z', 'z', 's', 'f', 'O', 'o', 'U', 'u', 'A', 'a', 'I', 'i', 'O', 'o', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'A', 'a', 'AE', 'ae', 'O', 'o'); - $cyr = array('ж', 'ч', 'щ', 'ш', 'ю', 'а', 'б', 'в', 'г', 'д', 'e', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ъ', 'ь', 'я', 'Ж', 'Ч', 'Щ', 'Ш', 'Ю', 'А', 'Б', 'В', 'Г', 'Д', 'Е', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ъ', 'Ь', 'Я'); - $lat = array('zh', 'ch', 'sht', 'sh', 'yu', 'a', 'b', 'v', 'g', 'd', 'e', 'z', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'u', 'f', 'h', 'c', 'y', 'x', 'q', 'Zh', 'Ch', 'Sht', 'Sh', 'Yu', 'A', 'B', 'V', 'G', 'D', 'E', 'Z', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'R', 'S', 'T', 'U', 'F', 'H', 'c', 'Y', 'X', 'Q'); - $a = array_merge($a, $cyr); - $b = array_merge($b, $lat); - return str_replace($a, $b, $str); + return URLify::downcode($str); } // Edit blog posts diff --git a/system/htmly.php b/system/htmly.php index a94710d..5ad493c 100644 --- a/system/htmly.php +++ b/system/htmly.php @@ -1564,6 +1564,9 @@ get('/tag/:tag', function ($tag) { $posts = get_tag($tag, $page, $perpage, false); $total = get_tagcount($tag, 'basename'); + + $ttag = new stdClass; + $ttag->title = tag_i18n($tag); if (empty($posts) || $page < 1) { // a non-existing page @@ -1575,6 +1578,7 @@ get('/tag/:tag', function ($tag) { 'canonical' => site_url() . 'tag/' . strtolower($tag), 'page' => $page, 'posts' => $posts, + 'tag' => $ttag, 'bodyclass' => 'intag', 'breadcrumb' => '' . config('breadcrumb.home') . ' » Posts tagged: ' . tag_i18n($tag), 'pagination' => has_pagination($total, $perpage, $page), @@ -1612,6 +1616,9 @@ get('/archive/:req', function ($req) { } else { $timestamp = $req; } + + $tarchive = new stdClass; + $tarchive->title = $timestamp; if (!$date) { // a non-existing page @@ -1624,6 +1631,7 @@ get('/archive/:req', function ($req) { 'canonical' => site_url() . 'archive/' . $req, 'page' => $page, 'posts' => $posts, + 'archive' => $tarchive, 'bodyclass' => 'inarchive', 'breadcrumb' => '' . config('breadcrumb.home') . ' » Archive for: ' . $timestamp, 'pagination' => has_pagination($total, $perpage, $page), @@ -1643,16 +1651,20 @@ get('/search/:keyword', function ($keyword) { $perpage = config('search.perpage'); $posts = get_keyword($keyword, $page, $perpage); + + $tsearch = new stdClass; + $tsearch->title = $keyword; if (!$posts || $page < 1) { // a non-existing page or no search result render('404-search', array( 'title' => 'Search results not found! - ' . blog_title(), 'description' => 'Search results not found!', + 'search' => $tsearch, 'breadcrumb' => '' . config('breadcrumb.home') . ' » No search results', 'canonical' => site_url(), 'bodyclass' => 'error-404-search', - 'is_search' => true, + 'is_404search' => true, )); die; } @@ -1665,6 +1677,7 @@ get('/search/:keyword', function ($keyword) { 'canonical' => site_url() . 'search/' . strtolower($keyword), 'page' => $page, 'posts' => $posts, + 'search' => $tsearch, 'bodyclass' => 'insearch', 'breadcrumb' => '' . config('breadcrumb.home') . ' » Search results for: ' . tag_i18n($keyword), 'pagination' => has_pagination($total, $perpage, $page), diff --git a/system/includes/functions.php b/system/includes/functions.php index 24565e3..7b5879a 100644 --- a/system/includes/functions.php +++ b/system/includes/functions.php @@ -1346,9 +1346,12 @@ function has_pagination($total, $perpage, $page = 1) if (!$total) { $total = count(get_post_unsorted()); } + $totalPage = ceil($total / $perpage); + $number = 'Page '. $page . ' of ' . $totalPage; return array( 'prev' => $page > 1, - 'next' => $total > $page * $perpage + 'next' => $total > $page * $perpage, + 'number' => $number ); } diff --git a/system/plugins/urlify/.gitignore b/system/plugins/urlify/.gitignore new file mode 100644 index 0000000..9df9b99 --- /dev/null +++ b/system/plugins/urlify/.gitignore @@ -0,0 +1,3 @@ +vendor +composer.phar +composer.lock diff --git a/system/plugins/urlify/.travis.yml b/system/plugins/urlify/.travis.yml new file mode 100644 index 0000000..e06af44 --- /dev/null +++ b/system/plugins/urlify/.travis.yml @@ -0,0 +1,15 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +before_script: + - composer install --no-interaction --prefer-source + +script: + - phpunit --coverage-text --verbose diff --git a/system/plugins/urlify/INSTALL b/system/plugins/urlify/INSTALL new file mode 100644 index 0000000..e92e55e --- /dev/null +++ b/system/plugins/urlify/INSTALL @@ -0,0 +1,10 @@ +To install URLify, you can add it as a dependency ar by downloading the composer.phar executable. + +$ curl -s http://getcomposer.org/installer | php + +and run install + +$ php composer.phar install + +For more details, see http://getcomposer.org. + diff --git a/system/plugins/urlify/LICENSE b/system/plugins/urlify/LICENSE new file mode 100644 index 0000000..5f4f225 --- /dev/null +++ b/system/plugins/urlify/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) Django Software Foundation and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of Django nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/system/plugins/urlify/README.md b/system/plugins/urlify/README.md new file mode 100644 index 0000000..334734d --- /dev/null +++ b/system/plugins/urlify/README.md @@ -0,0 +1,94 @@ +# URLify for PHP + +A PHP port of [URLify.js](https://github.com/django/django/blob/master/django/contrib/admin/static/admin/js/urlify.js) +from the Django project. Handles symbols from Latin languages as well as Arabic, Azerbaijani, Czech, German, Greek, +Latvian, Lithuanian, Polish, Romanian, Bulgarian, Russian, Serbian, Turkish, Ukrainian and Vietnamese. Symbols it cannot +transliterate it will simply omit. + +## Usage: + +To generate slugs for URLs: + +```php + +``` + +To generate slugs for file names: + +```php + +``` + + +To simply transliterate characters: + +```php + +``` + +To extend the character list: + +```php + '?', '®' => '(r)', '¼' => '1/4', + '½' => '1/2', '¾' => '3/4', '¶' => 'P' +)); + +echo URLify::downcode ('¿ ® ¼ ¼ ¾ ¶'); +// "? (r) 1/2 1/2 3/4 P" + +?> +``` + +To extend the list of words to remove: + +```php + +``` + +To prioritize a certain language map: + +```php + +``` +Please note that the "ü" is transliterated to "ue" in the first case, whereas it results in a simple "u" in the latter. diff --git a/system/plugins/urlify/URLify.php b/system/plugins/urlify/URLify.php new file mode 100644 index 0000000..823f843 --- /dev/null +++ b/system/plugins/urlify/URLify.php @@ -0,0 +1,274 @@ + array ( /* German */ + 'Ä' => 'Ae', 'Ö' => 'Oe', 'Ü' => 'Ue', 'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'ß' => 'ss', + 'ẞ' => 'SS' + ), + 'latin' => array ( + 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A','Ă' => 'A', 'Æ' => 'AE', 'Ç' => + 'C', 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', + 'Ï' => 'I', 'Ð' => 'D', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => + 'O', 'Ő' => 'O', 'Ø' => 'O','Ș' => 'S','Ț' => 'T', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ű' => 'U', + 'Ý' => 'Y', 'Þ' => 'TH', 'ß' => 'ss', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => + 'a', 'å' => 'a', 'ă' => 'a', 'æ' => 'ae', 'ç' => 'c', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', + 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'd', 'ñ' => 'n', 'ò' => 'o', 'ó' => + 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ő' => 'o', 'ø' => 'o', 'ș' => 's', 'ț' => 't', 'ù' => 'u', 'ú' => 'u', + 'û' => 'u', 'ü' => 'u', 'ű' => 'u', 'ý' => 'y', 'þ' => 'th', 'ÿ' => 'y' + ), + 'latin_symbols' => array ( + '©' => '(c)' + ), + 'el' => array ( /* Greek */ + 'α' => 'a', 'β' => 'b', 'γ' => 'g', 'δ' => 'd', 'ε' => 'e', 'ζ' => 'z', 'η' => 'h', 'θ' => '8', + 'ι' => 'i', 'κ' => 'k', 'λ' => 'l', 'μ' => 'm', 'ν' => 'n', 'ξ' => '3', 'ο' => 'o', 'π' => 'p', + 'ρ' => 'r', 'σ' => 's', 'τ' => 't', 'υ' => 'y', 'φ' => 'f', 'χ' => 'x', 'ψ' => 'ps', 'ω' => 'w', + 'ά' => 'a', 'έ' => 'e', 'ί' => 'i', 'ό' => 'o', 'ύ' => 'y', 'ή' => 'h', 'ώ' => 'w', 'ς' => 's', + 'ϊ' => 'i', 'ΰ' => 'y', 'ϋ' => 'y', 'ΐ' => 'i', + 'Α' => 'A', 'Β' => 'B', 'Γ' => 'G', 'Δ' => 'D', 'Ε' => 'E', 'Ζ' => 'Z', 'Η' => 'H', 'Θ' => '8', + 'Ι' => 'I', 'Κ' => 'K', 'Λ' => 'L', 'Μ' => 'M', 'Ν' => 'N', 'Ξ' => '3', 'Ο' => 'O', 'Π' => 'P', + 'Ρ' => 'R', 'Σ' => 'S', 'Τ' => 'T', 'Υ' => 'Y', 'Φ' => 'F', 'Χ' => 'X', 'Ψ' => 'PS', 'Ω' => 'W', + 'Ά' => 'A', 'Έ' => 'E', 'Ί' => 'I', 'Ό' => 'O', 'Ύ' => 'Y', 'Ή' => 'H', 'Ώ' => 'W', 'Ϊ' => 'I', + 'Ϋ' => 'Y' + ), + 'tr' => array ( /* Turkish */ + 'ş' => 's', 'Ş' => 'S', 'ı' => 'i', 'İ' => 'I', 'ç' => 'c', 'Ç' => 'C', 'ü' => 'u', 'Ü' => 'U', + 'ö' => 'o', 'Ö' => 'O', 'ğ' => 'g', 'Ğ' => 'G' + ), + 'bg' => array( /* Bulgarian */ + "Щ" => 'Sht', "Ш" => 'Sh', "Ч" => 'Ch', "Ц" => 'C', "Ю" => 'Yu', "Я" => 'Ya', + "Ж" => 'J', "А" => 'A', "Б" => 'B', "В" => 'V', "Г" => 'G', "Д" => 'D', + "Е" => 'E', "З" => 'Z', "И" => 'I', "Й" => 'Y', "К" => 'K', "Л" => 'L', + "М" => 'M', "Н" => 'N', "О" => 'O', "П" => 'P', "Р" => 'R', "С" => 'S', + "Т" => 'T', "У" => 'U', "Ф" => 'F', "Х" => 'H', "Ь" => '', "Ъ" => 'A', + "щ" => 'sht', "ш" => 'sh', "ч" => 'ch', "ц" => 'c', "ю" => 'yu', "я" => 'ya', + "ж" => 'j', "а" => 'a', "б" => 'b', "в" => 'v', "г" => 'g', "д" => 'd', + "е" => 'e', "з" => 'z', "и" => 'i', "й" => 'y', "к" => 'k', "л" => 'l', + "м" => 'm', "н" => 'n', "о" => 'o', "п" => 'p', "р" => 'r', "с" => 's', + "т" => 't', "у" => 'u', "ф" => 'f', "х" => 'h', "ь" => '', "ъ" => 'a' + ), + 'ru' => array ( /* Russian */ + 'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e', 'ё' => 'yo', 'ж' => 'zh', + 'з' => 'z', 'и' => 'i', 'й' => 'j', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o', + 'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c', + 'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sh', 'ъ' => '', 'ы' => 'y', 'ь' => '', 'э' => 'e', 'ю' => 'yu', + 'я' => 'ya', + 'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', 'Ё' => 'Yo', 'Ж' => 'Zh', + 'З' => 'Z', 'И' => 'I', 'Й' => 'J', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N', 'О' => 'O', + 'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C', + 'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sh', 'Ъ' => '', 'Ы' => 'Y', 'Ь' => '', 'Э' => 'E', 'Ю' => 'Yu', + 'Я' => 'Ya', + '№' => '' + ), + 'uk' => array ( /* Ukrainian */ + 'Є' => 'Ye', 'І' => 'I', 'Ї' => 'Yi', 'Ґ' => 'G', 'є' => 'ye', 'і' => 'i', 'ї' => 'yi', 'ґ' => 'g' + ), + 'cs' => array ( /* Czech */ + 'č' => 'c', 'ď' => 'd', 'ě' => 'e', 'ň' => 'n', 'ř' => 'r', 'š' => 's', 'ť' => 't', 'ů' => 'u', + 'ž' => 'z', 'Č' => 'C', 'Ď' => 'D', 'Ě' => 'E', 'Ň' => 'N', 'Ř' => 'R', 'Š' => 'S', 'Ť' => 'T', + 'Ů' => 'U', 'Ž' => 'Z' + ), + 'pl' => array ( /* Polish */ + 'ą' => 'a', 'ć' => 'c', 'ę' => 'e', 'ł' => 'l', 'ń' => 'n', 'ó' => 'o', 'ś' => 's', 'ź' => 'z', + 'ż' => 'z', 'Ą' => 'A', 'Ć' => 'C', 'Ę' => 'e', 'Ł' => 'L', 'Ń' => 'N', 'Ó' => 'O', 'Ś' => 'S', + 'Ź' => 'Z', 'Ż' => 'Z' + ), + 'ro' => array ( /* Romanian */ + 'ă' => 'a', 'â' => 'a', 'î' => 'i', 'ș' => 's', 'ț' => 't', 'Ţ' => 'T', 'ţ' => 't' + ), + 'lv' => array ( /* Latvian */ + 'ā' => 'a', 'č' => 'c', 'ē' => 'e', 'ģ' => 'g', 'ī' => 'i', 'ķ' => 'k', 'ļ' => 'l', 'ņ' => 'n', + 'š' => 's', 'ū' => 'u', 'ž' => 'z', 'Ā' => 'A', 'Č' => 'C', 'Ē' => 'E', 'Ģ' => 'G', 'Ī' => 'i', + 'Ķ' => 'k', 'Ļ' => 'L', 'Ņ' => 'N', 'Š' => 'S', 'Ū' => 'u', 'Ž' => 'Z' + ), + 'lt' => array ( /* Lithuanian */ + 'ą' => 'a', 'č' => 'c', 'ę' => 'e', 'ė' => 'e', 'į' => 'i', 'š' => 's', 'ų' => 'u', 'ū' => 'u', 'ž' => 'z', + 'Ą' => 'A', 'Č' => 'C', 'Ę' => 'E', 'Ė' => 'E', 'Į' => 'I', 'Š' => 'S', 'Ų' => 'U', 'Ū' => 'U', 'Ž' => 'Z' + ), + 'vn' => array ( /* Vietnamese */ + 'Á' => 'A', 'À' => 'A', 'Ả' => 'A', 'Ã' => 'A', 'Ạ' => 'A', 'Ă' => 'A', 'Ắ' => 'A', 'Ằ' => 'A', 'Ẳ' => 'A', 'Ẵ' => 'A', 'Ặ' => 'A', 'Â' => 'A', 'Ấ' => 'A', 'Ầ' => 'A', 'Ẩ' => 'A', 'Ẫ' => 'A', 'Ậ' => 'A', + 'á' => 'a', 'à' => 'a', 'ả' => 'a', 'ã' => 'a', 'ạ' => 'a', 'ă' => 'a', 'ắ' => 'a', 'ằ' => 'a', 'ẳ' => 'a', 'ẵ' => 'a', 'ặ' => 'a', 'â' => 'a', 'ấ' => 'a', 'ầ' => 'a', 'ẩ' => 'a', 'ẫ' => 'a', 'ậ' => 'a', + 'É' => 'E', 'È' => 'E', 'Ẻ' => 'E', 'Ẽ' => 'E', 'Ẹ' => 'E', 'Ê' => 'E', 'Ế' => 'E', 'Ề' => 'E', 'Ể' => 'E', 'Ễ' => 'E', 'Ệ' => 'E', + 'é' => 'e', 'è' => 'e', 'ẻ' => 'e', 'ẽ' => 'e', 'ẹ' => 'e', 'ê' => 'e', 'ế' => 'e', 'ề' => 'e', 'ể' => 'e', 'ễ' => 'e', 'ệ' => 'e', + 'Í' => 'I', 'Ì' => 'I', 'Ỉ' => 'I', 'Ĩ' => 'I', 'Ị' => 'I', 'í' => 'i', 'ì' => 'i', 'ỉ' => 'i', 'ĩ' => 'i', 'ị' => 'i', + 'Ó' => 'O', 'Ò' => 'O', 'Ỏ' => 'O', 'Õ' => 'O', 'Ọ' => 'O', 'Ô' => 'O', 'Ố' => 'O', 'Ồ' => 'O', 'Ổ' => 'O', 'Ỗ' => 'O', 'Ộ' => 'O', 'Ơ' => 'O', 'Ớ' => 'O', 'Ờ' => 'O', 'Ở' => 'O', 'Ỡ' => 'O', 'Ợ' => 'O', + 'ó' => 'o', 'ò' => 'o', 'ỏ' => 'o', 'õ' => 'o', 'ọ' => 'o', 'ô' => 'o', 'ố' => 'o', 'ồ' => 'o', 'ổ' => 'o', 'ỗ' => 'o', 'ộ' => 'o', 'ơ' => 'o', 'ớ' => 'o', 'ờ' => 'o', 'ở' => 'o', 'ỡ' => 'o', 'ợ' => 'o', + 'Ú' => 'U', 'Ù' => 'U', 'Ủ' => 'U', 'Ũ' => 'U', 'Ụ' => 'U', 'Ư' => 'U', 'Ứ' => 'U', 'Ừ' => 'U', 'Ử' => 'U', 'Ữ' => 'U', 'Ự' => 'U', + 'ú' => 'u', 'ù' => 'u', 'ủ' => 'u', 'ũ' => 'u', 'ụ' => 'u', 'ư' => 'u', 'ứ' => 'u', 'ừ' => 'u', 'ử' => 'u', 'ữ' => 'u', 'ự' => 'u', + 'Ý' => 'Y', 'Ỳ' => 'Y', 'Ỷ' => 'Y', 'Ỹ' => 'Y', 'Ỵ' => 'Y', 'ý' => 'y', 'ỳ' => 'y', 'ỷ' => 'y', 'ỹ' => 'y', 'ỵ' => 'y', + 'Đ' => 'D', 'đ' => 'd' + ), + 'ar' => array ( /* Arabic */ + 'أ' => 'a', 'ب' => 'b', 'ت' => 't', 'ث' => 'th', 'ج' => 'g', 'ح' => 'h', 'خ' => 'kh', 'د' => 'd', + 'ذ' => 'th', 'ر' => 'r', 'ز' => 'z', 'س' => 's', 'ش' => 'sh', 'ص' => 's', 'ض' => 'd', 'ط' => 't', + 'ظ' => 'th', 'ع' => 'aa', 'غ' => 'gh', 'ف' => 'f', 'ق' => 'k', 'ك' => 'k', 'ل' => 'l', 'م' => 'm', + 'ن' => 'n', 'ه' => 'h', 'و' => 'o', 'ي' => 'y' + ), + 'sr' => array ( /* Serbian */ + 'ђ' => 'dj', 'ј' => 'j', 'љ' => 'lj', 'њ' => 'nj', 'ћ' => 'c', 'џ' => 'dz', 'đ' => 'dj', + 'Ђ' => 'Dj', 'Ј' => 'j', 'Љ' => 'Lj', 'Њ' => 'Nj', 'Ћ' => 'C', 'Џ' => 'Dz', 'Đ' => 'Dj' + ), + 'az' => array ( /* Azerbaijani */ + 'ç' => 'c', 'ə' => 'e', 'ğ' => 'g', 'ı' => 'i', 'ö' => 'o', 'ş' => 's', 'ü' => 'u', + 'Ç' => 'C', 'Ə' => 'E', 'Ğ' => 'G', 'İ' => 'I', 'Ö' => 'O', 'Ş' => 'S', 'Ü' => 'U' + ), + 'slo' => array ( /* Slovakian */ + 'DZ' => 'DZ', 'dz' => 'dz', 'DŽ' => 'DZ', 'dž' => 'dz', 'CH' => 'CH', 'Ĺ' => 'L', 'ĺ' => 'l', 'Ľ' => 'L', + 'ľ' => 'l', 'Ŕ' => 'R', 'ŕ' => 'r' + ), + 'add' => array ( /* Custom */ + 'Ĉ' => 'C', 'ĉ' => 'c', 'Ċ' => 'C', 'ċ' => 'c', 'Ď' => 'D', 'ď' => 'd', 'Ĕ' => 'E', 'ĕ' => 'e', 'Ĝ' => 'G', 'ĝ' => 'g', + 'Ġ' => 'G', 'ġ' => 'g', 'Ĥ' => 'H', 'ĥ' => 'h', 'Ħ' => 'H', 'ħ' => 'h', 'Ĭ' => 'I', 'ĭ' => 'i', 'IJ' => 'J', 'ij' => 'j', + 'Ĵ' => 'J', 'ĵ' => 'j', 'Ŀ' => 'L', 'ŀ' => 'l', 'ʼn' => 'n', 'Ō' => 'O', 'ō' => 'o', 'Ŏ' => 'O', 'ŏ' => 'o', 'Œ' => 'OE', + 'œ' => 'oe', 'Ŗ' => 'R', 'ŗ' => 'r', 'Ŝ' => 'S', 'ŝ' => 's', 'Ŧ' => 'T', 'ŧ' => 't', 'Ŭ' => 'U', 'ŭ' => 'u', 'Ŵ' => 'W', + 'ŵ' => 'w', 'Ŷ' => 'Y', 'ŷ' => 'y', 'Ÿ' => 'Y', 'ſ' => 'S', 'Ƒ' => 'F', 'ƒ' => 'f', 'Ǎ' => 'A', 'ǎ' => 'a', 'Ǐ' => 'I', + 'ǐ' => 'i', 'Ǒ' => 'O', 'ǒ' => 'o', 'Ǔ' => 'U', 'ǔ' => 'u', 'Ǖ' => 'U', 'ǖ' => 'u', 'Ǘ' => 'U', 'ǘ' => 'u', 'Ǚ' => 'U', + 'ǚ' => 'u', 'Ǜ' => 'U', 'ǜ' => 'u', 'Ǻ' => 'A', 'ǻ' => 'a', 'Ǽ' => 'AE', 'ǽ' => 'ae', 'Ǿ' => 'O', 'ǿ' => 'o' + ) + ); + + /** + * List of words to remove from URLs. + */ + public static $remove_list = array ( + 'a', 'an', 'as', 'at', 'before', 'but', 'by', 'for', 'from', + 'is', 'in', 'into', 'like', 'of', 'off', 'on', 'onto', 'per', + 'since', 'than', 'the', 'this', 'that', 'to', 'up', 'via', + 'with' + ); + + /** + * The character map. + */ + private static $map = array (); + + /** + * The character list as a string. + */ + private static $chars = ''; + + /** + * The character list as a regular expression. + */ + private static $regex = ''; + + /** + * The current language + */ + private static $language = ''; + + /** + * Initializes the character map. + */ + private static function init ($language = "") { + if (count (self::$map) > 0 && (($language == "") || ($language == self::$language))) { + return; + } + + /* Is a specific map associated with $language ? */ + if (isset(self::$maps[$language]) && is_array(self::$maps[$language])) { + /* Move this map to end. This means it will have priority over others */ + $m = self::$maps[$language]; + unset(self::$maps[$language]); + self::$maps[$language] = $m; + } + /* Reset static vars */ + self::$language = $language; + self::$map = array(); + self::$chars = ''; + + foreach (self::$maps as $map) { + foreach ($map as $orig => $conv) { + self::$map[$orig] = $conv; + self::$chars .= $orig; + } + } + + self::$regex = '/[' . self::$chars . ']/u'; + } + + /** + * Add new characters to the list. `$map` should be a hash. + */ + public static function add_chars ($map) { + if (! is_array ($map)) { + throw new LogicException ('$map must be an associative array.'); + } + self::$maps[] = $map; + self::$map = array (); + self::$chars = ''; + } + + /** + * Append words to the remove list. Accepts either single words + * or an array of words. + */ + public static function remove_words ($words) { + $words = is_array ($words) ? $words : array ($words); + self::$remove_list = array_merge (self::$remove_list, $words); + } + + /** + * Transliterates characters to their ASCII equivalents. + * $language specifies a priority for a specific language. + * The latter is useful if languages have different rules for the same character. + */ + public static function downcode ($text, $language = "") { + self::init ($language); + + if (preg_match_all (self::$regex, $text, $matches)) { + for ($i = 0; $i < count ($matches[0]); $i++) { + $char = $matches[0][$i]; + if (isset (self::$map[$char])) { + $text = str_replace ($char, self::$map[$char], $text); + } + } + } + return $text; + } + + /** + * Filters a string, e.g., "Petty theft" to "petty-theft" + */ + public static function filter ($text, $length = 60, $language = "", $file_name = false, $use_remove_list = true) { + $text = self::downcode ($text,$language); + + if ($use_remove_list) { + // remove all these words from the string before urlifying + $text = preg_replace ('/\b(' . join ('|', self::$remove_list) . ')\b/i', '', $text); + } + + // if downcode doesn't hit, the char will be stripped here + $remove_pattern = ($file_name) ? '/[^_\-.\-a-zA-Z0-9\s]/u' : '/[^\s_\-a-zA-Z0-9]/u'; + $text = preg_replace ($remove_pattern, '', $text); // remove unneeded chars + $text = str_replace ('_', ' ', $text); // treat underscores as spaces + $text = preg_replace ('/^\s+|\s+$/u', '', $text); // trim leading/trailing spaces + $text = preg_replace ('/[-\s]+/u', '-', $text); // convert spaces to hyphens + $text = strtolower ($text); // convert to lowercase + return trim (substr ($text, 0, $length), '-'); // trim to first $length chars + } + + /** + * Alias of `URLify::downcode()`. + */ + public static function transliterate ($text) { + return self::downcode ($text); + } +} diff --git a/system/plugins/urlify/composer.json b/system/plugins/urlify/composer.json new file mode 100644 index 0000000..b226f8e --- /dev/null +++ b/system/plugins/urlify/composer.json @@ -0,0 +1,26 @@ +{ + "name": "jbroadway/urlify", + "type": "library", + "description": "PHP port of URLify.js from the Django project. Transliterates non-ascii characters for use in URLs.", + "keywords": ["urlify","transliterate","translit","transliteration","url","encode","slug","link","iconv"], + "homepage": "https://github.com/jbroadway/urlify", + "license": "BSD", + "authors": [ + { + "name": "Johnny Broadway", + "email": "johnny@johnnybroadway.com", + "homepage": "http://www.johnnybroadway.com/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { "URLify": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/system/plugins/urlify/phpunit.xml b/system/plugins/urlify/phpunit.xml new file mode 100644 index 0000000..eeebfcc --- /dev/null +++ b/system/plugins/urlify/phpunit.xml @@ -0,0 +1,8 @@ + + + tests + + + + + diff --git a/system/plugins/urlify/scripts/downcode.php b/system/plugins/urlify/scripts/downcode.php new file mode 100644 index 0000000..f2a7fda --- /dev/null +++ b/system/plugins/urlify/scripts/downcode.php @@ -0,0 +1,20 @@ + 2) { + die ("Usage (argument): php " . basename(__FILE__) . " \"\"\nUsage (pipe): | php " . basename(__FILE__) . "\n"); +} + +//Process the provided argument +if($argc === 2) { + $s = $argv[1]; +//Or read from stdin if the argument wasn't present +} else { + $piped = true; + $s = file_get_contents("php://stdin"); +} + +echo URLify::downcode ($s) . ($piped ? "\n" : ""); diff --git a/system/plugins/urlify/scripts/filter.php b/system/plugins/urlify/scripts/filter.php new file mode 100644 index 0000000..aec6a0a --- /dev/null +++ b/system/plugins/urlify/scripts/filter.php @@ -0,0 +1,20 @@ + 2) { + die ("Usage (argument): php " . basename(__FILE__) . " \"\"\nUsage (pipe): | php " . basename(__FILE__) . "\n"); +} + +//Process the provided argument +if($argc === 2) { + $s = $argv[1]; +//Or read from stdin if the argument wasn't present +} else { + $piped = true; + $s = file_get_contents("php://stdin"); +} + +echo URLify::filter ($s) . ($piped ? "\n" : ""); diff --git a/system/plugins/urlify/scripts/transliterate.php b/system/plugins/urlify/scripts/transliterate.php new file mode 100644 index 0000000..258581b --- /dev/null +++ b/system/plugins/urlify/scripts/transliterate.php @@ -0,0 +1,20 @@ + 2) { + die ("Usage (argument): php " . basename(__FILE__) . " \"\"\nUsage (pipe): | php " . basename(__FILE__) . "\n"); +} + +//Process the provided argument +if($argc === 2) { + $s = $argv[1]; +//Or read from stdin if the argument wasn't present +} else { + $piped = true; + $s = file_get_contents("php://stdin"); +} + +echo URLify::transliterate($s) . ($piped ? "\n" : ""); diff --git a/system/plugins/urlify/tests/URLifyTest.php b/system/plugins/urlify/tests/URLifyTest.php new file mode 100644 index 0000000..5e09401 --- /dev/null +++ b/system/plugins/urlify/tests/URLifyTest.php @@ -0,0 +1,54 @@ +assertEquals (' J\'etudie le francais ', URLify::downcode (' J\'étudie le français ')); + $this->assertEquals ('Lo siento, no hablo espanol.', URLify::downcode ('Lo siento, no hablo español.')); + $this->assertEquals ('F3PWS', URLify::downcode ('ΦΞΠΏΣ')); + $this->assertEquals ('foo-bar', URLify::filter ('_foo_bar_')); + } + + function test_filter () { + $this->assertEquals ('jetudie-le-francais', URLify::filter (' J\'étudie le français ')); + $this->assertEquals ('lo-siento-no-hablo-espanol', URLify::filter ('Lo siento, no hablo español.')); + $this->assertEquals ('f3pws', URLify::filter ('ΦΞΠΏΣ')); + $this->assertEquals ('', URLify::filter('大般若經')); + $this->assertEquals ('test-.txt', URLify::filter('test-大般若經.txt', 60, "", $file_name = true)); + $this->assertEquals ('ykrhy-ltoytr', URLify::filter('ياكرهي لتويتر')); + $this->assertEquals ('foto.jpg', URLify::filter ('фото.jpg', 60, "", $file_name = true)); + // priorization of language-specific maps + $this->assertEquals ('aouaou', URLify::filter ('ÄÖÜäöü',60,"tr")); + $this->assertEquals ('aeoeueaeoeue', URLify::filter ('ÄÖÜäöü',60,"de")); + + $this->assertEquals ('bobby-mcferrin-dont-worry-be-happy', URLify::filter ("Bobby McFerrin — Don't worry be happy",600,"en")); + // test stripping and conversion of UTF-8 spaces + $this->assertEquals ('test-mahito-mukai', URLify::filter('向井 真人test (Mahito Mukai)')); + } + + function test_add_chars () { + $this->assertEquals ('¿ ® ¼ ¼ ¾ ¶', URLify::downcode ('¿ ® ¼ ¼ ¾ ¶')); + URLify::add_chars (array ( + '¿' => '?', '®' => '(r)', '¼' => '1/4', + '¼' => '1/2', '¾' => '3/4', '¶' => 'P' + )); + $this->assertEquals ('? (r) 1/2 1/2 3/4 P', URLify::downcode ('¿ ® ¼ ¼ ¾ ¶')); + } + + function test_remove_words () { + $this->assertEquals ('foo-bar', URLify::filter ('foo bar')); + URLify::remove_words (array ('foo', 'bar')); + $this->assertEquals ('', URLify::filter ('foo bar')); + } + + function test_many_rounds_with_unknown_language_code () { + for ($i = 0; $i < 1000; $i++) { + URLify::downcode ('Lo siento, no hablo español.',-1); + } + } + + function test_remove_words_disable () { + URLify::remove_words (array ('foo', 'bar')); + $this->assertEquals ('foo-bar', URLify::filter ('foo bar', 60, '', false, false)); + } +} + +?> diff --git a/system/plugins/urlify/tests/bootstrap.php b/system/plugins/urlify/tests/bootstrap.php new file mode 100644 index 0000000..d56d466 --- /dev/null +++ b/system/plugins/urlify/tests/bootstrap.php @@ -0,0 +1,9 @@ +getCurrentInfo(); diff --git a/system/vendor/composer/autoload_files.php b/system/vendor/composer/autoload_files.php index a582309..1e0a472 100644 --- a/system/vendor/composer/autoload_files.php +++ b/system/vendor/composer/autoload_files.php @@ -6,10 +6,11 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname(dirname($vendorDir)); return array( - $vendorDir . '/ircmaxell/password-compat/lib/password.php', - $baseDir . '/system/includes/dispatch.php', - $baseDir . '/system/includes/functions.php', - $baseDir . '/system/admin/admin.php', - $baseDir . '/system/includes/session.php', - $baseDir . '/system/includes/opml.php', + '05669301f06448944ce5eb260126f84e' => $vendorDir . '/ircmaxell/password-compat/lib/password.php', + '7acf2c394ac19fdcadb351c1328f6361' => $baseDir . '/system/includes/dispatch.php', + '72aa34b3aa4866e1c401ecb30ee29a30' => $baseDir . '/system/includes/functions.php', + '22715f0e6ee6f21bb7ada1f56df82c76' => $baseDir . '/system/admin/admin.php', + '372406877c969643fd5d4bdc28f7a64a' => $baseDir . '/system/includes/session.php', + '46286444bb46675598ca39831bb66c52' => $baseDir . '/system/includes/opml.php', + 'e1070fa2f8e817d10783474c01748a52' => $baseDir . '/system/plugins/urlify/URLify.php', ); diff --git a/system/vendor/composer/autoload_real.php b/system/vendor/composer/autoload_real.php index 7593687..389be55 100644 --- a/system/vendor/composer/autoload_real.php +++ b/system/vendor/composer/autoload_real.php @@ -41,15 +41,19 @@ class ComposerAutoloaderInit202d771b98d07410d9e52c5a90cbc9e1 $loader->register(true); $includeFiles = require __DIR__ . '/autoload_files.php'; - foreach ($includeFiles as $file) { - composerRequire202d771b98d07410d9e52c5a90cbc9e1($file); + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequire202d771b98d07410d9e52c5a90cbc9e1($fileIdentifier, $file); } return $loader; } } -function composerRequire202d771b98d07410d9e52c5a90cbc9e1($file) +function composerRequire202d771b98d07410d9e52c5a90cbc9e1($fileIdentifier, $file) { - require $file; + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } }