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); }
/** * @dataProvider asortProvider */ public function testAsortIntl($array, $sortFlag, $expected) { $this->skipIfIntlExtensionIsNotLoaded(); $collator = new \Collator('en'); $collator->asort($array, $sortFlag); $this->assertSame($expected, $array); }
protected function sortByValue($collection) { $collator = new \Collator('fr_FR'); $items = iterator_to_array($collection); $collator->asort($items); return new Map($items); }
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(); } }
/** * @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); } }
/** * {@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; }
/** * {@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; }
/** * 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); } }
/** * 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}"); }
/** * 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]; }
/** * {@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; }
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); }
/** * @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)); }
<?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>
/** * @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); }
/** * 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); }
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); }
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; }
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; }
/** * @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; }
/** * 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]; }
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; }