示例#1
0
 public function testCollate()
 {
     $file = $this->createLayoutFile('test/fixture.less', 'Fixture_TestModule');
     $expected = array($this->baseFile->getFileIdentifier() => $this->baseFile, $file->getFileIdentifier() => $file);
     $result = $this->model->collate(array($file), $this->originFiles);
     $this->assertSame($expected, $result);
 }
示例#2
0
 /**
  * @dataProvider asortProvider
  */
 public function testAsortIntl($array, $sortFlag, $expected)
 {
     $this->skipIfIntlExtensionIsNotLoaded();
     $collator = new \Collator('en');
     $collator->asort($array, $sortFlag);
     $this->assertSame($expected, $array);
 }
示例#3
0
 protected function sortByValue($collection)
 {
     $collator = new \Collator('fr_FR');
     $items = iterator_to_array($collection);
     $collator->asort($items);
     return new Map($items);
 }
示例#4
0
 public function getFinancialInstitutions()
 {
     /** @var Wirecard_CheckoutSeamless_Helper_Data $helper */
     $helper = Mage::helper('wirecard_checkoutseamless');
     $cl = new WirecardCEE_QMore_BackendClient($helper->getBackendConfigArray());
     $response = $cl->getFinancialInstitutions($this->getMethod()->getPaymentMethodType());
     if (!$response->hasFailed()) {
         $ret = $response->getFinancialInstitutions();
         $c = null;
         if (class_exists('Collator')) {
             $c = new Collator('root');
         }
         uasort($ret, function ($a, $b) use($c) {
             if ($c === null) {
                 return strcmp($a['id'], $b['id']);
             } else {
                 return $c->compare($a['name'], $b['name']);
             }
         });
         return $ret;
     } else {
         $helper->log(__METHOD__ . ':' . print_r($response->getErrors(), true), LOG_WARNING);
         return array();
     }
 }
示例#5
0
 /**
  * @param string $a
  * @param string $b
  *
  * @return int
  */
 public function compare($a, $b)
 {
     if ($this->collator instanceof \Collator) {
         return $this->collator->compare($a, $b);
     } else {
         return $a == $b ? 0 : ($a > $b ? 1 : -1);
     }
 }
示例#6
0
 /**
  * {@inheritdoc}
  */
 public function getCurrencyNames($locale = null)
 {
     if (null === $locale) {
         $locale = \Locale::getDefault();
     }
     $names = parent::getCurrencyNames($locale);
     $collator = new \Collator($locale);
     $collator->asort($names);
     return $names;
 }
示例#7
0
 /**
  * {@inheritdoc}
  */
 public function getScriptNames($locale = null)
 {
     if (null === $locale) {
         $locale = \Locale::getDefault();
     }
     $scripts = parent::getScriptNames($locale);
     $collator = new \Collator($locale);
     $collator->asort($scripts);
     return $scripts;
 }
 /**
  * @dataProvider provideLocales
  */
 public function testGetNames($displayLocale)
 {
     $names = $this->dataProvider->getNames($displayLocale);
     $keys = array_keys($names);
     sort($keys);
     $this->assertEquals(static::$currencies, $keys);
     // Names should be sorted
     $sortedNames = $names;
     $collator = new \Collator($displayLocale);
     $collator->asort($names);
     $this->assertSame($sortedNames, $names);
 }
 public function getNames($displayLocale = null)
 {
     if (null === $displayLocale) {
         $displayLocale = Locale::getDefault();
     }
     $names = $this->reader->readEntry($this->path, $displayLocale, array('Names'));
     if ($names instanceof \Traversable) {
         $names = iterator_to_array($names);
     }
     $collator = new \Collator($displayLocale);
     $collator->asort($names);
     return $names;
 }
 /**
  * Returns the remaining locales sorted by language name
  *
  * @return array
  */
 public function getRemainingViews()
 {
     $remainingViews = parent::getRemainingViews();
     usort($remainingViews, function (ChoiceView $choiceView1, ChoiceView $choiceView2) {
         return \Collator::create(null)->compare($choiceView1->label, $choiceView2->label);
     });
     return $remainingViews;
 }
示例#11
0
 /**
  * Sort an array of strings based on current locale.
  *
  * @param array &$sorted  Array of strings.
  */
 protected function _sortString(&$sorted)
 {
     if (empty($this->_collator)) {
         asort($sorted, SORT_LOCALE_STRING);
     } else {
         $this->_collator->asort($sorted, Collator::SORT_STRING);
     }
 }
示例#12
0
 /**
  * Opposite of testIsPrefix
  *
  * @dataProvider notPrefixDataProvider
  */
 function testNotIsPrefix($lang, $base, $extended)
 {
     $cp = Collator::create($lang);
     $cp->setStrength(Collator::PRIMARY);
     $baseBin = $cp->getSortKey($base);
     // Remove sortkey terminator
     $baseBin = rtrim($baseBin, "");
     $extendedBin = $cp->getSortKey($extended);
     $this->assertStringStartsNotWith($baseBin, $extendedBin, "{$base} is a prefix of {$extended}");
 }
示例#13
0
文件: Locale.php 项目: spf13/symfony
 /**
  * Returns the language names for a locale
  *
  * @param  string $locale     The locale to use for the language names
  * @return array              The language names with their codes as keys
  * @throws RuntimeException   When the resource bundles cannot be loaded
  */
 public static function getDisplayLanguages($locale)
 {
     if (!isset(self::$languages[$locale])) {
         $bundle = new \ResourceBundle($locale, __DIR__ . '/Resources/data/lang');
         if (null === $bundle) {
             throw new \RuntimeException('The language resource bundle could not be loaded');
         }
         $collator = new \Collator($locale);
         $languages = array();
         foreach ($bundle->get('Languages') as $code => $name) {
             // "mul" is the code for multiple languages
             if ('mul' !== $code) {
                 $languages[$code] = $name;
             }
         }
         $collator->asort($languages);
         self::$languages[$locale] = $languages;
     }
     return self::$languages[$locale];
 }
示例#14
0
 /**
  * {@inheritdoc}
  */
 public function getCountryNames($locale = null)
 {
     if (null === $locale) {
         $locale = \Locale::getDefault();
     }
     $countries = parent::getCountryNames($locale);
     // "ZZ" is the code for unknown country
     unset($countries['ZZ']);
     // Global countries (f.i. "America") have numeric codes
     // Countries have alphabetic codes
     foreach ($countries as $code => $name) {
         // is_int() does not work, since some numbers start with '0' and
         // thus are stored as strings.
         // The (string) cast is necessary since ctype_digit() returns false
         // for integers.
         if (ctype_digit((string) $code)) {
             unset($countries[$code]);
         }
     }
     $collator = new \Collator($locale);
     $collator->asort($countries);
     return $countries;
 }
示例#15
0
 public function __construct($name, $title = null, $source = null, $value = "", $form = null)
 {
     if (!is_array($source)) {
         // Get a list of countries from Zend
         $source = Zend_Locale::getTranslationList('territory', $this->locale(), 2);
         // We want them ordered by display name, not country code
         // PHP 5.3 has an extension that sorts UTF-8 strings correctly
         if (class_exists('Collator') && ($collator = Collator::create($this->locale()))) {
             $collator->asort($source);
         } else {
             asort($source);
         }
         // We don't want "unknown country" as an option
         unset($source['ZZ']);
     }
     parent::__construct($name, $title === null ? $name : $title, $source, $value, $form);
 }
 public function setSource($source)
 {
     if ($source) {
         return parent::setSource($source);
     }
     // map empty source to country list
     // Get a list of countries from Zend
     $source = Zend_Locale::getTranslationList('territory', $this->locale(), 2);
     // We want them ordered by display name, not country code
     // PHP 5.3 has an extension that sorts UTF-8 strings correctly
     if (class_exists('Collator') && ($collator = Collator::create($this->locale()))) {
         $collator->asort($source);
     } else {
         // Otherwise just put up with them being weirdly ordered for now
         asort($source);
     }
     // We don't want "unknown country" as an option
     unset($source['ZZ']);
     return parent::setSource($source);
 }
示例#17
0
 /**
  * @param array $array
  * @param bool $keepKeys
  *
  * @return array
  */
 public function sort(&$array, $keepKeys = false)
 {
     $me = $this;
     if ($keepKeys) {
         if (isset($this->collator)) {
             $result = $this->collator->asort($array);
         } else {
             $result = uasort($array, function ($a, $b) use($me) {
                 return $me->compare($a, $b);
             });
         }
     } else {
         if (isset($this->collator)) {
             $result = $this->collator->sort($array);
         } else {
             $result = usort($array, function ($a, $b) use($me) {
                 return $me->compare($a, $b);
             });
         }
     }
     return $result;
 }
 public function __construct($name, $title = null, $source = null, $value = "", $form = null)
 {
     if (!is_array($source)) {
         // Get a list of countries from Zend
         $source = Zend_Locale::getTranslationList('territory', $this->locale(), 2);
         // We want them ordered by display name, not country code
         // PHP 5.3 has an extension that sorts UTF-8 strings correctly
         if (class_exists('Collator') && ($collator = Collator::create($this->locale()))) {
             $collator->asort($source);
         } else {
             asort($source);
         }
         // We don't want "unknown country" as an option
         unset($source['ZZ']);
         // We don't want a number of countries which have ceased to exist
         unset($source['SU']);
         // Soviet Union
         unset($source['BQ']);
         // British Antarctic Territory
         unset($source['CT']);
         // Canton and Enderbury Islands
         unset($source['NQ']);
         // Dronning Maud Land
         unset($source['FX']);
         // France, Metropolitan
         unset($source['FQ']);
         // French Southern and Antarctic Territories
         unset($source['NT']);
         // Iraq-Saudi-Arabia Neutral Zone
         unset($source['PZ']);
         // Panama Canal Zone
         unset($source['CS']);
         // Serbia and Montenegro
     }
     parent::__construct($name, $title === null ? $name : $title, $source, $value, $form);
 }
 public function finishView(FormView $view, FormInterface $form, array $options)
 {
     if ($view->children['country']->vars['choice_translation_domain'] === false) {
         return;
     }
     $collator = new \Collator($this->translator->getLocale());
     $translator = $this->translator;
     $sortFunction = function ($a, $b) use($collator, $translator) {
         return $collator->compare($translator->trans($a->label), $translator->trans($b->label));
     };
     usort($view->children['country']->vars['choices'], $sortFunction);
     if (array_key_exists('state', $view->children) && $view->children['state']->vars['choice_translation_domain']) {
         usort($view->children['state']->vars['choices'], $sortFunction);
     }
     if (array_key_exists('city', $view->children) && $view->children['city']->vars['choice_translation_domain']) {
         usort($view->children['city']->vars['choices'], $sortFunction);
     }
 }
 function generateFirstChars()
 {
     $file = fopen("{$this->dataDir}/allkeys.txt", 'r');
     if (!$file) {
         $this->error("Unable to open allkeys.txt");
         exit(1);
     }
     global $IP;
     $outFile = fopen("{$IP}/serialized/first-letters-root.ser", 'w');
     if (!$outFile) {
         $this->error("Unable to open output file first-letters-root.ser");
         exit(1);
     }
     $goodTertiaryChars = array();
     // For each character with an entry in allkeys.txt, overwrite the implicit
     // entry in $this->weights that came from the UCD.
     // Also gather a list of tertiary weights, for use in selecting the group header
     while (false !== ($line = fgets($file))) {
         // We're only interested in single-character weights, pick them out with a regex
         $line = trim($line);
         if (!preg_match('/^([0-9A-F]+)\\s*;\\s*([^#]*)/', $line, $m)) {
             continue;
         }
         $cp = hexdec($m[1]);
         $allWeights = trim($m[2]);
         $primary = '';
         $tertiary = '';
         if (!isset($this->weights[$cp])) {
             // Non-printable, ignore
             continue;
         }
         foreach (StringUtils::explode('[', $allWeights) as $weightStr) {
             preg_match_all('/[*.]([0-9A-F]+)/', $weightStr, $m);
             if (!empty($m[1])) {
                 if ($m[1][0] !== '0000') {
                     $primary .= '.' . $m[1][0];
                 }
                 if ($m[1][2] !== '0000') {
                     $tertiary .= '.' . $m[1][2];
                 }
             }
         }
         $this->weights[$cp] = $primary;
         if ($tertiary === '.0008' || $tertiary === '.000E') {
             $goodTertiaryChars[$cp] = true;
         }
     }
     fclose($file);
     // Identify groups of characters with the same primary weight
     $this->groups = array();
     asort($this->weights, SORT_STRING);
     $prevWeight = reset($this->weights);
     $group = array();
     foreach ($this->weights as $cp => $weight) {
         if ($weight !== $prevWeight) {
             $this->groups[$prevWeight] = $group;
             $prevWeight = $weight;
             if (isset($this->groups[$weight])) {
                 $group = $this->groups[$weight];
             } else {
                 $group = array();
             }
         }
         $group[] = $cp;
     }
     if ($group) {
         $this->groups[$prevWeight] = $group;
     }
     // If one character has a given primary weight sequence, and a second
     // character has a longer primary weight sequence with an initial
     // portion equal to the first character, then remove the second
     // character. This avoids having characters like U+A732 (double A)
     // polluting the basic latin sort area.
     foreach ($this->groups as $weight => $group) {
         if (preg_match('/(\\.[0-9A-F]*)\\./', $weight, $m)) {
             if (isset($this->groups[$m[1]])) {
                 unset($this->groups[$weight]);
             }
         }
     }
     ksort($this->groups, SORT_STRING);
     // Identify the header character in each group
     $headerChars = array();
     $prevChar = "";
     $tertiaryCollator = new Collator('root');
     $primaryCollator = new Collator('root');
     $primaryCollator->setStrength(Collator::PRIMARY);
     $numOutOfOrder = 0;
     foreach ($this->groups as $weight => $group) {
         $uncomposedChars = array();
         $goodChars = array();
         foreach ($group as $cp) {
             if (isset($goodTertiaryChars[$cp])) {
                 $goodChars[] = $cp;
             }
             if (!isset($this->mappedChars[$cp])) {
                 $uncomposedChars[] = $cp;
             }
         }
         $x = array_intersect($goodChars, $uncomposedChars);
         if (!$x) {
             $x = $uncomposedChars;
             if (!$x) {
                 $x = $group;
             }
         }
         // Use ICU to pick the lowest sorting character in the selection
         $tertiaryCollator->sort($x);
         $cp = $x[0];
         $char = UtfNormal\Utils::codepointToUtf8($cp);
         $headerChars[] = $char;
         if ($primaryCollator->compare($char, $prevChar) <= 0) {
             $numOutOfOrder++;
             /*
             				printf( "Out of order: U+%05X > U+%05X\n",
             					utf8ToCodepoint( $prevChar ),
             					utf8ToCodepoint( $char ) );
             */
         }
         $prevChar = $char;
         if ($this->debugOutFile) {
             fwrite($this->debugOutFile, sprintf("%05X %s %s (%s)\n", $cp, $weight, $char, implode(' ', array_map('UtfNormal\\Utils::codepointToUtf8', $group))));
         }
     }
     print "Out of order: {$numOutOfOrder} / " . count($headerChars) . "\n";
     fwrite($outFile, serialize($headerChars));
 }
示例#21
0
<?php header ('Content-Type: text/html; charset=UTF-8'); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
	<title>Collation in PHP</title>
</head>
<body style="font-size: 18pt;">
<?php # Script 14.3 - collation.php

// Create an array of words:
$words = array('chère', 'côté', 'chaise', 'château', 'chaînette', 'châle', 'Chère', 'côte', 'chemise');

// Sort using the default PHP function:
echo '<h3>Using sort()</h3>';
sort($words);
echo implode('<br />', $words);

// Sort using the Collator:
echo '<h3>Using Collator</h3>';
$c = new Collator('fr_FR');
$c->sort($words);
echo implode('<br />', $words);

?>
</body>
</html>
示例#22
0
 /**
  * @expectedException \LogicException
  * @expectedExceptionMessage Overriding view file 'new.xml' does not match to any of the files
  */
 public function testReplaceThemeFileException()
 {
     $file = $this->_createViewFile('new.xml', 'Fixture_TestModule', 'area/theme/path');
     $this->_model->collate(array($file), $this->_originFiles);
 }
示例#23
0
 /**
  * Locale aware comparison of two strings.
  *
  * Returns:
  *   1 if str1 is greater than str2
  *   0 if str1 is equal to str2
  *  -1 if str1 is less than str2
  *
  * @param string $str1 first string to compare
  * @param string $str2 second string to compare
  * @return int
  */
 public static function compare($str1, $str2) {
     if (self::ensure_collator_available()) {
         return self::$collator->compare($str1, $str2);
     }
     return strcmp($str1, $str2);
 }
示例#24
0
 function __construct($locale)
 {
     if (!extension_loaded('intl')) {
         throw new MWException('An ICU collation was requested, ' . 'but the intl extension is not available.');
     }
     $this->locale = $locale;
     $this->mainCollator = Collator::create($locale);
     if (!$this->mainCollator) {
         throw new MWException("Invalid ICU locale specified for collation: {$locale}");
     }
     $this->primaryCollator = Collator::create($locale);
     $this->primaryCollator->setStrength(Collator::PRIMARY);
 }
示例#25
0
 function getFirstLetterData()
 {
     if ($this->firstLetterData !== null) {
         return $this->firstLetterData;
     }
     $cache = wfGetCache(CACHE_ANYTHING);
     $cacheKey = wfMemcKey('first-letters', $this->locale, $this->digitTransformLanguage->getCode(), self::getICUVersion());
     $cacheEntry = $cache->get($cacheKey);
     if ($cacheEntry && isset($cacheEntry['version']) && $cacheEntry['version'] == self::FIRST_LETTER_VERSION) {
         $this->firstLetterData = $cacheEntry;
         return $this->firstLetterData;
     }
     // Generate data from serialized data file
     if (isset(self::$tailoringFirstLetters[$this->locale])) {
         $letters = wfGetPrecompiledData("first-letters-root.ser");
         // Append additional characters
         $letters = array_merge($letters, self::$tailoringFirstLetters[$this->locale]);
         // Remove unnecessary ones, if any
         if (isset(self::$tailoringFirstLetters['-' . $this->locale])) {
             $letters = array_diff($letters, self::$tailoringFirstLetters['-' . $this->locale]);
         }
         // Apply digit transforms
         $digits = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
         $letters = array_diff($letters, $digits);
         foreach ($digits as $digit) {
             $letters[] = $this->digitTransformLanguage->formatNum($digit, true);
         }
     } else {
         $letters = wfGetPrecompiledData("first-letters-{$this->locale}.ser");
         if ($letters === false) {
             throw new MWException("MediaWiki does not support ICU locale " . "\"{$this->locale}\"");
         }
     }
     /* Sort the letters.
      *
      * It's impossible to have the precompiled data file properly sorted,
      * because the sort order changes depending on ICU version. If the
      * array is not properly sorted, the binary search will return random
      * results.
      *
      * We also take this opportunity to remove primary collisions.
      */
     $letterMap = array();
     foreach ($letters as $letter) {
         $key = $this->getPrimarySortKey($letter);
         if (isset($letterMap[$key])) {
             // Primary collision
             // Keep whichever one sorts first in the main collator
             if ($this->mainCollator->compare($letter, $letterMap[$key]) < 0) {
                 $letterMap[$key] = $letter;
             }
         } else {
             $letterMap[$key] = $letter;
         }
     }
     ksort($letterMap, SORT_STRING);
     /* Remove duplicate prefixes. Basically if something has a sortkey
      * which is a prefix of some other sortkey, then it is an
      * expansion and probably should not be considered a section
      * header.
      *
      * For example 'þ' is sometimes sorted as if it is the letters
      * 'th'. Other times it is its own primary element. Another
      * example is '₨'. Sometimes its a currency symbol. Sometimes it
      * is an 'R' followed by an 's'.
      *
      * Additionally an expanded element should always sort directly
      * after its first element due to they way sortkeys work.
      *
      * UCA sortkey elements are of variable length but no collation
      * element should be a prefix of some other element, so I think
      * this is safe. See:
      * - https://ssl.icu-project.org/repos/icu/icuhtml/trunk/design/collation/ICU_collation_design.htm
      * - http://site.icu-project.org/design/collation/uca-weight-allocation
      *
      * Additionally, there is something called primary compression to
      * worry about. Basically, if you have two primary elements that
      * are more than one byte and both start with the same byte then
      * the first byte is dropped on the second primary. Additionally
      * either \x03 or \xFF may be added to mean that the next primary
      * does not start with the first byte of the first primary.
      *
      * This shouldn't matter much, as the first primary is not
      * changed, and that is what we are comparing against.
      *
      * tl;dr: This makes some assumptions about how icu implements
      * collations. It seems incredibly unlikely these assumptions
      * will change, but nonetheless they are assumptions.
      */
     $prev = false;
     $duplicatePrefixes = array();
     foreach ($letterMap as $key => $value) {
         // Remove terminator byte. Otherwise the prefix
         // comparison will get hung up on that.
         $trimmedKey = rtrim($key, "");
         if ($prev === false || $prev === '') {
             $prev = $trimmedKey;
             // We don't yet have a collation element
             // to compare against, so continue.
             continue;
         }
         // Due to the fact the array is sorted, we only have
         // to compare with the element directly previous
         // to the current element (skipping expansions).
         // An element "X" will always sort directly
         // before "XZ" (Unless we have "XY", but we
         // do not update $prev in that case).
         if (substr($trimmedKey, 0, strlen($prev)) === $prev) {
             $duplicatePrefixes[] = $key;
             // If this is an expansion, we don't want to
             // compare the next element to this element,
             // but to what is currently $prev
             continue;
         }
         $prev = $trimmedKey;
     }
     foreach ($duplicatePrefixes as $badKey) {
         wfDebug("Removing '{$letterMap[$badKey]}' from first letters.\n");
         unset($letterMap[$badKey]);
         // This code assumes that unsetting does not change sort order.
     }
     $data = array('chars' => array_values($letterMap), 'keys' => array_keys($letterMap), 'version' => self::FIRST_LETTER_VERSION);
     // Reduce memory usage before caching
     unset($letterMap);
     // Save to cache
     $this->firstLetterData = $data;
     $cache->set($cacheKey, $data, $cache::TTL_WEEK);
     return $data;
 }
示例#26
0
function get_data($index, $dataDir, $locale = 'en', $constraint = null)
{
    $data = array();
    $bundle = load_resource_bundle($locale, $dataDir);
    foreach ($bundle->get($index) as $code => $name) {
        if (null !== $constraint) {
            if ($constraint($code)) {
                $data[$code] = $name;
            }
            continue;
        }
        $data[$code] = $name;
    }
    $collator = new \Collator($locale);
    $collator->asort($data);
    return $data;
}
示例#27
0
 /**
  * @param string $path
  * @param array  $pages
  * @param string $order_by
  * @param array  $manual
  *
  * @throws \RuntimeException
  * @internal
  */
 protected function buildSort($path, array $pages, $order_by = 'default', $manual = null)
 {
     $list = [];
     $header_default = null;
     $header_query = null;
     $sort_flags = SORT_NATURAL | SORT_FLAG_CASE;
     // do this header query work only once
     if (strpos($order_by, 'header.') === 0) {
         $header_query = explode('|', str_replace('header.', '', $order_by));
         if (isset($header_query[1])) {
             $header_default = $header_query[1];
         }
     }
     foreach ($pages as $key => $info) {
         $child = isset($this->instances[$key]) ? $this->instances[$key] : null;
         if (!$child) {
             throw new \RuntimeException("Page does not exist: {$key}");
         }
         switch ($order_by) {
             case 'title':
                 $list[$key] = $child->title();
                 break;
             case 'date':
                 $list[$key] = $child->date();
                 $sort_flags = SORT_REGULAR;
                 break;
             case 'modified':
                 $list[$key] = $child->modified();
                 $sort_flags = SORT_REGULAR;
                 break;
             case 'slug':
                 $list[$key] = $child->slug();
                 break;
             case 'basename':
                 $list[$key] = basename($key);
                 break;
             case is_string($header_query[0]):
                 $child_header = new Header((array) $child->header());
                 $header_value = $child_header->get($header_query[0]);
                 if ($header_value) {
                     $list[$key] = $header_value;
                 } else {
                     $list[$key] = $header_default ?: $key;
                 }
                 $sort_flags = SORT_REGULAR;
                 break;
             case 'manual':
             case 'default':
             default:
                 $list[$key] = $key;
                 $sort_flags = SORT_REGULAR;
         }
     }
     // handle special case when order_by is random
     if ($order_by == 'random') {
         $list = $this->arrayShuffle($list);
     } else {
         // else just sort the list according to specified key
         if (extension_loaded('intl')) {
             $locale = setlocale(LC_COLLATE, 0);
             //`setlocale` with a 0 param returns the current locale set
             $col = \Collator::create($locale);
             if ($col) {
                 $col->asort($list, $sort_flags);
             } else {
                 asort($list, $sort_flags);
             }
         } else {
             asort($list, $sort_flags);
         }
     }
     // Move manually ordered items into the beginning of the list. Order of the unlisted items does not change.
     if (is_array($manual) && !empty($manual)) {
         $new_list = [];
         $i = count($manual);
         foreach ($list as $key => $dummy) {
             $info = $pages[$key];
             $order = array_search($info['slug'], $manual);
             if ($order === false) {
                 $order = $i++;
             }
             $new_list[$key] = (int) $order;
         }
         $list = $new_list;
         // Apply manual ordering to the list.
         asort($list);
     }
     foreach ($list as $key => $sort) {
         $info = $pages[$key];
         $this->sort[$path][$order_by][$key] = $info;
     }
 }
 /**
  *  Sort an array and maintain index association, use Collate from the
  *  PECL "intl" package, if available, for UTF-8 sorting (ex: list of countries).
  *  On Debian/Ubuntu: apt-get install php5-intl
  *
  *  @param array $array array of values
  *
  *  @return  array  Sorted array
  *  @static
  */
 static function asort($array = array())
 {
     $lcMessages = CRM_Utils_System::getUFLocale();
     if ($lcMessages && $lcMessages != 'en_US' && class_exists('Collator')) {
         $collator = new Collator($lcMessages . '.utf8');
         $collator->asort($array);
     } else {
         asort($array);
     }
     return $array;
 }
示例#29
0
 /**
  * Returns the locale names for a locale
  *
  * @param string $locale The locale to use for the locale names
  *
  * @return array              The locale names with their codes as keys
  *
  * @throws \RuntimeException  When the resource bundles cannot be loaded
  */
 public static function getDisplayLocales($locale)
 {
     if (!isset(self::$locales[$locale])) {
         $bundle = \ResourceBundle::create($locale, self::getIcuDataDirectory() . '/names');
         if (null === $bundle) {
             throw new \RuntimeException(sprintf('The locale resource bundle could not be loaded for locale "%s"', $locale));
         }
         $collator = new \Collator($locale);
         $locales = array();
         $bundleLocales = $bundle->get('Locales') ?: array();
         foreach ($bundleLocales as $code => $name) {
             $locales[$code] = $name;
         }
         $fallbackLocale = self::getFallbackLocale($locale);
         if (null !== $fallbackLocale) {
             $locales = array_merge(self::getDisplayLocales($fallbackLocale), $locales);
         }
         $collator->asort($locales);
         self::$locales[$locale] = $locales;
     }
     return self::$locales[$locale];
 }
示例#30
0
文件: index.php 项目: toneiv/Shaarli
function renderPage()
{
    $LINKSDB = new LinkDB($GLOBALS['config']['DATASTORE'], isLoggedIn(), $GLOBALS['config']['HIDE_PUBLIC_LINKS'], $GLOBALS['redirector'], $GLOBALS['config']['REDIRECTOR_URLENCODE']);
    $updater = new Updater(read_updates_file($GLOBALS['config']['UPDATES_FILE']), $GLOBALS, $LINKSDB, isLoggedIn());
    try {
        $newUpdates = $updater->update();
        if (!empty($newUpdates)) {
            write_updates_file($GLOBALS['config']['UPDATES_FILE'], $updater->getDoneUpdates());
        }
    } catch (Exception $e) {
        die($e->getMessage());
    }
    $PAGE = new PageBuilder();
    $PAGE->assign('linkcount', count($LINKSDB));
    $PAGE->assign('privateLinkcount', count_private($LINKSDB));
    // Determine which page will be rendered.
    $query = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
    $targetPage = Router::findPage($query, $_GET, isLoggedIn());
    // Call plugin hooks for header, footer and includes, specifying which page will be rendered.
    // Then assign generated data to RainTPL.
    $common_hooks = array('includes', 'header', 'footer');
    $pluginManager = PluginManager::getInstance();
    foreach ($common_hooks as $name) {
        $plugin_data = array();
        $pluginManager->executeHooks('render_' . $name, $plugin_data, array('target' => $targetPage, 'loggedin' => isLoggedIn()));
        $PAGE->assign('plugins_' . $name, $plugin_data);
    }
    // -------- Display login form.
    if ($targetPage == Router::$PAGE_LOGIN) {
        if ($GLOBALS['config']['OPEN_SHAARLI']) {
            header('Location: ?');
            exit;
        }
        // No need to login for open Shaarli
        $token = '';
        if (ban_canLogin()) {
            $token = getToken();
        }
        // Do not waste token generation if not useful.
        $PAGE->assign('token', $token);
        if (isset($_GET['username'])) {
            $PAGE->assign('username', escape($_GET['username']));
        }
        $PAGE->assign('returnurl', isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : '');
        $PAGE->renderPage('loginform');
        exit;
    }
    // -------- User wants to logout.
    if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) {
        invalidateCaches($GLOBALS['config']['PAGECACHE']);
        logout();
        header('Location: ?');
        exit;
    }
    // -------- Picture wall
    if ($targetPage == Router::$PAGE_PICWALL) {
        // Optionally filter the results:
        $links = $LINKSDB->filterSearch($_GET);
        $linksToDisplay = array();
        // Get only links which have a thumbnail.
        foreach ($links as $link) {
            $permalink = '?' . escape(smallhash($link['linkdate']));
            $thumb = lazyThumbnail($link['url'], $permalink);
            if ($thumb != '') {
                $link['thumbnail'] = $thumb;
                // Thumbnail HTML code.
                $linksToDisplay[] = $link;
                // Add to array.
            }
        }
        $data = array('linksToDisplay' => $linksToDisplay);
        $pluginManager->executeHooks('render_picwall', $data, array('loggedin' => isLoggedIn()));
        foreach ($data as $key => $value) {
            $PAGE->assign($key, $value);
        }
        $PAGE->renderPage('picwall');
        exit;
    }
    // -------- Tag cloud
    if ($targetPage == Router::$PAGE_TAGCLOUD) {
        $tags = $LINKSDB->allTags();
        // We sort tags alphabetically, then choose a font size according to count.
        // First, find max value.
        $maxcount = 0;
        foreach ($tags as $value) {
            $maxcount = max($maxcount, $value);
        }
        // Sort tags alphabetically: case insensitive, support locale if avalaible.
        uksort($tags, function ($a, $b) {
            // Collator is part of PHP intl.
            if (class_exists('Collator')) {
                $c = new Collator(setlocale(LC_COLLATE, 0));
                if (!intl_is_failure(intl_get_error_code())) {
                    return $c->compare($a, $b);
                }
            }
            return strcasecmp($a, $b);
        });
        $tagList = array();
        foreach ($tags as $key => $value) {
            // Tag font size scaling:
            //   default 15 and 30 logarithm bases affect scaling,
            //   22 and 6 are arbitrary font sizes for max and min sizes.
            $size = log($value, 15) / log($maxcount, 30) * 2.2 + 0.8;
            $tagList[$key] = array('count' => $value, 'size' => number_format($size, 2, '.', ''));
        }
        $data = array('tags' => $tagList);
        $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => isLoggedIn()));
        foreach ($data as $key => $value) {
            $PAGE->assign($key, $value);
        }
        $PAGE->renderPage('tagcloud');
        exit;
    }
    // Daily page.
    if ($targetPage == Router::$PAGE_DAILY) {
        showDaily($PAGE, $LINKSDB);
    }
    // ATOM and RSS feed.
    if ($targetPage == Router::$PAGE_FEED_ATOM || $targetPage == Router::$PAGE_FEED_RSS) {
        $feedType = $targetPage == Router::$PAGE_FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM;
        header('Content-Type: application/' . $feedType . '+xml; charset=utf-8');
        // Cache system
        $query = $_SERVER['QUERY_STRING'];
        $cache = new CachedPage($GLOBALS['config']['PAGECACHE'], page_url($_SERVER), startsWith($query, 'do=' . $targetPage) && !isLoggedIn());
        $cached = $cache->cachedVersion();
        if (!empty($cached)) {
            echo $cached;
            exit;
        }
        // Generate data.
        $feedGenerator = new FeedBuilder($LINKSDB, $feedType, $_SERVER, $_GET, isLoggedIn());
        $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0)));
        $feedGenerator->setHideDates($GLOBALS['config']['HIDE_TIMESTAMPS'] && !isLoggedIn());
        $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$GLOBALS['config']['ENABLE_RSS_PERMALINKS']);
        if (!empty($GLOBALS['config']['PUBSUBHUB_URL'])) {
            $feedGenerator->setPubsubhubUrl($GLOBALS['config']['PUBSUBHUB_URL']);
        }
        $data = $feedGenerator->buildData();
        // Process plugin hook.
        $pluginManager = PluginManager::getInstance();
        $pluginManager->executeHooks('render_feed', $data, array('loggedin' => isLoggedIn(), 'target' => $targetPage));
        // Render the template.
        $PAGE->assignAll($data);
        $PAGE->renderPage('feed.' . $feedType);
        $cache->cache(ob_get_contents());
        ob_end_flush();
        exit;
    }
    // Display openseach plugin (XML)
    if ($targetPage == Router::$PAGE_OPENSEARCH) {
        header('Content-Type: application/xml; charset=utf-8');
        $PAGE->assign('serverurl', index_url($_SERVER));
        $PAGE->renderPage('opensearch');
        exit;
    }
    // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...)
    if (isset($_GET['addtag'])) {
        // Get previous URL (http_referer) and add the tag to the searchtags parameters in query.
        if (empty($_SERVER['HTTP_REFERER'])) {
            header('Location: ?searchtags=' . urlencode($_GET['addtag']));
            exit;
        }
        // In case browser does not send HTTP_REFERER
        parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
        // Prevent redirection loop
        if (isset($params['addtag'])) {
            unset($params['addtag']);
        }
        // Check if this tag is already in the search query and ignore it if it is.
        // Each tag is always separated by a space
        if (isset($params['searchtags'])) {
            $current_tags = explode(' ', $params['searchtags']);
        } else {
            $current_tags = array();
        }
        $addtag = true;
        foreach ($current_tags as $value) {
            if ($value === $_GET['addtag']) {
                $addtag = false;
                break;
            }
        }
        // Append the tag if necessary
        if (empty($params['searchtags'])) {
            $params['searchtags'] = trim($_GET['addtag']);
        } else {
            if ($addtag) {
                $params['searchtags'] = trim($params['searchtags']) . ' ' . trim($_GET['addtag']);
            }
        }
        unset($params['page']);
        // We also remove page (keeping the same page has no sense, since the results are different)
        header('Location: ?' . http_build_query($params));
        exit;
    }
    // -------- User clicks on a tag in result count: Remove the tag from the list of searched tags (searchtags=...)
    if (isset($_GET['removetag'])) {
        // Get previous URL (http_referer) and remove the tag from the searchtags parameters in query.
        if (empty($_SERVER['HTTP_REFERER'])) {
            header('Location: ?');
            exit;
        }
        // In case browser does not send HTTP_REFERER
        parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
        // Prevent redirection loop
        if (isset($params['removetag'])) {
            unset($params['removetag']);
        }
        if (isset($params['searchtags'])) {
            $tags = explode(' ', $params['searchtags']);
            // Remove value from array $tags.
            $tags = array_diff($tags, array($_GET['removetag']));
            $params['searchtags'] = implode(' ', $tags);
            if (empty($params['searchtags'])) {
                unset($params['searchtags']);
            }
            unset($params['page']);
            // We also remove page (keeping the same page has no sense, since the results are different)
        }
        header('Location: ?' . http_build_query($params));
        exit;
    }
    // -------- User wants to change the number of links per page (linksperpage=...)
    if (isset($_GET['linksperpage'])) {
        if (is_numeric($_GET['linksperpage'])) {
            $_SESSION['LINKS_PER_PAGE'] = abs(intval($_GET['linksperpage']));
        }
        header('Location: ' . generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('linksperpage')));
        exit;
    }
    // -------- User wants to see only private links (toggle)
    if (isset($_GET['privateonly'])) {
        if (empty($_SESSION['privateonly'])) {
            $_SESSION['privateonly'] = 1;
            // See only private links
        } else {
            unset($_SESSION['privateonly']);
            // See all links
        }
        header('Location: ' . generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('privateonly')));
        exit;
    }
    // -------- Handle other actions allowed for non-logged in users:
    if (!isLoggedIn()) {
        // User tries to post new link but is not logged in:
        // Show login screen, then redirect to ?post=...
        if (isset($_GET['post'])) {
            header('Location: ?do=login&post=' . urlencode($_GET['post']) . (!empty($_GET['title']) ? '&title=' . urlencode($_GET['title']) : '') . (!empty($_GET['description']) ? '&description=' . urlencode($_GET['description']) : '') . (!empty($_GET['source']) ? '&source=' . urlencode($_GET['source']) : ''));
            // Redirect to login page, then back to post link.
            exit;
        }
        showLinkList($PAGE, $LINKSDB);
        if (isset($_GET['edit_link'])) {
            header('Location: ?do=login&edit_link=' . escape($_GET['edit_link']));
            exit;
        }
        exit;
        // Never remove this one! All operations below are reserved for logged in user.
    }
    // -------- All other functions are reserved for the registered user:
    // -------- Display the Tools menu if requested (import/export/bookmarklet...)
    if ($targetPage == Router::$PAGE_TOOLS) {
        $data = array('pageabsaddr' => index_url($_SERVER));
        $pluginManager->executeHooks('render_tools', $data);
        foreach ($data as $key => $value) {
            $PAGE->assign($key, $value);
        }
        $PAGE->renderPage('tools');
        exit;
    }
    // -------- User wants to change his/her password.
    if ($targetPage == Router::$PAGE_CHANGEPASSWORD) {
        if ($GLOBALS['config']['OPEN_SHAARLI']) {
            die('You are not supposed to change a password on an Open Shaarli.');
        }
        if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) {
            if (!tokenOk($_POST['token'])) {
                die('Wrong token.');
            }
            // Go away!
            // Make sure old password is correct.
            $oldhash = sha1($_POST['oldpassword'] . $GLOBALS['login'] . $GLOBALS['salt']);
            if ($oldhash != $GLOBALS['hash']) {
                echo '<script>alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>';
                exit;
            }
            // Save new password
            $GLOBALS['salt'] = sha1(uniqid('', true) . '_' . mt_rand());
            // Salt renders rainbow-tables attacks useless.
            $GLOBALS['hash'] = sha1($_POST['setpassword'] . $GLOBALS['login'] . $GLOBALS['salt']);
            try {
                writeConfig($GLOBALS, isLoggedIn());
            } catch (Exception $e) {
                error_log('ERROR while writing config file after changing password.' . PHP_EOL . $e->getMessage());
                // TODO: do not handle exceptions/errors in JS.
                echo '<script>alert("' . $e->getMessage() . '");document.location=\'?do=tools\';</script>';
                exit;
            }
            echo '<script>alert("Your password has been changed.");document.location=\'?do=tools\';</script>';
            exit;
        } else {
            $PAGE->assign('token', getToken());
            $PAGE->renderPage('changepassword');
            exit;
        }
    }
    // -------- User wants to change configuration
    if ($targetPage == Router::$PAGE_CONFIGURE) {
        if (!empty($_POST['title'])) {
            if (!tokenOk($_POST['token'])) {
                die('Wrong token.');
                // Go away!
            }
            $tz = 'UTC';
            if (!empty($_POST['continent']) && !empty($_POST['city']) && isTimeZoneValid($_POST['continent'], $_POST['city'])) {
                $tz = $_POST['continent'] . '/' . $_POST['city'];
            }
            $GLOBALS['timezone'] = $tz;
            $GLOBALS['title'] = $_POST['title'];
            $GLOBALS['titleLink'] = $_POST['titleLink'];
            $GLOBALS['redirector'] = $_POST['redirector'];
            $GLOBALS['disablesessionprotection'] = !empty($_POST['disablesessionprotection']);
            $GLOBALS['privateLinkByDefault'] = !empty($_POST['privateLinkByDefault']);
            $GLOBALS['config']['ENABLE_RSS_PERMALINKS'] = !empty($_POST['enableRssPermalinks']);
            $GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']);
            $GLOBALS['config']['HIDE_PUBLIC_LINKS'] = !empty($_POST['hidePublicLinks']);
            try {
                writeConfig($GLOBALS, isLoggedIn());
            } catch (Exception $e) {
                error_log('ERROR while writing config file after configuration update.' . PHP_EOL . $e->getMessage());
                // TODO: do not handle exceptions/errors in JS.
                echo '<script>alert("' . $e->getMessage() . '");document.location=\'?do=tools\';</script>';
                exit;
            }
            echo '<script>alert("Configuration was saved.");document.location=\'?do=tools\';</script>';
            exit;
        } else {
            $PAGE->assign('token', getToken());
            $PAGE->assign('title', empty($GLOBALS['title']) ? '' : $GLOBALS['title']);
            $PAGE->assign('redirector', empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']);
            list($timezone_form, $timezone_js) = generateTimeZoneForm($GLOBALS['timezone']);
            $PAGE->assign('timezone_form', $timezone_form);
            $PAGE->assign('timezone_js', $timezone_js);
            $PAGE->renderPage('configure');
            exit;
        }
    }
    // -------- User wants to rename a tag or delete it
    if ($targetPage == Router::$PAGE_CHANGETAG) {
        if (empty($_POST['fromtag']) || empty($_POST['totag']) && isset($_POST['renametag'])) {
            $PAGE->assign('token', getToken());
            $PAGE->assign('tags', $LINKSDB->allTags());
            $PAGE->renderPage('changetag');
            exit;
        }
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // Delete a tag:
        if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) {
            $needle = trim($_POST['fromtag']);
            // True for case-sensitive tag search.
            $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true);
            foreach ($linksToAlter as $key => $value) {
                $tags = explode(' ', trim($value['tags']));
                unset($tags[array_search($needle, $tags)]);
                // Remove tag.
                $value['tags'] = trim(implode(' ', $tags));
                $LINKSDB[$key] = $value;
            }
            $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']);
            echo '<script>alert("Tag was removed from ' . count($linksToAlter) . ' links.");document.location=\'?\';</script>';
            exit;
        }
        // Rename a tag:
        if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) {
            $needle = trim($_POST['fromtag']);
            // True for case-sensitive tag search.
            $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true);
            foreach ($linksToAlter as $key => $value) {
                $tags = explode(' ', trim($value['tags']));
                $tags[array_search($needle, $tags)] = trim($_POST['totag']);
                // Replace tags value.
                $value['tags'] = trim(implode(' ', $tags));
                $LINKSDB[$key] = $value;
            }
            $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']);
            // Save to disk.
            echo '<script>alert("Tag was renamed in ' . count($linksToAlter) . ' links.");document.location=\'?searchtags=' . urlencode($_POST['totag']) . '\';</script>';
            exit;
        }
    }
    // -------- User wants to add a link without using the bookmarklet: Show form.
    if ($targetPage == Router::$PAGE_ADDLINK) {
        $PAGE->renderPage('addlink');
        exit;
    }
    // -------- User clicked the "Save" button when editing a link: Save link to database.
    if (isset($_POST['save_edit'])) {
        // Go away!
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // Remove multiple spaces.
        $tags = trim(preg_replace('/\\s\\s+/', ' ', $_POST['lf_tags']));
        // Remove first '-' char in tags.
        $tags = preg_replace('/(^| )\\-/', '$1', $tags);
        // Remove duplicates.
        $tags = implode(' ', array_unique(explode(' ', $tags)));
        $linkdate = $_POST['lf_linkdate'];
        $url = trim($_POST['lf_url']);
        if (!startsWith($url, 'http:') && !startsWith($url, 'https:') && !startsWith($url, 'ftp:') && !startsWith($url, 'magnet:') && !startsWith($url, '?') && !startsWith($url, 'javascript:')) {
            $url = 'http://' . $url;
        }
        $link = array('title' => trim($_POST['lf_title']), 'url' => $url, 'description' => $_POST['lf_description'], 'private' => isset($_POST['lf_private']) ? 1 : 0, 'linkdate' => $linkdate, 'tags' => str_replace(',', ' ', $tags));
        // If title is empty, use the URL as title.
        if ($link['title'] == '') {
            $link['title'] = $link['url'];
        }
        $pluginManager->executeHooks('save_link', $link);
        $LINKSDB[$linkdate] = $link;
        $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']);
        pubsubhub();
        // If we are called from the bookmarklet, we must close the popup:
        if (isset($_GET['source']) && ($_GET['source'] == 'bookmarklet' || $_GET['source'] == 'firefoxsocialapi')) {
            echo '<script>self.close();</script>';
            exit;
        }
        $returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?';
        $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
        // Scroll to the link which has been edited.
        $location .= '#' . smallHash($_POST['lf_linkdate']);
        // After saving the link, redirect to the page the user was on.
        header('Location: ' . $location);
        exit;
    }
    // -------- User clicked the "Cancel" button when editing a link.
    if (isset($_POST['cancel_edit'])) {
        // If we are called from the bookmarklet, we must close the popup:
        if (isset($_GET['source']) && ($_GET['source'] == 'bookmarklet' || $_GET['source'] == 'firefoxsocialapi')) {
            echo '<script>self.close();</script>';
            exit;
        }
        $returnurl = isset($_POST['returnurl']) ? $_POST['returnurl'] : '?';
        $returnurl .= '#' . smallHash($_POST['lf_linkdate']);
        // Scroll to the link which has been edited.
        $returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
        header('Location: ' . $returnurl);
        // After canceling, redirect to the page the user was on.
        exit;
    }
    // -------- User clicked the "Delete" button when editing a link: Delete link from database.
    if (isset($_POST['delete_link'])) {
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // We do not need to ask for confirmation:
        // - confirmation is handled by JavaScript
        // - we are protected from XSRF by the token.
        $linkdate = $_POST['lf_linkdate'];
        $pluginManager->executeHooks('delete_link', $LINKSDB[$linkdate]);
        unset($LINKSDB[$linkdate]);
        $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']);
        // save to disk
        // If we are called from the bookmarklet, we must close the popup:
        if (isset($_GET['source']) && ($_GET['source'] == 'bookmarklet' || $_GET['source'] == 'firefoxsocialapi')) {
            echo '<script>self.close();</script>';
            exit;
        }
        // Pick where we're going to redirect
        // =============================================================
        // Basically, we can't redirect to where we were previously if it was a permalink
        // or an edit_link, because it would 404.
        // Cases:
        //    - /             : nothing in $_GET, redirect to self
        //    - /?page        : redirect to self
        //    - /?searchterm  : redirect to self (there might be other links)
        //    - /?searchtags  : redirect to self
        //    - /permalink    : redirect to / (the link does not exist anymore)
        //    - /?edit_link   : redirect to / (the link does not exist anymore)
        // PHP treats the permalink as a $_GET variable, so we need to check if every condition for self
        // redirect is not satisfied, and only then redirect to /
        $location = "?";
        // Self redirection
        if (count($_GET) == 0 || isset($_GET['page']) || isset($_GET['searchterm']) || isset($_GET['searchtags'])) {
            if (isset($_POST['returnurl'])) {
                $location = $_POST['returnurl'];
                // Handle redirects given by the form
            } else {
                $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('delete_link'));
            }
        }
        header('Location: ' . $location);
        // After deleting the link, redirect to appropriate location
        exit;
    }
    // -------- User clicked the "EDIT" button on a link: Display link edit form.
    if (isset($_GET['edit_link'])) {
        $link = $LINKSDB[$_GET['edit_link']];
        // Read database
        if (!$link) {
            header('Location: ?');
            exit;
        }
        // Link not found in database.
        $data = array('link' => $link, 'link_is_new' => false, 'token' => getToken(), 'http_referer' => isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : '', 'tags' => $LINKSDB->allTags());
        $pluginManager->executeHooks('render_editlink', $data);
        foreach ($data as $key => $value) {
            $PAGE->assign($key, $value);
        }
        $PAGE->renderPage('editlink');
        exit;
    }
    // -------- User want to post a new link: Display link edit form.
    if (isset($_GET['post'])) {
        $url = cleanup_url($_GET['post']);
        $link_is_new = false;
        // Check if URL is not already in database (in this case, we will edit the existing link)
        $link = $LINKSDB->getLinkFromUrl($url);
        if (!$link) {
            $link_is_new = true;
            $linkdate = strval(date('Ymd_His'));
            // Get title if it was provided in URL (by the bookmarklet).
            $title = empty($_GET['title']) ? '' : escape($_GET['title']);
            // Get description if it was provided in URL (by the bookmarklet). [Bronco added that]
            $description = empty($_GET['description']) ? '' : escape($_GET['description']);
            $tags = empty($_GET['tags']) ? '' : escape($_GET['tags']);
            $private = !empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0;
            // If this is an HTTP(S) link, we try go get the page to extract the title (otherwise we will to straight to the edit form.)
            if (empty($title) && strpos(get_url_scheme($url), 'http') !== false) {
                // Short timeout to keep the application responsive
                list($headers, $content) = get_http_response($url, 4);
                if (strpos($headers[0], '200 OK') !== false) {
                    // Retrieve charset.
                    $charset = get_charset($headers, $content);
                    // Extract title.
                    $title = html_extract_title($content);
                    // Re-encode title in utf-8 if necessary.
                    if (!empty($title) && strtolower($charset) != 'utf-8') {
                        $title = mb_convert_encoding($title, 'utf-8', $charset);
                    }
                }
            }
            if ($url == '') {
                $url = '?' . smallHash($linkdate);
                $title = 'Note: ';
            }
            $url = escape($url);
            $title = escape($title);
            $link = array('linkdate' => $linkdate, 'title' => $title, 'url' => $url, 'description' => $description, 'tags' => $tags, 'private' => $private);
        }
        $data = array('link' => $link, 'link_is_new' => $link_is_new, 'token' => getToken(), 'http_referer' => isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : '', 'source' => isset($_GET['source']) ? $_GET['source'] : '', 'tags' => $LINKSDB->allTags());
        $pluginManager->executeHooks('render_editlink', $data);
        foreach ($data as $key => $value) {
            $PAGE->assign($key, $value);
        }
        $PAGE->renderPage('editlink');
        exit;
    }
    if ($targetPage == Router::$PAGE_EXPORT) {
        // Export links as a Netscape Bookmarks file
        if (empty($_GET['selection'])) {
            $PAGE->renderPage('export');
            exit;
        }
        // export as bookmarks_(all|private|public)_YYYYmmdd_HHMMSS.html
        $selection = $_GET['selection'];
        if (isset($_GET['prepend_note_url'])) {
            $prependNoteUrl = $_GET['prepend_note_url'];
        } else {
            $prependNoteUrl = false;
        }
        try {
            $PAGE->assign('links', NetscapeBookmarkUtils::filterAndFormat($LINKSDB, $selection, $prependNoteUrl, index_url($_SERVER)));
        } catch (Exception $exc) {
            header('Content-Type: text/plain; charset=utf-8');
            echo $exc->getMessage();
            exit;
        }
        $now = new DateTime();
        header('Content-Type: text/html; charset=utf-8');
        header('Content-disposition: attachment; filename=bookmarks_' . $selection . '_' . $now->format(LinkDB::LINK_DATE_FORMAT) . '.html');
        $PAGE->assign('date', $now->format(DateTime::RFC822));
        $PAGE->assign('eol', PHP_EOL);
        $PAGE->assign('selection', $selection);
        $PAGE->renderPage('export.bookmarks');
        exit;
    }
    // -------- User is uploading a file for import
    if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=upload')) {
        // If file is too big, some form field may be missing.
        if (!isset($_POST['token']) || !isset($_FILES) || isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) {
            $returnurl = empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'];
            echo '<script>alert("The file you are trying to upload is probably bigger than what this webserver can accept (' . getMaxFileSize() . ' bytes). Please upload in smaller chunks.");document.location=\'' . escape($returnurl) . '\';</script>';
            exit;
        }
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        importFile($LINKSDB);
        exit;
    }
    // -------- Show upload/import dialog:
    if ($targetPage == Router::$PAGE_IMPORT) {
        $PAGE->assign('token', getToken());
        $PAGE->assign('maxfilesize', getMaxFileSize());
        $PAGE->renderPage('import');
        exit;
    }
    // Plugin administration page
    if ($targetPage == Router::$PAGE_PLUGINSADMIN) {
        $pluginMeta = $pluginManager->getPluginsMeta();
        // Split plugins into 2 arrays: ordered enabled plugins and disabled.
        $enabledPlugins = array_filter($pluginMeta, function ($v) {
            return $v['order'] !== false;
        });
        // Load parameters.
        $enabledPlugins = load_plugin_parameter_values($enabledPlugins, $GLOBALS['plugins']);
        uasort($enabledPlugins, function ($a, $b) {
            return $a['order'] - $b['order'];
        });
        $disabledPlugins = array_filter($pluginMeta, function ($v) {
            return $v['order'] === false;
        });
        $PAGE->assign('enabledPlugins', $enabledPlugins);
        $PAGE->assign('disabledPlugins', $disabledPlugins);
        $PAGE->renderPage('pluginsadmin');
        exit;
    }
    // Plugin administration form action
    if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) {
        try {
            if (isset($_POST['parameters_form'])) {
                unset($_POST['parameters_form']);
                foreach ($_POST as $param => $value) {
                    $GLOBALS['plugins'][$param] = escape($value);
                }
            } else {
                $GLOBALS['config']['ENABLED_PLUGINS'] = save_plugin_config($_POST);
            }
            writeConfig($GLOBALS, isLoggedIn());
        } catch (Exception $e) {
            error_log('ERROR while saving plugin configuration:.' . PHP_EOL . $e->getMessage());
            // TODO: do not handle exceptions/errors in JS.
            echo '<script>alert("' . $e->getMessage() . '");document.location=\'?do=' . Router::$PAGE_PLUGINSADMIN . '\';</script>';
            exit;
        }
        header('Location: ?do=' . Router::$PAGE_PLUGINSADMIN);
        exit;
    }
    // -------- Otherwise, simply display search form and links:
    showLinkList($PAGE, $LINKSDB);
    exit;
}