/**
  * Currency format a number
  *
  * @throws Exception\RuntimeException
  * @param  float|string|int  $number
  * @return string
  */
 public function format($number, ArrayObject $row = null)
 {
     $locale = $this->params['locale'];
     //$formatterId = md5($locale);
     $formatterId = $locale . (string) $this->params['pattern'];
     if (!array_key_exists($formatterId, $this->formatters)) {
         $this->loadFormatterId($formatterId);
     }
     if ($number !== null && !is_numeric($number)) {
         $this->throwNumberFormatterException($this->formatters[$formatterId], $number);
     }
     if ($this->unit_column !== null) {
         if (!isset($row[$this->unit_column])) {
             throw new Exception\RuntimeException(__METHOD__ . " Cannot determine unit code based on column '{$this->unit_column}'.");
         }
         $value = $this->formatters[$formatterId]->format($number) . ' ' . $row[$this->unit_column];
     } elseif ($this->params['unit'] != '') {
         $value = $this->formatters[$formatterId]->format($number) . ' ' . $this->params['unit'];
     } else {
         throw new Exception\RuntimeException(__METHOD__ . " Unit code must be set prior to use the UnitFormatter");
     }
     if (intl_is_failure($this->formatters[$formatterId]->getErrorCode())) {
         $this->throwNumberFormatterException($this->formatters[$formatterId], $number);
     }
     return $value;
 }
 /**
  *
  * {@inheritdoc}
  *
  */
 public function load($resource, $locale, $domain = 'messages')
 {
     if (!stream_is_local($resource)) {
         throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
     }
     if (!is_dir($resource)) {
         throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
     }
     try {
         $rb = new \ResourceBundle($locale, $resource);
     } catch (\Exception $e) {
         // HHVM compatibility: constructor throws on invalid resource
         $rb = null;
     }
     if (!$rb) {
         throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource));
     } elseif (intl_is_failure($rb->getErrorCode())) {
         throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
     }
     $messages = $this->flatten($rb);
     $catalogue = new MessageCatalogue($locale);
     $catalogue->add($messages, $domain);
     if (class_exists('Symfony\\Component\\Config\\Resource\\DirectoryResource')) {
         $catalogue->addResource(new DirectoryResource($resource));
     }
     return $catalogue;
 }
 /**
  * Transforms a localized number into an integer or float
  *
  * @param string $value
  */
 public function reverseTransform($value)
 {
     if (!is_string($value)) {
         throw new \InvalidArgumentException(sprintf('Expected argument of type string, %s given', gettype($value)));
     }
     $formatter = $this->getNumberFormatter();
     $value = $formatter->parse($value);
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new TransformationFailedException($formatter->getErrorMessage());
     }
     return $value;
 }
 /**
  * {@inheritdoc}
  */
 public function load($resource, $locale, $domain = 'messages')
 {
     $rb = new \ResourceBundle($locale, $resource);
     if (!$rb) {
         throw new \RuntimeException("cannot load this resource : {$resource}");
     } elseif (intl_is_failure($rb->getErrorCode())) {
         throw new \RuntimeException($rb->getErrorMessage(), $rb->getErrorCode());
     }
     $messages = $this->flatten($rb);
     $catalogue = new MessageCatalogue($locale);
     $catalogue->add($messages, $domain);
     $catalogue->addResource(new FileResource($resource . '.dat'));
     return $catalogue;
 }
 /**
  * Transforms a localized number into an integer or float
  *
  * @param string $value
  */
 public function reverseTransform($value)
 {
     if (!is_string($value)) {
         throw new UnexpectedTypeException($value, 'string');
     }
     if ('' === $value) {
         return null;
     }
     $formatter = $this->getNumberFormatter();
     $value = $formatter->parse($value);
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new TransformationFailedException($formatter->getErrorMessage());
     }
     return $value;
 }
 /**
  * {@inheritdoc}
  */
 public function validate($value, Constraint $constraint)
 {
     if (null === $value || '' === $value) {
         return;
     }
     if (!is_scalar($value)) {
         throw new UnexpectedTypeException($value, 'string');
     }
     $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL);
     $position = 0;
     $formatter->parse($value, \NumberFormatter::TYPE_DOUBLE, $position);
     if (intl_is_failure($formatter->getErrorCode()) || $position < strlen($value)) {
         /** @var Decimal $constraint */
         $this->context->addViolation($constraint->message, ['{{ value }}' => $this->formatValue($value)]);
     }
 }
 /**
  * Transforms between a percentage value into a normalized format (integer or float).
  *
  * @param  number $value  Percentage value.
  * @return number         Normalized value.
  */
 public function reverseTransform($value)
 {
     if (!is_string($value)) {
         throw new \InvalidArgumentException(sprintf('Expected argument of type string, %s given', gettype($value)));
     }
     $formatter = $this->getNumberFormatter();
     // replace normal spaces so that the formatter can read them
     $value = $formatter->parse(str_replace(' ', ' ', $value));
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new TransformationFailedException($formatter->getErrorMessage());
     }
     if (self::FRACTIONAL == $this->getOption('type')) {
         $value /= 100;
     }
     return $value;
 }
 /**
  * {@inheritdoc}
  */
 public function reverseTransform($value)
 {
     if (!is_string($value)) {
         throw new TransformationFailedException('Expected a string.');
     }
     if ('' === $value) {
         return;
     }
     if ('NaN' === $value) {
         throw new TransformationFailedException('"NaN" is not a valid integer');
     }
     $formatter = $this->getNumberFormatter();
     $value = $formatter->parse($value, PHP_INT_SIZE == 8 ? $formatter::TYPE_INT64 : $formatter::TYPE_INT32);
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new TransformationFailedException($formatter->getErrorMessage());
     }
     return $value;
 }
 /**
  * Transforms between a percentage value into a normalized format (integer or float).
  *
  * @param  number $value  Percentage value.
  * @return number         Normalized value.
  */
 public function reverseTransform($value)
 {
     if (!is_string($value)) {
         throw new UnexpectedTypeException($value, 'string');
     }
     if ('' === $value) {
         return null;
     }
     $formatter = $this->getNumberFormatter();
     // replace normal spaces so that the formatter can read them
     $value = $formatter->parse(str_replace(' ', ' ', $value));
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new TransformationFailedException($formatter->getErrorMessage());
     }
     if (self::FRACTIONAL == $this->getOption('type')) {
         $value /= 100;
     }
     return $value;
 }
 /**
  * @inheritdoc
  */
 public function transform($value)
 {
     if (!is_string($value) && !is_numeric($value)) {
         throw new TransformationFailedException(sprintf('Expected a string to transform, got "%s" instead.', json_encode($value)));
     }
     if ('' === $value) {
         return null;
     }
     if ('NaN' === $value) {
         throw new TransformationFailedException('"NaN" is not a valid number');
     }
     $position = 0;
     $formatter = $this->getNumberFormatter();
     $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
     $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
     if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) {
         $value = str_replace('.', $decSep, $value);
     }
     if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) {
         $value = str_replace(',', $decSep, $value);
     }
     $result = $formatter->parse($value, \NumberFormatter::TYPE_DOUBLE, $position);
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new TransformationFailedException($formatter->getErrorMessage());
     }
     if ($result >= PHP_INT_MAX || $result <= -PHP_INT_MAX) {
         throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like');
     }
     $encoding = mb_detect_encoding($value);
     $length = mb_strlen($value, $encoding);
     // After parsing, position holds the index of the character where the parsing stopped
     if ($position < $length) {
         // Check if there are unrecognized characters at the end of the
         // number (excluding whitespace characters)
         $remainder = trim(mb_substr($value, $position, $length, $encoding), " \t\n\r\v ");
         if ('' !== $remainder) {
             throw new TransformationFailedException(sprintf('The number contains unrecognized characters: "%s"', $remainder));
         }
     }
     // Only the format() method in the NumberFormatter rounds, whereas parse() does not
     return $this->round($result);
 }
Exemple #11
0
 /**
  * {@inheritdoc}
  */
 public function format($value, array $options = array())
 {
     if (null === $value) {
         return $options['null_value'];
     }
     if (!is_numeric($value)) {
         throw FormatterException::invalidType($this, $value, 'numeric value');
     }
     $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL);
     if (null !== $options['precision']) {
         $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $options['precision']);
         $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $options['rounding_mode']);
     }
     $formatter->setAttribute(\NumberFormatter::GROUPING_USED, $options['grouping']);
     $value = $formatter->format($value);
     if (intl_is_failure($formatter->getErrorCode())) {
         throw FormatterException::intlError($this, $formatter->getErrorMessage());
     }
     return $value;
 }
 /**
  * {@inheritdoc}
  */
 public function load($resource, $locale, $domain = 'messages')
 {
     if (!stream_is_local($resource . '.dat')) {
         throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
     }
     if (!file_exists($resource . '.dat')) {
         throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
     }
     $rb = new \ResourceBundle($locale, $resource);
     if (!$rb) {
         throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource));
     } elseif (intl_is_failure($rb->getErrorCode())) {
         throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
     }
     $messages = $this->flatten($rb);
     $catalogue = new MessageCatalogue($locale);
     $catalogue->add($messages, $domain);
     $catalogue->addResource(new FileResource($resource . '.dat'));
     return $catalogue;
 }
 /**
  * {@inheritdoc}
  */
 public function format($value, array $options = array())
 {
     if (null === $value) {
         return $options['null_value'];
     }
     if (!$value instanceof \DateTime) {
         throw FormatterException::invalidType($this, $value, 'DateTime instance');
     }
     $dateTime = clone $value;
     if ('UTC' !== $options['time_zone']) {
         $dateTime->setTimezone(new \DateTimeZone('UTC'));
     }
     $formatter = new \IntlDateFormatter(\Locale::getDefault(), $options['date_format'], $options['time_format'], $options['time_zone'], $options['calendar'], $options['pattern']);
     $formatter->setLenient(false);
     $value = $formatter->format((int) $dateTime->format('U'));
     if (intl_is_failure(intl_get_error_code())) {
         throw FormatterException::intlError($this, intl_get_error_message());
     }
     $value = preg_replace('~GMT\\+00:00$~', 'GMT', $value);
     return $value;
 }
 /**
  * {@inheritdoc}
  */
 public function validate($value, Constraint $constraint)
 {
     if (null === $value || '' === $value) {
         return;
     }
     if (!is_scalar($value)) {
         throw new UnexpectedTypeException($value, 'string');
     }
     $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL);
     $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, \NumberFormatter::ROUND_DOWN);
     $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, 0);
     $formatter->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, 0);
     $formatter->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, 0);
     $decimalSeparator = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
     $position = 0;
     $formatter->parse($value, PHP_INT_SIZE == 8 ? $formatter::TYPE_INT64 : $formatter::TYPE_INT32, $position);
     if (intl_is_failure($formatter->getErrorCode()) || strpos($value, $decimalSeparator) !== false || $position < strlen($value)) {
         /** @var Integer $constraint */
         $this->context->addViolation($constraint->message, ['{{ value }}' => $this->formatValue($value)]);
     }
 }
 /**
  * Transforms a normalized format into a localized money string.
  *
  * @param MoneyValue $value Normalized number
  *
  * @throws TransformationFailedException If the given value is not numeric or
  *                                       if the value can not be transformed
  *
  * @return string Localized money string
  */
 public function transform($value)
 {
     if (null === $value) {
         return '';
     }
     if (!$value instanceof MoneyValue) {
         throw new TransformationFailedException('Expected a MoneyValue object.');
     }
     if (!is_numeric($value->value)) {
         throw new TransformationFailedException('Expected a numeric value.');
     }
     $amountValue = $value->value;
     $amountValue /= $this->divisor;
     $formatter = $this->getNumberFormatter();
     $value = $formatter->formatCurrency($amountValue, $value->currency);
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new TransformationFailedException($formatter->getErrorMessage());
     }
     // Convert fixed spaces to normal ones
     $value = str_replace(" ", ' ', $value);
     return $value;
 }
 public function getDisplayedValue($value)
 {
     if ($value !== null && $value !== '') {
         $formatter = new \NumberFormatter($this->locale, $this->style);
         if ($this->precision !== null) {
             $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->precision);
             $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $this->roundingMode);
         }
         if ($this->ruleSet !== null) {
             $formatter->setTextAttribute(\NumberFormatter::DEFAULT_RULESET, $this->ruleSet);
         }
         $formatter->setAttribute(\NumberFormatter::GROUPING_USED, $this->grouping);
         if ($this->style === \NumberFormatter::PERCENT && !$this->fractional) {
             $value /= 100;
         }
         if ($this->style === \NumberFormatter::CURRENCY) {
             if ($this->currencyCode === null) {
                 $this->currencyCode = $formatter->getTextAttribute(\NumberFormatter::CURRENCY_CODE);
             }
             if (strlen($this->currencyCode) !== 3) {
                 throw new TransformationFailedException('Your locale definition is not complete, you have to define a language and a country. (.e.g en_US, fr_FR)');
             }
             $value = $formatter->formatCurrency($value, $this->currencyCode);
         } else {
             $value = $formatter->format($value);
         }
         if (intl_is_failure($formatter->getErrorCode())) {
             throw new TransformationFailedException($formatter->getErrorMessage());
         }
         if (key_exists((string) $value, $this->values)) {
             $value = $this->values[$value];
         }
         return $value;
     }
     return '';
 }
 /**
  * @dataProvider parseErrorProvider
  */
 public function testParseErrorIntl($pattern, $value)
 {
     $errorCode = StubIntl::U_PARSE_ERROR;
     $errorMessage = 'Date parsing failed: U_PARSE_ERROR';
     $this->skipIfIntlExtensionIsNotLoaded();
     $formatter = $this->createIntlFormatter($pattern);
     $this->assertFalse($formatter->parse($value));
     $this->assertSame($errorMessage, intl_get_error_message());
     $this->assertSame($errorCode, intl_get_error_code());
     $this->assertTrue(intl_is_failure(intl_get_error_code()));
 }
 /**
  * Transforms a localized number into an integer or float.
  *
  * @param string $value The localized value
  *
  * @return int|float The numeric value
  *
  * @throws TransformationFailedException If the given value is not a string
  *                                       or if the value can not be transformed.
  */
 public function reverseTransform($value)
 {
     if (!is_string($value)) {
         throw new TransformationFailedException('Expected a string.');
     }
     if ('' === $value) {
         return;
     }
     if ('NaN' === $value) {
         throw new TransformationFailedException('"NaN" is not a valid number');
     }
     $position = 0;
     $formatter = $this->getNumberFormatter();
     $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
     $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
     if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) {
         $value = str_replace('.', $decSep, $value);
     }
     if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) {
         $value = str_replace(',', $decSep, $value);
     }
     if (false !== strpos($value, $decSep)) {
         $type = \NumberFormatter::TYPE_DOUBLE;
     } else {
         $type = PHP_INT_SIZE === 8 ? \NumberFormatter::TYPE_INT64 : \NumberFormatter::TYPE_INT32;
     }
     $result = $formatter->parse($value, $type, $position);
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new TransformationFailedException($formatter->getErrorMessage());
     }
     if ($result >= PHP_INT_MAX || $result <= -PHP_INT_MAX) {
         throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like');
     }
     if (false !== ($encoding = mb_detect_encoding($value, null, true))) {
         $length = mb_strlen($value, $encoding);
         $remainder = mb_substr($value, $position, $length, $encoding);
     } else {
         $length = strlen($value);
         $remainder = substr($value, $position, $length);
     }
     // After parsing, position holds the index of the character where the
     // parsing stopped
     if ($position < $length) {
         // Check if there are unrecognized characters at the end of the
         // number (excluding whitespace characters)
         $remainder = trim($remainder, " \t\n\r\v ");
         if ('' !== $remainder) {
             throw new TransformationFailedException(sprintf('The number contains unrecognized characters: "%s"', $remainder));
         }
     }
     // NumberFormatter::parse() does not round
     return $this->round($result);
 }
 /**
  * @dataProvider parseProvider
  */
 public function testParseIntl($pattern, $value, $expected, $errorCode = 0, $errorMessage = 'U_ZERO_ERROR')
 {
     $this->skipIfIntlExtensionIsNotLoaded();
     $formatter = $this->createIntlFormatter($pattern);
     $this->assertSame($expected, $formatter->parse($value));
     $this->assertSame($errorMessage, intl_get_error_message());
     $this->assertSame($errorCode, intl_get_error_code());
     $this->assertSame($errorCode != 0, intl_is_failure(intl_get_error_code()));
 }
Exemple #20
0
 /**
  * Returns true if and only if $value is a valid integer
  *
  * @param  string|int $value
  * @return bool
  * @throws Exception\InvalidArgumentException
  */
 public function isValid($value)
 {
     if (!is_string($value) && !is_int($value) && !is_float($value)) {
         $this->error(self::INVALID);
         return false;
     }
     if (is_int($value)) {
         return true;
     }
     $this->setValue($value);
     $locale = $this->getLocale();
     $format = new NumberFormatter($locale, NumberFormatter::DECIMAL);
     if (intl_is_failure($format->getErrorCode())) {
         throw new Exception\InvalidArgumentException("Invalid locale string given");
     }
     $parsedInt = $format->parse($value, NumberFormatter::TYPE_INT64);
     if (intl_is_failure($format->getErrorCode())) {
         $this->error(self::NOT_INT);
         return false;
     }
     $decimalSep = $format->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
     $groupingSep = $format->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
     $valueFiltered = str_replace($groupingSep, '', $value);
     $valueFiltered = str_replace($decimalSep, '.', $valueFiltered);
     if (strval($parsedInt) !== $valueFiltered) {
         $this->error(self::NOT_INT);
         return false;
     }
     return true;
 }
 protected function isIntlFailure($errorCode)
 {
     return intl_is_failure($errorCode);
 }
    /**
     * Transforms a localized number into an integer or float
     *
     * @param string $value The localized value
     *
     * @return integer|float The numeric value
     *
     * @throws UnexpectedTypeException if the given value is not a string
     * @throws TransformationFailedException if the value can not be transformed
     */
    public function reverseTransform($value)
    {
        if (!is_string($value)) {
            throw new UnexpectedTypeException($value, 'string');
        }

        if ('' === $value) {
            return null;
        }

        if ('NaN' === $value) {
            throw new TransformationFailedException('"NaN" is not a valid number');
        }

        $position = 0;
        $formatter = $this->getNumberFormatter();
        $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
        $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);

        if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) {
            $value = str_replace('.', $decSep, $value);
        }

        if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) {
            $value = str_replace(',', $decSep, $value);
        }

        $result = $formatter->parse($value, \NumberFormatter::TYPE_DOUBLE, $position);

        if (intl_is_failure($formatter->getErrorCode())) {
            throw new TransformationFailedException($formatter->getErrorMessage());
        }

        if ($result >= INF || $result <= -INF) {
            throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like');
        }

        // After parsing, position holds the index of the character where the
        // parsing stopped
        if ($position < strlen($value)) {
            // Check if there are unrecognized characters at the end of the
            // number
            $remainder = substr($value, $position);

            // Remove all whitespace characters
            if ('' !== preg_replace('/[\s\xc2\xa0]*/', '', $remainder)) {
                throw new TransformationFailedException(
                    sprintf('The number contains unrecognized characters: "%s"',
                        $remainder
                    ));
            }
        }

        return $result;
    }
Exemple #23
0
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;
}
 /**
  * Transforms a localized number into an integer or float
  *
  * @param string $value The localized value
  *
  * @return integer|float The numeric value
  *
  * @throws TransformationFailedException If the given value is not a string
  *                                       or if the value can not be transformed.
  */
 public function reverseTransform($value)
 {
     if (!is_string($value)) {
         throw new TransformationFailedException('Expected a string.');
     }
     if ('' === $value) {
         return null;
     }
     if ('NaN' === $value) {
         throw new TransformationFailedException('"NaN" is not a valid number');
     }
     $formatter = $this->getNumberFormatter();
     $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
     $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
     if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) {
         $value = str_replace('.', $decSep, $value);
     }
     if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) {
         $value = str_replace(',', $decSep, $value);
     }
     $value = $formatter->parse($value);
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new TransformationFailedException($formatter->getErrorMessage());
     }
     if ($value >= INF || $value <= -INF) {
         throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like');
     }
     return $value;
 }
Exemple #25
0
 /**
  * Returns true if and only if $value is a floating-point value
  *
  * @param  string $value
  * @return bool
  * @throws ValidatorException\InvalidArgumentException
  */
 public function isValid($value)
 {
     if (!is_string($value)) {
         $this->error(self::INVALID);
         return false;
     }
     $this->setValue($value);
     try {
         $formatter = $this->getIntlDateFormatter();
         if (intl_is_failure($formatter->getErrorCode())) {
             throw new ValidatorException\InvalidArgumentException($formatter->getErrorMessage());
         }
     } catch (IntlException $intlException) {
         throw new ValidatorException\InvalidArgumentException($intlException->getMessage(), 0, $intlException);
     }
     try {
         $timestamp = $formatter->parse($value);
         if (intl_is_failure($formatter->getErrorCode()) || $timestamp === false) {
             $this->error(self::INVALID_DATETIME);
             $this->invalidateFormatter = true;
             return false;
         }
     } catch (IntlException $intlException) {
         $this->error(self::INVALID_DATETIME);
         $this->invalidateFormatter = true;
         return false;
     }
     return true;
 }
 /**
  * Returns a non lenient configured IntlDateFormatter
  *
  * @return IntlDateFormatter
  */
 public function filter($value)
 {
     if (!is_string($value)) {
         return $value;
     }
     $dateType = $this->getDateType();
     $timeType = $this->getTimeType();
     $locale = $this->getLocale();
     $timezone = $this->getTimezone();
     $calendar = $this->getCalendar();
     $pattern = $this->getPattern();
     $formatterId = md5($dateType . "" . $timeType . "" . $locale . "" . $timezone . "" . $calendar . "" . $pattern);
     if (!isset($this->formatters[$formatterId])) {
         try {
             $this->formatters[$formatterId] = new IntlDateFormatter($locale, $dateType, $timeType, $timezone, IntlDateFormatter::GREGORIAN);
             $this->formatters[$formatterId]->setLenient(false);
         } catch (IntlException $intlException) {
             // throw new FilterException\InvalidArgumentException($intlException->getMessage(), 0, $intlException);
             return $value;
         }
     }
     $this->setTimezone($this->formatters[$formatterId]->getTimezone()->getID());
     $this->setCalendar($this->formatters[$formatterId]->getCalendar());
     $timestamp = $this->formatters[$formatterId]->parse($value);
     $this->formatters[$formatterId]->setPattern($this->getPattern());
     $formatted = $this->formatters[$formatterId]->format($timestamp);
     if (intl_is_failure($this->formatters[$formatterId]->getErrorCode())) {
         // throw new FilterException\InvalidArgumentException($this->formatters[$formatterId]->getErrorMessage());
         return $value;
     }
     return $formatted;
 }
Exemple #27
0
function check($err_code)
{
    var_export(intl_is_failure($err_code));
    echo "\n";
}
 /**
  * @dataProvider parseProvider
  */
 public function testParseIntl($value, $expected, $message = '')
 {
     $this->skipIfIntlExtensionIsNotLoaded();
     $this->skipIfICUVersionIsTooOld();
     $formatter = $this->getIntlFormatterWithDecimalStyle();
     $parsedValue = $formatter->parse($value, \NumberFormatter::TYPE_DOUBLE);
     $this->assertSame($expected, $parsedValue, $message);
     if ($expected === false) {
         $errorCode = StubIntl::U_PARSE_ERROR;
         $errorMessage = 'Number parsing failed: U_PARSE_ERROR';
     } else {
         $errorCode = StubIntl::U_ZERO_ERROR;
         $errorMessage = 'U_ZERO_ERROR';
     }
     $this->assertSame($errorMessage, intl_get_error_message());
     $this->assertSame($errorCode, intl_get_error_code());
     $this->assertSame($errorCode > 0, intl_is_failure(intl_get_error_code()));
     $this->assertSame($errorMessage, $formatter->getErrorMessage());
     $this->assertSame($errorCode, $formatter->getErrorCode());
     $this->assertSame($errorCode > 0, intl_is_failure($formatter->getErrorCode()));
 }
 /**
  * Returns true if and only if $value is a floating-point value
  *
  * @param  string                             $value
  * @return bool
  * @throws ValidatorException\InvalidArgumentException
  */
 public function isValid($value)
 {
     if (!is_string($value)) {
         $this->error(self::INVALID);
         return false;
     }
     $this->setValue($value);
     $formatter = $this->getIntlDateFormatter();
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new ValidatorException\InvalidArgumentException("Invalid locale string given");
     }
     $position = 0;
     $parsedDate = $formatter->parse($value, $position);
     if (intl_is_failure($formatter->getErrorCode())) {
         $this->error(self::INVALID_DATETIME);
         return false;
     }
     if ($position != strlen($value)) {
         $this->error(self::INVALID_DATETIME);
         return false;
     }
     return true;
 }
Exemple #30
0
 /**
  * Returns true if and only if $value is a floating-point value. Uses the formal definition of a float as described
  * in the PHP manual: {@link http://www.php.net/float}
  *
  * @param  string $value
  * @return bool
  * @throws Exception\InvalidArgumentException
  */
 public function isValid($value)
 {
     if (!is_scalar($value) || is_bool($value)) {
         $this->error(self::INVALID);
         return false;
     }
     $this->setValue($value);
     if (is_float($value) || is_int($value)) {
         return true;
     }
     // Need to check if this is scientific formatted string. If not, switch to decimal.
     $formatter = new NumberFormatter($this->getLocale(), NumberFormatter::SCIENTIFIC);
     if (intl_is_failure($formatter->getErrorCode())) {
         throw new Exception\InvalidArgumentException($formatter->getErrorMessage());
     }
     if (StringUtils::hasPcreUnicodeSupport()) {
         $exponentialSymbols = '[Ee' . $formatter->getSymbol(NumberFormatter::EXPONENTIAL_SYMBOL) . ']+';
         $search = '/' . $exponentialSymbols . '/u';
     } else {
         $exponentialSymbols = '[Ee]';
         $search = '/' . $exponentialSymbols . '/';
     }
     if (!preg_match($search, $value)) {
         $formatter = new NumberFormatter($this->getLocale(), NumberFormatter::DECIMAL);
     }
     /**
      * @desc There are seperator "look-alikes" for decimal and group seperators that are more commonly used than the
      *       official unicode chracter. We need to replace those with the real thing - or remove it.
      */
     $groupSeparator = $formatter->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
     $decSeparator = $formatter->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
     //NO-BREAK SPACE and ARABIC THOUSANDS SEPARATOR
     if ($groupSeparator == " ") {
         $value = str_replace(' ', $groupSeparator, $value);
     } elseif ($groupSeparator == "٬") {
         //NumberFormatter doesn't have grouping at all for Arabic-Indic
         $value = str_replace(array('\'', $groupSeparator), '', $value);
     }
     //ARABIC DECIMAL SEPARATOR
     if ($decSeparator == "٫") {
         $value = str_replace(',', $decSeparator, $value);
     }
     $groupSeparatorPosition = $this->wrapper->strpos($value, $groupSeparator);
     $decSeparatorPosition = $this->wrapper->strpos($value, $decSeparator);
     //We have seperators, and they are flipped. i.e. 2.000,000 for en-US
     if ($groupSeparatorPosition && $decSeparatorPosition && $groupSeparatorPosition > $decSeparatorPosition) {
         return false;
     }
     //If we have Unicode support, we can use the real graphemes, otherwise, just the ASCII characters
     $decimal = '[' . preg_quote($decSeparator, '/') . ']';
     $prefix = '[+-]';
     $exp = $exponentialSymbols;
     $numberRange = '0-9';
     $useUnicode = '';
     $suffix = '';
     if (StringUtils::hasPcreUnicodeSupport()) {
         $prefix = '[' . preg_quote($formatter->getTextAttribute(NumberFormatter::POSITIVE_PREFIX) . $formatter->getTextAttribute(NumberFormatter::NEGATIVE_PREFIX) . $formatter->getSymbol(NumberFormatter::PLUS_SIGN_SYMBOL) . $formatter->getSymbol(NumberFormatter::MINUS_SIGN_SYMBOL), '/') . ']{0,3}';
         $suffix = $formatter->getTextAttribute(NumberFormatter::NEGATIVE_SUFFIX) ? '[' . preg_quote($formatter->getTextAttribute(NumberFormatter::POSITIVE_SUFFIX) . $formatter->getTextAttribute(NumberFormatter::NEGATIVE_SUFFIX) . $formatter->getSymbol(NumberFormatter::PLUS_SIGN_SYMBOL) . $formatter->getSymbol(NumberFormatter::MINUS_SIGN_SYMBOL), '/') . ']{0,3}' : '';
         $numberRange = '\\p{N}';
         $useUnicode = 'u';
     }
     /**
      * @desc Match against the formal definition of a float. The
      *       exponential number check is modified for RTL non-Latin number
      *       systems (Arabic-Indic numbering). I'm also switching out the period
      *       for the decimal separator. The formal definition leaves out +- from
      *       the integer and decimal notations so add that.  This also checks
      *       that a grouping sperator is not in the last GROUPING_SIZE graphemes
      *       of the string - i.e. 10,6 is not valid for en-US.
      * @see http://www.php.net/float
      */
     $lnum = '[' . $numberRange . ']+';
     $dnum = '(([' . $numberRange . ']*' . $decimal . $lnum . ')|(' . $lnum . $decimal . '[' . $numberRange . ']*))';
     $expDnum = '((' . $prefix . '((' . $lnum . '|' . $dnum . ')' . $exp . $prefix . $lnum . ')' . $suffix . ')|' . '(' . $suffix . '(' . $lnum . $prefix . $exp . '(' . $dnum . '|' . $lnum . '))' . $prefix . '))';
     // LEFT-TO-RIGHT MARK (U+200E) is messing up everything for the handful
     // of locales that have it
     $lnumSearch = str_replace("‎", '', '/^' . $prefix . $lnum . $suffix . '$/' . $useUnicode);
     $dnumSearch = str_replace("‎", '', '/^' . $prefix . $dnum . $suffix . '$/' . $useUnicode);
     $expDnumSearch = str_replace("‎", '', '/^' . $expDnum . '$/' . $useUnicode);
     $value = str_replace("‎", '', $value);
     $unGroupedValue = str_replace($groupSeparator, '', $value);
     // No strrpos() in wrappers yet. ICU 4.x doesn't have grouping size for
     // everything. ICU 52 has 3 for ALL locales.
     $groupSize = $formatter->getAttribute(NumberFormatter::GROUPING_SIZE) ? $formatter->getAttribute(NumberFormatter::GROUPING_SIZE) : 3;
     $lastStringGroup = $this->wrapper->substr($value, -$groupSize);
     if ((preg_match($lnumSearch, $unGroupedValue) || preg_match($dnumSearch, $unGroupedValue) || preg_match($expDnumSearch, $unGroupedValue)) && false === $this->wrapper->strpos($lastStringGroup, $groupSeparator)) {
         return true;
     }
     return false;
 }