Release v0.11.0
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEWe5LuNiFNDXAgI8BOzJIyqqwgW4FAl08H7AACgkQOzJIyqqw gW4dEw/9F55N9HMK1xTByxsnrMihjzBaKKc1lBBNJizAXrX2QchgnhE15ATRnQNy /7GUU8hCRukBsffMp7Ve1tbPkVvQwWgyQn2Hpp+ayGNWgQYrU1jNSaCQcbyxybyP 6e+8DFAdDsleHiYCSZBPUHMpiJyQWsVBDV1wQPRrqvm+JYE3+9IwHzm+9/y4sk55 7bp5Mj7fYyts5AJfLj9gxg2juGRnnhKXGWj2WI4Yk1mpwQLFSf43wC8lFf0ASY1J PfhjwOOFCRv/7LOL66nIPp74+pKcyO/S8p2m/pFNgrHL2bJXaAmFMPmYQjyoFmaA 83iM5Jv3fBXMSf/iHnPvQlD0nmIvXUeu5ftBUIE/C4Uwu8LZTlOsPelW1dH5ygGa TVaA3/vlRhDWATe9mRNrHPHQT3VoxHg8U3qIv3p3cakj1uRFaFvkKhI7dEoqFSJY zsmISLbPMbmvJkMMNT4sI2q3ioyGDiU0OSayKocJziiu/H9+c2Pdty3YOSvJp/SX sjgqSX/hwtNmpQnS63dweDLoBGWjj01MYgedI9r64kmfW3QoSYsdVfykEMHIfofw /g8hRMBmuzK0VuDrla6DIBl7s58w0Uepr+e/lFMI4pzwHzxzUCZ5lc6wG0yCxuq2 R+wTbpLqeXghKIaprmxq9i1TnAiCIl+lmw9zKj3M3fXwBGQ8e4I= =c7Xq -----END PGP SIGNATURE----- Merge tag 'v0.11.0' into myShaarli_commu Release v0.11.0
This commit is contained in:
commit
984073a980
212 changed files with 5288 additions and 5465 deletions
|
@ -2,9 +2,11 @@ options:
|
|||
max-warnings: 0
|
||||
rules:
|
||||
property-sort-order:
|
||||
- 1
|
||||
-
|
||||
order: 'concentric'
|
||||
- 0
|
||||
# Sort order rule does not work with CSS variables: https://github.com/sasstools/sass-lint/issues/1161
|
||||
# - 1
|
||||
# -
|
||||
# order: 'concentric'
|
||||
no-important:
|
||||
- 0
|
||||
no-vendor-prefixes:
|
||||
|
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -22,15 +22,17 @@ inc/languages/*/LC_MESSAGES/shaarli.mo
|
|||
|
||||
# Development and test resources
|
||||
coverage
|
||||
doxygen
|
||||
sandbox
|
||||
phpmd.html
|
||||
phpdoc.xml
|
||||
|
||||
# User plugin configuration
|
||||
plugins/*/config.php
|
||||
plugins/default_colors/default_colors.css
|
||||
|
||||
# HTML documentation
|
||||
doc/html/
|
||||
doc/phpdoc/
|
||||
|
||||
# 3rd party themes
|
||||
tpl/*
|
||||
|
@ -52,3 +54,6 @@ tpl/vintage/css
|
|||
tpl/vintage/img
|
||||
|
||||
.composer.lock
|
||||
|
||||
# Documented scripts
|
||||
generate_templates.php
|
||||
|
|
15
.readthedocs.yml
Normal file
15
.readthedocs.yml
Normal file
|
@ -0,0 +1,15 @@
|
|||
# .readthedocs.yml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation with MkDocs
|
||||
mkdocs:
|
||||
configuration: mkdocs.yml
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
# https://github.com/rtfd/readthedocs.org/issues/5250
|
||||
python:
|
||||
version: 3.5
|
|
@ -3,6 +3,8 @@ dist: trusty
|
|||
|
||||
matrix:
|
||||
include:
|
||||
- language: php
|
||||
php: 7.3
|
||||
- language: php
|
||||
php: 7.2
|
||||
- language: php
|
||||
|
|
10
AUTHORS
10
AUTHORS
|
@ -1,9 +1,10 @@
|
|||
716 ArthurHoaro <arthur@hoa.ro>
|
||||
372 VirtualTam <virtualtam@flibidi.net>
|
||||
208 nodiscc <nodiscc@gmail.com>
|
||||
769 ArthurHoaro <arthur@hoa.ro>
|
||||
401 VirtualTam <virtualtam@flibidi.net>
|
||||
216 nodiscc <nodiscc@gmail.com>
|
||||
56 Sébastien Sauvage <sebsauvage@sebsauvage.net>
|
||||
15 Florian Eula <eula.florian@gmail.com>
|
||||
13 Emilien Klein <emilien@klein.st>
|
||||
13 Luce Carević <lcarevic@access42.net>
|
||||
12 Nicolas Danelon <hi@nicolasmd.com.ar>
|
||||
9 Willi Eggeling <thewilli@gmail.com>
|
||||
8 Christophe HENRY <christophe.henry@sbgodin.fr>
|
||||
|
@ -15,6 +16,7 @@
|
|||
4 Alexandre Alapetite <alexandre@alapetite.fr>
|
||||
4 David Sferruzza <david.sferruzza@gmail.com>
|
||||
4 Immánuel Fodor <immanuelfactor+github@gmail.com>
|
||||
3 Agurato <mail.vmonot@gmail.com>
|
||||
3 Teromene <teromene@teromene.fr>
|
||||
2 Alexandre G.-Raymond <alex@ndre.gr>
|
||||
2 Chris Kuethe <chris.kuethe@gmail.com>
|
||||
|
@ -29,6 +31,7 @@
|
|||
2 julienCXX <software@chmodplusx.eu>
|
||||
2 philipp-r <philipp-r@users.noreply.github.com>
|
||||
2 pips <pips@e5150.fr>
|
||||
2 trailjeep <trailjeep@gmail.com>
|
||||
1 Adrien Oliva <adrien.oliva@yapbreak.fr>
|
||||
1 Adrien le Maire <adrien@alemaire.be>
|
||||
1 Alexis J <alexis@effingo.be>
|
||||
|
@ -56,6 +59,7 @@
|
|||
1 Mark Gerarts <mark.gerarts@gmail.com>
|
||||
1 Marsup <marsup@gmail.com>
|
||||
1 Neros <contact@neros.fr>
|
||||
1 Rajat Hans <rajathans9@gmail.com>
|
||||
1 Sbgodin <Sbgodin@users.noreply.github.com>
|
||||
1 TsT <tst2005@gmail.com>
|
||||
1 dimtion <zizou.xena@gmail.com>
|
||||
|
|
84
CHANGELOG.md
84
CHANGELOG.md
|
@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
<<<<<<< HEAD
|
||||
## [v0.10.4](https://github.com/shaarli/Shaarli/releases/tag/v0.10.4) - 2019-04-16
|
||||
|
||||
### Fixed
|
||||
|
@ -39,6 +40,89 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||
### Removed
|
||||
- Remove Firefox Share documentation
|
||||
|
||||
||||||| merged common ancestors
|
||||
=======
|
||||
## [v0.11.0](https://github.com/shaarli/Shaarli/releases/tag/v0.11.0) - 2019-07-27
|
||||
|
||||
**Shaarli no longer officially support PHP 5.6 and PHP 7.0 as they've reached end of life.**
|
||||
|
||||
**Shaarli classes now use namespace, third party plugins need to update.**
|
||||
|
||||
### Added
|
||||
- Add optional PHP extension to composer suggestions.
|
||||
- composer: enforce PHP security advisories
|
||||
- phpDocumentor configuration and make target
|
||||
- Run unit tests against PHP 7.3
|
||||
- Bunch of accessibility improvements to the default template, thanks to @llune
|
||||
- Bulk actions: set visibility
|
||||
- Display sticky label in linklist
|
||||
- Add print CSS rules to the default template
|
||||
- New setting to automatically retrieve description for new bookmarks
|
||||
- Plugin to override default template colors
|
||||
|
||||
### Changed
|
||||
- Shaarli now uses namespaces for its classes.
|
||||
- Rewrite IP ban management
|
||||
- Default template: slightly lighten visited link color
|
||||
- Hide select all button on mobile view
|
||||
- Switch from FontAwesome v4.x to ForkAwesome
|
||||
- Daily - display the current day instead of the previous one
|
||||
|
||||
### Fixed
|
||||
- Do not check the IP address with session protection disabled
|
||||
- API: update test regexes to comply with PCRE2
|
||||
- Optimize and cleanup imports
|
||||
- ensure HTML tags are stripped from OpenGraph description
|
||||
- Documentation invalid links
|
||||
- Thumbnails disabling if PHP GD is not installed
|
||||
- Warning if links sticky status isn't set
|
||||
- Fix button overlapping on mobile in linklist
|
||||
- Do not try to retrieve thumbnails for internal link
|
||||
- Update node-sass to fix a vulnerability in node tar dependency
|
||||
- armhf Dockerfile
|
||||
- Default template: Responsive issue with delete button fix
|
||||
- Persist sticky status on bookmark update
|
||||
|
||||
### Removed
|
||||
- Doxygen configuration
|
||||
- redirector setting
|
||||
- QRCode link to an external service
|
||||
|
||||
## [v0.10.4](https://github.com/shaarli/Shaarli/releases/tag/v0.10.4) - 2019-04-16
|
||||
### Fixed
|
||||
- Fix thumbnails disabling if PHP GD is not installed
|
||||
- Fix a warning if links sticky status isn't set
|
||||
|
||||
## [v0.10.3](https://github.com/shaarli/Shaarli/releases/tag/v0.10.3) - 2019-02-23
|
||||
### Added
|
||||
- Add OpenGraph metadata tags on permalink page
|
||||
- Add CORS headers to REST API reponses
|
||||
- Add a button to toggle checkboxes of displayed links
|
||||
- Add an icon to the link list when the Isso plugin is enabled
|
||||
- Add noindex, nofollow to documentation pages
|
||||
- Document usage of robots.txt
|
||||
- Add a button to set links as sticky
|
||||
|
||||
### Changed
|
||||
- Update French translation
|
||||
- Refactor the documentation homepage
|
||||
- Bump netscape-bookmark-parser
|
||||
- Update session_start condition
|
||||
- Improve accessibility
|
||||
- Cleanup and refactor lint tooling
|
||||
|
||||
### Fixed
|
||||
- Fix input size for dropdown search form
|
||||
- Fix history for bulk link deletion
|
||||
- Fix thumbnail requests
|
||||
- Fix hashtag rendering when markdown escaping is enabled
|
||||
- Fix AJAX tag deletion
|
||||
- Fix lint errors and improve PSR-1 and PSR-2 compliance
|
||||
|
||||
### Removed
|
||||
- Remove Firefox Share documentation
|
||||
|
||||
>>>>>>> v0.11.0
|
||||
## [v0.10.2](https://github.com/shaarli/Shaarli/releases/tag/v0.10.2) - 2018-08-11
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -1,4 +1,34 @@
|
|||
FROM lsiobase/alpine.armhf:3.6
|
||||
# Stage 1:
|
||||
# - Copy Shaarli sources
|
||||
# - Build documentation
|
||||
FROM arm32v6/alpine:3.8 as docs
|
||||
ADD . /usr/src/app/shaarli
|
||||
RUN apk --update --no-cache add py2-pip \
|
||||
&& cd /usr/src/app/shaarli \
|
||||
&& pip install --no-cache-dir mkdocs \
|
||||
&& mkdocs build --clean
|
||||
|
||||
# Stage 2:
|
||||
# - Resolve PHP dependencies with Composer
|
||||
FROM arm32v6/alpine:3.8 as composer
|
||||
COPY --from=docs /usr/src/app/shaarli /app/shaarli
|
||||
RUN apk --update --no-cache add php7-curl php7-mbstring composer \
|
||||
&& cd /app/shaarli \
|
||||
&& composer --prefer-dist --no-dev install
|
||||
|
||||
# Stage 3:
|
||||
# - Frontend dependencies
|
||||
FROM arm32v6/alpine:3.8 as node
|
||||
COPY --from=composer /app/shaarli /shaarli
|
||||
RUN apk --update --no-cache add yarn nodejs-current python2 build-base \
|
||||
&& cd /shaarli \
|
||||
&& yarn install \
|
||||
&& yarn run build \
|
||||
&& rm -rf node_modules
|
||||
|
||||
# Stage 4:
|
||||
# - Shaarli image
|
||||
FROM arm32v6/alpine:3.8
|
||||
LABEL maintainer="Shaarli Community"
|
||||
MAINTAINER Shaarli Community
|
||||
|
||||
|
|
7
Makefile
7
Makefile
|
@ -146,10 +146,9 @@ authors:
|
|||
@git shortlog -sne > AUTHORS
|
||||
@rm .mailmap
|
||||
|
||||
### generate Doxygen documentation
|
||||
doxygen: clean
|
||||
@rm -rf doxygen
|
||||
@doxygen Doxyfile
|
||||
### generate phpDocumentor documentation
|
||||
phpdoc: clean
|
||||
@docker run --rm -v $(PWD):/data -u `id -u`:`id -g` phpdoc/phpdoc
|
||||
|
||||
### generate HTML documentation from Markdown pages with MkDocs
|
||||
htmldoc:
|
||||
|
|
|
@ -9,10 +9,10 @@ _It is designed to be personal (single-user), fast and handy._
|
|||
[![](https://img.shields.io/badge/stable-v0.9.7-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.7)
|
||||
[![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli)
|
||||
•
|
||||
[![](https://img.shields.io/badge/latest-v0.10.2-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.10.2)
|
||||
[![](https://img.shields.io/badge/latest-v0.10.4-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.10.4)
|
||||
[![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli)
|
||||
•
|
||||
[![](https://img.shields.io/badge/master-v0.10.x-blue.svg)](https://github.com/shaarli/Shaarli)
|
||||
[![](https://img.shields.io/badge/master-v0.11.x-blue.svg)](https://github.com/shaarli/Shaarli)
|
||||
[![](https://img.shields.io/travis/shaarli/Shaarli.svg?label=master)](https://travis-ci.org/shaarli/Shaarli)
|
||||
|
||||
[![Join the chat at https://gitter.im/shaarli/Shaarli](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/shaarli/Shaarli)
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<?php
|
||||
namespace Shaarli;
|
||||
|
||||
use Exception;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
/**
|
||||
* Shaarli (application) utilities
|
||||
*/
|
||||
|
@ -51,7 +56,7 @@ public static function getVersion($remote, $timeout = 2)
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
if (! is_file($remote)) {
|
||||
if (!is_file($remote)) {
|
||||
return false;
|
||||
}
|
||||
$data = file_get_contents($remote);
|
||||
|
@ -97,7 +102,7 @@ public static function checkUpdate(
|
|||
// Do not check versions for visitors
|
||||
// Do not check if the user doesn't want to
|
||||
// Do not check with dev version
|
||||
if (! $isLoggedIn || empty($enableCheck) || $currentVersion === 'dev') {
|
||||
if (!$isLoggedIn || empty($enableCheck) || $currentVersion === 'dev') {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -111,7 +116,7 @@ public static function checkUpdate(
|
|||
return false;
|
||||
}
|
||||
|
||||
if (! in_array($branch, self::$GIT_BRANCHES)) {
|
||||
if (!in_array($branch, self::$GIT_BRANCHES)) {
|
||||
throw new Exception(
|
||||
'Invalid branch selected for updates: "' . $branch . '"'
|
||||
);
|
||||
|
@ -123,7 +128,7 @@ public static function checkUpdate(
|
|||
self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE
|
||||
);
|
||||
|
||||
if (! $latestVersion) {
|
||||
if (!$latestVersion) {
|
||||
// Only update the file's modification date
|
||||
file_put_contents($updateFile, $currentVersion);
|
||||
return false;
|
||||
|
@ -178,10 +183,10 @@ public static function checkResourcePermissions($conf)
|
|||
'inc',
|
||||
'plugins',
|
||||
$rainTplDir,
|
||||
$rainTplDir.'/'.$conf->get('resource.theme'),
|
||||
$rainTplDir . '/' . $conf->get('resource.theme'),
|
||||
) as $path) {
|
||||
if (! is_readable(realpath($path))) {
|
||||
$errors[] = '"'.$path.'" '. t('directory is not readable');
|
||||
if (!is_readable(realpath($path))) {
|
||||
$errors[] = '"' . $path . '" ' . t('directory is not readable');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,11 +197,11 @@ public static function checkResourcePermissions($conf)
|
|||
$conf->get('resource.page_cache'),
|
||||
$conf->get('resource.raintpl_tmp'),
|
||||
) as $path) {
|
||||
if (! is_readable(realpath($path))) {
|
||||
$errors[] = '"'.$path.'" '. t('directory is not readable');
|
||||
if (!is_readable(realpath($path))) {
|
||||
$errors[] = '"' . $path . '" ' . t('directory is not readable');
|
||||
}
|
||||
if (! is_writable(realpath($path))) {
|
||||
$errors[] = '"'.$path.'" '. t('directory is not writable');
|
||||
if (!is_writable(realpath($path))) {
|
||||
$errors[] = '"' . $path . '" ' . t('directory is not writable');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,16 +213,16 @@ public static function checkResourcePermissions($conf)
|
|||
$conf->get('resource.log'),
|
||||
$conf->get('resource.update_check'),
|
||||
) as $path) {
|
||||
if (! is_file(realpath($path))) {
|
||||
if (!is_file(realpath($path))) {
|
||||
# the file may not exist yet
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_readable(realpath($path))) {
|
||||
$errors[] = '"'.$path.'" '. t('file is not readable');
|
||||
if (!is_readable(realpath($path))) {
|
||||
$errors[] = '"' . $path . '" ' . t('file is not readable');
|
||||
}
|
||||
if (! is_writable(realpath($path))) {
|
||||
$errors[] = '"'.$path.'" '. t('file is not writable');
|
||||
if (!is_writable(realpath($path))) {
|
||||
$errors[] = '"' . $path . '" ' . t('file is not writable');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
|
||||
require_once 'exceptions/IOException.php';
|
||||
namespace Shaarli;
|
||||
|
||||
use Shaarli\Exceptions\IOException;
|
||||
|
||||
/**
|
||||
* Class FileUtils
|
||||
|
@ -44,7 +46,7 @@ public static function writeFlatDB($file, $content)
|
|||
|
||||
return file_put_contents(
|
||||
$file,
|
||||
self::$phpPrefix.base64_encode(gzdeflate(serialize($content))).self::$phpSuffix
|
||||
self::$phpPrefix . base64_encode(gzdeflate(serialize($content))) . self::$phpSuffix
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -62,7 +64,7 @@ public static function readFlatDB($file, $default = null)
|
|||
{
|
||||
// Note that gzinflate is faster than gzuncompress.
|
||||
// See: http://www.php.net/manual/en/function.gzdeflate.php#96439
|
||||
if (! is_readable($file)) {
|
||||
if (!is_readable($file)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
<?php
|
||||
namespace Shaarli;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class History
|
||||
|
@ -66,7 +70,7 @@ class History
|
|||
* History constructor.
|
||||
*
|
||||
* @param string $historyFilePath History file path.
|
||||
* @param int $retentionTime History content rentention time in seconds.
|
||||
* @param int $retentionTime History content retention time in seconds.
|
||||
*
|
||||
* @throws Exception if something goes wrong.
|
||||
*/
|
||||
|
@ -166,11 +170,11 @@ protected function addEvent($status, $id = null)
|
|||
*/
|
||||
protected function check()
|
||||
{
|
||||
if (! is_file($this->historyFilePath)) {
|
||||
if (!is_file($this->historyFilePath)) {
|
||||
FileUtils::writeFlatDB($this->historyFilePath, []);
|
||||
}
|
||||
|
||||
if (! is_writable($this->historyFilePath)) {
|
||||
if (!is_writable($this->historyFilePath)) {
|
||||
throw new Exception(t('History file isn\'t readable or writable'));
|
||||
}
|
||||
}
|
||||
|
@ -191,7 +195,7 @@ protected function read()
|
|||
*/
|
||||
protected function write()
|
||||
{
|
||||
$comparaison = new DateTime('-'. $this->retentionTime . ' seconds');
|
||||
$comparaison = new DateTime('-' . $this->retentionTime . ' seconds');
|
||||
foreach ($this->history as $key => $value) {
|
||||
if ($value['datetime'] < $comparaison) {
|
||||
unset($this->history[$key]);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
namespace Shaarli;
|
||||
|
||||
use Gettext\GettextTranslator;
|
||||
use Gettext\Merge;
|
||||
use Gettext\Translations;
|
||||
use Gettext\Translator;
|
||||
use Gettext\TranslatorInterface;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
namespace Shaarli;
|
||||
|
||||
/**
|
||||
* Class Router
|
||||
|
@ -37,6 +38,8 @@ class Router
|
|||
|
||||
public static $PAGE_DELETELINK = 'delete_link';
|
||||
|
||||
public static $PAGE_CHANGE_VISIBILITY = 'change_visibility';
|
||||
|
||||
public static $PAGE_PINLINK = 'pin';
|
||||
|
||||
public static $PAGE_EXPORT = 'export';
|
||||
|
@ -75,43 +78,43 @@ public static function findPage($query, $get, $loggedIn)
|
|||
return self::$PAGE_LINKLIST;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_LOGIN) && $loggedIn === false) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_LOGIN) && $loggedIn === false) {
|
||||
return self::$PAGE_LOGIN;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_PICWALL)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_PICWALL)) {
|
||||
return self::$PAGE_PICWALL;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_TAGCLOUD)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_TAGCLOUD)) {
|
||||
return self::$PAGE_TAGCLOUD;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_TAGLIST)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_TAGLIST)) {
|
||||
return self::$PAGE_TAGLIST;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_OPENSEARCH)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_OPENSEARCH)) {
|
||||
return self::$PAGE_OPENSEARCH;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_DAILY)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_DAILY)) {
|
||||
return self::$PAGE_DAILY;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_FEED_ATOM)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_FEED_ATOM)) {
|
||||
return self::$PAGE_FEED_ATOM;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_FEED_RSS)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_FEED_RSS)) {
|
||||
return self::$PAGE_FEED_RSS;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_THUMBS_UPDATE)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_THUMBS_UPDATE)) {
|
||||
return self::$PAGE_THUMBS_UPDATE;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$AJAX_THUMB_UPDATE)) {
|
||||
if (startsWith($query, 'do=' . self::$AJAX_THUMB_UPDATE)) {
|
||||
return self::$AJAX_THUMB_UPDATE;
|
||||
}
|
||||
|
||||
|
@ -120,23 +123,23 @@ public static function findPage($query, $get, $loggedIn)
|
|||
return self::$PAGE_LINKLIST;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_TOOLS)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_TOOLS)) {
|
||||
return self::$PAGE_TOOLS;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_CHANGEPASSWORD)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_CHANGEPASSWORD)) {
|
||||
return self::$PAGE_CHANGEPASSWORD;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_CONFIGURE)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_CONFIGURE)) {
|
||||
return self::$PAGE_CONFIGURE;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_CHANGETAG)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_CHANGETAG)) {
|
||||
return self::$PAGE_CHANGETAG;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_ADDLINK)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_ADDLINK)) {
|
||||
return self::$PAGE_ADDLINK;
|
||||
}
|
||||
|
||||
|
@ -148,27 +151,31 @@ public static function findPage($query, $get, $loggedIn)
|
|||
return self::$PAGE_DELETELINK;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_PINLINK)) {
|
||||
if (isset($get[self::$PAGE_CHANGE_VISIBILITY])) {
|
||||
return self::$PAGE_CHANGE_VISIBILITY;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do=' . self::$PAGE_PINLINK)) {
|
||||
return self::$PAGE_PINLINK;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_EXPORT)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_EXPORT)) {
|
||||
return self::$PAGE_EXPORT;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_IMPORT)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_IMPORT)) {
|
||||
return self::$PAGE_IMPORT;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_PLUGINSADMIN)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_PLUGINSADMIN)) {
|
||||
return self::$PAGE_PLUGINSADMIN;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_SAVE_PLUGINSADMIN)) {
|
||||
if (startsWith($query, 'do=' . self::$PAGE_SAVE_PLUGINSADMIN)) {
|
||||
return self::$PAGE_SAVE_PLUGINSADMIN;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$GET_TOKEN)) {
|
||||
if (startsWith($query, 'do=' . self::$GET_TOKEN)) {
|
||||
return self::$GET_TOKEN;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
namespace Shaarli;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use WebThumbnailer\Application\ConfigManager as WTConfigManager;
|
||||
use WebThumbnailer\Exception\WebThumbnailerException;
|
||||
use WebThumbnailer\WebThumbnailer;
|
||||
use WebThumbnailer\Application\ConfigManager as WTConfigManager;
|
||||
|
||||
/**
|
||||
* Class Thumbnailer
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<?php
|
||||
namespace Shaarli\Api;
|
||||
|
||||
use Shaarli\Api\Exceptions\ApiException;
|
||||
use Shaarli\Api\Exceptions\ApiAuthorizationException;
|
||||
|
||||
use Shaarli\Api\Exceptions\ApiException;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Slim\Container;
|
||||
use Slim\Http\Request;
|
||||
|
@ -127,12 +126,10 @@ protected function checkToken($request)
|
|||
*/
|
||||
protected function setLinkDb($conf)
|
||||
{
|
||||
$linkDb = new \LinkDB(
|
||||
$linkDb = new \Shaarli\Bookmark\LinkDB(
|
||||
$conf->get('resource.datastore'),
|
||||
true,
|
||||
$conf->get('privacy.hide_public_links'),
|
||||
$conf->get('redirector.url'),
|
||||
$conf->get('redirector.encode_url')
|
||||
$conf->get('privacy.hide_public_links')
|
||||
);
|
||||
$this->container['db'] = $linkDb;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
namespace Shaarli\Api;
|
||||
|
||||
use Shaarli\Base64Url;
|
||||
use Shaarli\Api\Exceptions\ApiAuthorizationException;
|
||||
use Shaarli\Http\Base64Url;
|
||||
|
||||
/**
|
||||
* REST API utilities
|
||||
|
@ -59,7 +59,7 @@ public static function formatLink($link, $indexUrl)
|
|||
{
|
||||
$out['id'] = $link['id'];
|
||||
// Not an internal link
|
||||
if ($link['url'][0] != '?') {
|
||||
if (! is_note($link['url'])) {
|
||||
$out['url'] = $link['url'];
|
||||
} else {
|
||||
$out['url'] = $indexUrl . $link['url'];
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Bookmark\LinkDB;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use \Slim\Container;
|
||||
use Slim\Container;
|
||||
|
||||
/**
|
||||
* Abstract Class ApiController
|
||||
|
@ -25,12 +26,12 @@ abstract class ApiController
|
|||
protected $conf;
|
||||
|
||||
/**
|
||||
* @var \LinkDB
|
||||
* @var LinkDB
|
||||
*/
|
||||
protected $linkDb;
|
||||
|
||||
/**
|
||||
* @var \History
|
||||
* @var HistoryController
|
||||
*/
|
||||
protected $history;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
*
|
||||
* @package Shaarli\Api\Controllers
|
||||
*/
|
||||
class History extends ApiController
|
||||
class HistoryController extends ApiController
|
||||
{
|
||||
/**
|
||||
* Service providing operation regarding Shaarli datastore and settings.
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
use Shaarli\Api\ApiUtils;
|
||||
use Shaarli\Api\Exceptions\ApiBadParametersException;
|
||||
use Shaarli\Api\Exceptions\ApiLinkNotFoundException;
|
||||
use Shaarli\Api\Exceptions\ApiTagNotFoundException;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Shaarli\Api\Exceptions;
|
||||
|
||||
use Slim\Http\Response;
|
||||
|
||||
/**
|
||||
* Class ApiLinkNotFoundException
|
||||
*
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Shaarli\Api\Exceptions;
|
||||
|
||||
use Slim\Http\Response;
|
||||
|
||||
/**
|
||||
* Class ApiTagNotFoundException
|
||||
*
|
||||
|
|
|
@ -1,4 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Bookmark;
|
||||
|
||||
use ArrayAccess;
|
||||
use Countable;
|
||||
use DateTime;
|
||||
use Iterator;
|
||||
use Shaarli\Bookmark\Exception\LinkNotFoundException;
|
||||
use Shaarli\Exceptions\IOException;
|
||||
use Shaarli\FileUtils;
|
||||
|
||||
/**
|
||||
* Data storage for links.
|
||||
*
|
||||
|
@ -18,10 +29,10 @@
|
|||
* - private: Is this link private? 0=no, other value=yes
|
||||
* - tags: tags attached to this entry (separated by spaces)
|
||||
* - title Title of the link
|
||||
* - url URL of the link. Used for displayable links (no redirector, relative, etc.).
|
||||
* Can be absolute or relative.
|
||||
* Relative URLs are permalinks (e.g.'?m-ukcw')
|
||||
* - real_url Absolute processed URL.
|
||||
* - url URL of the link. Used for displayable links.
|
||||
* Can be absolute or relative in the database but the relative links
|
||||
* will be converted to absolute ones in templates.
|
||||
* - real_url Raw URL in stored in the DB (absolute or relative).
|
||||
* - shorturl Permalink smallhash
|
||||
*
|
||||
* Implements 3 interfaces:
|
||||
|
@ -77,19 +88,6 @@ class LinkDB implements Iterator, Countable, ArrayAccess
|
|||
// Hide public links
|
||||
private $hidePublicLinks;
|
||||
|
||||
// link redirector set in user settings.
|
||||
private $redirector;
|
||||
|
||||
/**
|
||||
* Set this to `true` to urlencode link behind redirector link, `false` to leave it untouched.
|
||||
*
|
||||
* Example:
|
||||
* anonym.to needs clean URL while dereferer.org needs urlencoded URL.
|
||||
*
|
||||
* @var boolean $redirectorEncode parameter: true or false
|
||||
*/
|
||||
private $redirectorEncode;
|
||||
|
||||
/**
|
||||
* Creates a new LinkDB
|
||||
*
|
||||
|
@ -98,21 +96,16 @@ class LinkDB implements Iterator, Countable, ArrayAccess
|
|||
* @param string $datastore datastore file path.
|
||||
* @param boolean $isLoggedIn is the user logged in?
|
||||
* @param boolean $hidePublicLinks if true all links are private.
|
||||
* @param string $redirector link redirector set in user settings.
|
||||
* @param boolean $redirectorEncode Enable urlencode on redirected urls (default: true).
|
||||
*/
|
||||
public function __construct(
|
||||
$datastore,
|
||||
$isLoggedIn,
|
||||
$hidePublicLinks,
|
||||
$redirector = '',
|
||||
$redirectorEncode = true
|
||||
$hidePublicLinks
|
||||
) {
|
||||
|
||||
$this->datastore = $datastore;
|
||||
$this->loggedIn = $isLoggedIn;
|
||||
$this->hidePublicLinks = $hidePublicLinks;
|
||||
$this->redirector = $redirector;
|
||||
$this->redirectorEncode = $redirectorEncode === true;
|
||||
$this->check();
|
||||
$this->read();
|
||||
}
|
||||
|
@ -137,7 +130,7 @@ public function offsetSet($offset, $value)
|
|||
if (!isset($value['id']) || empty($value['url'])) {
|
||||
die(t('Internal Error: A link should always have an id and URL.'));
|
||||
}
|
||||
if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) {
|
||||
if (($offset !== null && !is_int($offset)) || !is_int($value['id'])) {
|
||||
die(t('You must specify an integer as a key.'));
|
||||
}
|
||||
if ($offset !== null && $offset !== $value['id']) {
|
||||
|
@ -247,19 +240,19 @@ private function check()
|
|||
$this->links = array();
|
||||
$link = array(
|
||||
'id' => 1,
|
||||
'title'=> t('The personal, minimalist, super-fast, database free, bookmarking service'),
|
||||
'url'=>'https://shaarli.readthedocs.io',
|
||||
'description'=>t(
|
||||
'title' => t('The personal, minimalist, super-fast, database free, bookmarking service'),
|
||||
'url' => 'https://shaarli.readthedocs.io',
|
||||
'description' => t(
|
||||
'Welcome to Shaarli! This is your first public bookmark. '
|
||||
.'To edit or delete me, you must first login.
|
||||
. 'To edit or delete me, you must first login.
|
||||
|
||||
To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page.
|
||||
|
||||
You use the community supported version of the original Shaarli project, by Sebastien Sauvage.'
|
||||
),
|
||||
'private'=>0,
|
||||
'created'=> new DateTime(),
|
||||
'tags'=>'opensource software',
|
||||
'private' => 0,
|
||||
'created' => new DateTime(),
|
||||
'tags' => 'opensource software',
|
||||
'sticky' => false,
|
||||
);
|
||||
$link['shorturl'] = link_small_hash($link['created'], $link['id']);
|
||||
|
@ -267,12 +260,12 @@ private function check()
|
|||
|
||||
$link = array(
|
||||
'id' => 0,
|
||||
'title'=> t('My secret stuff... - Pastebin.com'),
|
||||
'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=',
|
||||
'description'=> t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'),
|
||||
'private'=>1,
|
||||
'created'=> new DateTime('1 minute ago'),
|
||||
'tags'=>'secretstuff',
|
||||
'title' => t('My secret stuff... - Pastebin.com'),
|
||||
'url' => 'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=',
|
||||
'description' => t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'),
|
||||
'private' => 1,
|
||||
'created' => new DateTime('1 minute ago'),
|
||||
'tags' => 'secretstuff',
|
||||
'sticky' => false,
|
||||
);
|
||||
$link['shorturl'] = link_small_hash($link['created'], $link['id']);
|
||||
|
@ -299,7 +292,7 @@ private function read()
|
|||
|
||||
$toremove = array();
|
||||
foreach ($this->links as $key => &$link) {
|
||||
if (! $this->loggedIn && $link['private'] != 0) {
|
||||
if (!$this->loggedIn && $link['private'] != 0) {
|
||||
// Transition for not upgraded databases.
|
||||
unset($this->links[$key]);
|
||||
continue;
|
||||
|
@ -309,29 +302,21 @@ private function read()
|
|||
sanitizeLink($link);
|
||||
|
||||
// Remove private tags if the user is not logged in.
|
||||
if (! $this->loggedIn) {
|
||||
if (!$this->loggedIn) {
|
||||
$link['tags'] = preg_replace('/(^|\s+)\.[^($|\s)]+\s*/', ' ', $link['tags']);
|
||||
}
|
||||
|
||||
// Do not use the redirector for internal links (Shaarli note URL starting with a '?').
|
||||
if (!empty($this->redirector) && !startsWith($link['url'], '?')) {
|
||||
$link['real_url'] = $this->redirector;
|
||||
if ($this->redirectorEncode) {
|
||||
$link['real_url'] .= urlencode(unescape($link['url']));
|
||||
} else {
|
||||
$link['real_url'] .= $link['url'];
|
||||
}
|
||||
} else {
|
||||
$link['real_url'] = $link['url'];
|
||||
}
|
||||
|
||||
$link['sticky'] = isset($link['sticky']) ? $link['sticky'] : false;
|
||||
|
||||
$link['sticky'] = isset($link['sticky']) ? $link['sticky'] : false;
|
||||
|
||||
// To be able to load links before running the update, and prepare the update
|
||||
if (! isset($link['created'])) {
|
||||
if (!isset($link['created'])) {
|
||||
$link['id'] = $link['linkdate'];
|
||||
$link['created'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['linkdate']);
|
||||
if (! empty($link['updated'])) {
|
||||
if (!empty($link['updated'])) {
|
||||
$link['updated'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['updated']);
|
||||
}
|
||||
$link['shorturl'] = smallHash($link['linkdate']);
|
||||
|
@ -422,7 +407,7 @@ public function filterDay($request)
|
|||
* - searchterm: term search
|
||||
* @param bool $casesensitive Optional: Perform case sensitive filter
|
||||
* @param string $visibility return only all/private/public links
|
||||
* @param string $untaggedonly return only untagged links
|
||||
* @param bool $untaggedonly return only untagged links
|
||||
*
|
||||
* @return array filtered links, all links if no suitable filter was provided.
|
||||
*/
|
||||
|
@ -432,6 +417,7 @@ public function filterSearch(
|
|||
$visibility = 'all',
|
||||
$untaggedonly = false
|
||||
) {
|
||||
|
||||
// Filter link database according to parameters.
|
||||
$searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : '';
|
||||
$searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : '';
|
|
@ -1,5 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Bookmark;
|
||||
|
||||
use Exception;
|
||||
use Shaarli\Bookmark\Exception\LinkNotFoundException;
|
||||
|
||||
/**
|
||||
* Class LinkFilter.
|
||||
*
|
||||
|
@ -58,7 +63,7 @@ public function __construct($links)
|
|||
*/
|
||||
public function filter($type, $request, $casesensitive = false, $visibility = 'all', $untaggedonly = false)
|
||||
{
|
||||
if (! in_array($visibility, ['all', 'public', 'private'])) {
|
||||
if (!in_array($visibility, ['all', 'public', 'private'])) {
|
||||
$visibility = 'all';
|
||||
}
|
||||
|
||||
|
@ -117,7 +122,7 @@ private function noFilter($visibility = 'all')
|
|||
foreach ($this->links as $key => $value) {
|
||||
if ($value['private'] && $visibility === 'private') {
|
||||
$out[$key] = $value;
|
||||
} elseif (! $value['private'] && $visibility === 'public') {
|
||||
} elseif (!$value['private'] && $visibility === 'public') {
|
||||
$out[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +137,7 @@ private function noFilter($visibility = 'all')
|
|||
*
|
||||
* @return array $filtered array containing permalink data.
|
||||
*
|
||||
* @throws LinkNotFoundException if the smallhash doesn't match any link.
|
||||
* @throws \Shaarli\Bookmark\Exception\LinkNotFoundException if the smallhash doesn't match any link.
|
||||
*/
|
||||
private function filterSmallHash($smallHash)
|
||||
{
|
||||
|
@ -207,7 +212,7 @@ private function filterFulltext($searchterms, $visibility = 'all')
|
|||
foreach ($this->links as $id => $link) {
|
||||
// ignore non private links when 'privatonly' is on.
|
||||
if ($visibility !== 'all') {
|
||||
if (! $link['private'] && $visibility === 'private') {
|
||||
if (!$link['private'] && $visibility === 'private') {
|
||||
continue;
|
||||
} elseif ($link['private'] && $visibility === 'public') {
|
||||
continue;
|
||||
|
@ -250,7 +255,9 @@ private function filterFulltext($searchterms, $visibility = 'all')
|
|||
|
||||
/**
|
||||
* generate a regex fragment out of a tag
|
||||
*
|
||||
* @param string $tag to to generate regexs from. may start with '-' to negate, contain '*' as wildcard
|
||||
*
|
||||
* @return string generated regex fragment
|
||||
*/
|
||||
private static function tag2regex($tag)
|
||||
|
@ -334,7 +341,7 @@ public function filterTags($tags, $casesensitive = false, $visibility = 'all')
|
|||
// check level of visibility
|
||||
// ignore non private links when 'privateonly' is on.
|
||||
if ($visibility !== 'all') {
|
||||
if (! $link['private'] && $visibility === 'private') {
|
||||
if (!$link['private'] && $visibility === 'private') {
|
||||
continue;
|
||||
} elseif ($link['private'] && $visibility === 'public') {
|
||||
continue;
|
||||
|
@ -377,7 +384,7 @@ public function filterUntagged($visibility)
|
|||
$filtered = [];
|
||||
foreach ($this->links as $key => $link) {
|
||||
if ($visibility !== 'all') {
|
||||
if (! $link['private'] && $visibility === 'private') {
|
||||
if (!$link['private'] && $visibility === 'private') {
|
||||
continue;
|
||||
} elseif ($link['private'] && $visibility === 'public') {
|
||||
continue;
|
||||
|
@ -406,7 +413,7 @@ public function filterUntagged($visibility)
|
|||
*/
|
||||
public function filterDay($day)
|
||||
{
|
||||
if (! checkDateFormat('Ymd', $day)) {
|
||||
if (!checkDateFormat('Ymd', $day)) {
|
||||
throw new Exception('Invalid date format');
|
||||
}
|
||||
|
||||
|
@ -440,14 +447,3 @@ public static function tagsStrToArray($tags, $casesensitive)
|
|||
return preg_split('/\s+/', $tagsOut, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
class LinkNotFoundException extends Exception
|
||||
{
|
||||
/**
|
||||
* LinkNotFoundException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->message = t('The link you are trying to reach does not exist or has been deleted.');
|
||||
}
|
||||
}
|
|
@ -1,17 +1,31 @@
|
|||
<?php
|
||||
|
||||
use Shaarli\Bookmark\LinkDB;
|
||||
|
||||
/**
|
||||
* Get cURL callback function for CURLOPT_WRITEFUNCTION
|
||||
*
|
||||
* @param string $charset to extract from the downloaded page (reference)
|
||||
* @param string $title to extract from the downloaded page (reference)
|
||||
* @param string $curlGetInfo Optionnaly overrides curl_getinfo function
|
||||
* @param string $description to extract from the downloaded page (reference)
|
||||
* @param string $keywords to extract from the downloaded page (reference)
|
||||
* @param bool $retrieveDescription Automatically tries to retrieve description and keywords from HTML content
|
||||
* @param string $curlGetInfo Optionally overrides curl_getinfo function
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_getinfo')
|
||||
{
|
||||
function get_curl_download_callback(
|
||||
&$charset,
|
||||
&$title,
|
||||
&$description,
|
||||
&$keywords,
|
||||
$retrieveDescription,
|
||||
$curlGetInfo = 'curl_getinfo'
|
||||
) {
|
||||
$isRedirected = false;
|
||||
$currentChunk = 0;
|
||||
$foundChunk = null;
|
||||
|
||||
/**
|
||||
* cURL callback function for CURLOPT_WRITEFUNCTION (called during the download).
|
||||
*
|
||||
|
@ -23,7 +37,18 @@ function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_get
|
|||
*
|
||||
* @return int|bool length of $data or false if we need to stop the download
|
||||
*/
|
||||
return function (&$ch, $data) use ($curlGetInfo, &$charset, &$title, &$isRedirected) {
|
||||
return function (&$ch, $data) use (
|
||||
$retrieveDescription,
|
||||
$curlGetInfo,
|
||||
&$charset,
|
||||
&$title,
|
||||
&$description,
|
||||
&$keywords,
|
||||
&$isRedirected,
|
||||
&$currentChunk,
|
||||
&$foundChunk
|
||||
) {
|
||||
$currentChunk++;
|
||||
$responseCode = $curlGetInfo($ch, CURLINFO_RESPONSE_CODE);
|
||||
if (!empty($responseCode) && in_array($responseCode, [301, 302])) {
|
||||
$isRedirected = true;
|
||||
|
@ -48,9 +73,34 @@ function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_get
|
|||
}
|
||||
if (empty($title)) {
|
||||
$title = html_extract_title($data);
|
||||
$foundChunk = ! empty($title) ? $currentChunk : $foundChunk;
|
||||
}
|
||||
if ($retrieveDescription && empty($description)) {
|
||||
$description = html_extract_tag('description', $data);
|
||||
$foundChunk = ! empty($description) ? $currentChunk : $foundChunk;
|
||||
}
|
||||
if ($retrieveDescription && empty($keywords)) {
|
||||
$keywords = html_extract_tag('keywords', $data);
|
||||
if (! empty($keywords)) {
|
||||
$foundChunk = $currentChunk;
|
||||
// Keywords use the format tag1, tag2 multiple words, tag
|
||||
// So we format them to match Shaarli's separator and glue multiple words with '-'
|
||||
$keywords = implode(' ', array_map(function($keyword) {
|
||||
return implode('-', preg_split('/\s+/', trim($keyword)));
|
||||
}, explode(',', $keywords)));
|
||||
}
|
||||
}
|
||||
|
||||
// We got everything we want, stop the download.
|
||||
if (!empty($responseCode) && !empty($contentType) && !empty($charset) && !empty($title)) {
|
||||
// If we already found either the title, description or keywords,
|
||||
// it's highly unlikely that we'll found the other metas further than
|
||||
// in the same chunk of data or the next one. So we also stop the download after that.
|
||||
if ((!empty($responseCode) && !empty($contentType) && !empty($charset)) && $foundChunk !== null
|
||||
&& (! $retrieveDescription
|
||||
|| $foundChunk < $currentChunk
|
||||
|| (!empty($title) && !empty($description) && !empty($keywords))
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -108,6 +158,35 @@ function html_extract_charset($html)
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract meta tag from HTML content in either:
|
||||
* - OpenGraph: <meta property="og:[tag]" ...>
|
||||
* - Meta tag: <meta name="[tag]" ...>
|
||||
*
|
||||
* @param string $tag Name of the tag to retrieve.
|
||||
* @param string $html HTML content where to look for charset.
|
||||
*
|
||||
* @return bool|string Charset string if found, false otherwise.
|
||||
*/
|
||||
function html_extract_tag($tag, $html)
|
||||
{
|
||||
$propertiesKey = ['property', 'name', 'itemprop'];
|
||||
$properties = implode('|', $propertiesKey);
|
||||
// Try to retrieve OpenGraph image.
|
||||
$ogRegex = '#<meta[^>]+(?:'. $properties .')=["\']?(?:og:)?'. $tag .'["\'\s][^>]*content=["\']?(.*?)["\'/>]#';
|
||||
// If the attributes are not in the order property => content (e.g. Github)
|
||||
// New regex to keep this readable... more or less.
|
||||
$ogRegexReverse = '#<meta[^>]+content=["\']([^"\']+)[^>]+(?:'. $properties .')=["\']?(?:og)?:'. $tag .'["\'\s/>]#';
|
||||
|
||||
if (preg_match($ogRegex, $html, $matches) > 0
|
||||
|| preg_match($ogRegexReverse, $html, $matches) > 0
|
||||
) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count private links in given linklist.
|
||||
*
|
||||
|
@ -131,29 +210,15 @@ function count_private($links)
|
|||
* In a string, converts URLs to clickable links.
|
||||
*
|
||||
* @param string $text input string.
|
||||
* @param string $redirector if a redirector is set, use it to gerenate links.
|
||||
* @param bool $urlEncode Use `urlencode()` on the URL after the redirector or not.
|
||||
*
|
||||
* @return string returns $text with all links converted to HTML links.
|
||||
*
|
||||
* @see Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722
|
||||
*/
|
||||
function text2clickable($text, $redirector = '', $urlEncode = true)
|
||||
function text2clickable($text)
|
||||
{
|
||||
$regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[a-z0-9\(\)]/?)!si';
|
||||
|
||||
if (empty($redirector)) {
|
||||
return preg_replace($regex, '<a href="$1">$1</a>', $text);
|
||||
}
|
||||
// Redirector is set, urlencode the final URL.
|
||||
return preg_replace_callback(
|
||||
$regex,
|
||||
function ($matches) use ($redirector, $urlEncode) {
|
||||
$url = $urlEncode ? urlencode($matches[1]) : $matches[1];
|
||||
return '<a href="' . $redirector . $url .'">'. $matches[1] .'</a>';
|
||||
},
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,15 +260,13 @@ function space2nbsp($text)
|
|||
* Format Shaarli's description
|
||||
*
|
||||
* @param string $description shaare's description.
|
||||
* @param string $redirector if a redirector is set, use it to gerenate links.
|
||||
* @param bool $urlEncode Use `urlencode()` on the URL after the redirector or not.
|
||||
* @param string $indexUrl URL to Shaarli's index.
|
||||
|
||||
* @return string formatted description.
|
||||
*/
|
||||
function format_description($description, $redirector = '', $urlEncode = true, $indexUrl = '')
|
||||
function format_description($description, $indexUrl = '')
|
||||
{
|
||||
return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector, $urlEncode), $indexUrl)));
|
||||
return nl2br(space2nbsp(hashtag_autolink(text2clickable($description), $indexUrl)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,3 +281,16 @@ function link_small_hash($date, $id)
|
|||
{
|
||||
return smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the link is an internal note.
|
||||
* Its URL starts by `?` because it's actually a permalink.
|
||||
*
|
||||
* @param string $linkUrl
|
||||
*
|
||||
* @return bool true if internal note, false otherwise.
|
||||
*/
|
||||
function is_note($linkUrl)
|
||||
{
|
||||
return isset($linkUrl[0]) && $linkUrl[0] === '?';
|
||||
}
|
15
application/bookmark/exception/LinkNotFoundException.php
Normal file
15
application/bookmark/exception/LinkNotFoundException.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
namespace Shaarli\Bookmark\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class LinkNotFoundException extends Exception
|
||||
{
|
||||
/**
|
||||
* LinkNotFoundException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->message = t('The link you are trying to reach does not exist or has been deleted.');
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ public function write($filepath, $conf)
|
|||
$print = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0;
|
||||
$data = self::getPhpHeaders() . json_encode($conf, $print) . self::getPhpSuffix();
|
||||
if (!file_put_contents($filepath, $data)) {
|
||||
throw new \IOException(
|
||||
throw new \Shaarli\Exceptions\IOException(
|
||||
$filepath,
|
||||
t('Shaarli could not create the config file. '.
|
||||
'Please make sure Shaarli has the right to write in the folder is it installed in.')
|
||||
|
|
|
@ -207,7 +207,7 @@ public function exists($setting)
|
|||
*
|
||||
* @throws MissingFieldConfigException: a mandatory field has not been provided in $conf.
|
||||
* @throws UnauthorizedConfigException: user is not authorize to change configuration.
|
||||
* @throws \IOException: an error occurred while writing the new config file.
|
||||
* @throws \Shaarli\Exceptions\IOException: an error occurred while writing the new config file.
|
||||
*/
|
||||
public function write($isLoggedIn)
|
||||
{
|
||||
|
@ -221,7 +221,6 @@ public function write($isLoggedIn)
|
|||
'general.title',
|
||||
'general.header_link',
|
||||
'privacy.default_private_links',
|
||||
'redirector.url',
|
||||
);
|
||||
|
||||
// Only logged in user can alter config.
|
||||
|
@ -366,6 +365,7 @@ protected function setDefaultValues()
|
|||
$this->setEmpty('general.links_per_page', 20);
|
||||
$this->setEmpty('general.enabled_plugins', self::$DEFAULT_PLUGINS);
|
||||
$this->setEmpty('general.default_note_title', 'Note: ');
|
||||
$this->setEmpty('general.retrieve_description', false);
|
||||
|
||||
$this->setEmpty('updates.check_updates', false);
|
||||
$this->setEmpty('updates.check_updates_branch', 'stable');
|
||||
|
@ -381,9 +381,6 @@ protected function setDefaultValues()
|
|||
// default state of the 'remember me' checkbox of the login form
|
||||
$this->setEmpty('privacy.remember_user_default', true);
|
||||
|
||||
$this->setEmpty('redirector.url', '');
|
||||
$this->setEmpty('redirector.encode_url', true);
|
||||
|
||||
$this->setEmpty('thumbnails.width', '125');
|
||||
$this->setEmpty('thumbnails.height', '90');
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class ConfigPhp implements ConfigIO
|
|||
/**
|
||||
* Map legacy config keys with the new ones.
|
||||
* If ConfigPhp is used, getting <newkey> will actually look for <legacykey>.
|
||||
* The Updater will use this array to transform keys when switching to JSON.
|
||||
* The updater will use this array to transform keys when switching to JSON.
|
||||
*
|
||||
* @var array current key => legacy key.
|
||||
*/
|
||||
|
@ -124,7 +124,7 @@ public function write($filepath, $conf)
|
|||
if (!file_put_contents($filepath, $configStr)
|
||||
|| strcmp(file_get_contents($filepath), $configStr) != 0
|
||||
) {
|
||||
throw new \IOException(
|
||||
throw new \Shaarli\Exceptions\IOException(
|
||||
$filepath,
|
||||
t('Shaarli could not create the config file. '.
|
||||
'Please make sure Shaarli has the right to write in the folder is it installed in.')
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
namespace Shaarli\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Exception class thrown when a filesystem access failure happens
|
||||
|
@ -17,6 +20,6 @@ public function __construct($path, $message = '')
|
|||
{
|
||||
$this->path = $path;
|
||||
$this->message = empty($message) ? t('Error accessing') : $message;
|
||||
$this->message .= ' "' . $this->path .'"';
|
||||
$this->message .= ' "' . $this->path . '"';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Feed;
|
||||
|
||||
/**
|
||||
* Simple cache system, mainly for the RSS/ATOM feeds
|
||||
*/
|
||||
|
@ -24,7 +27,7 @@ public function __construct($cacheDir, $url, $shouldBeCached)
|
|||
{
|
||||
// TODO: check write access to the cache directory
|
||||
$this->cacheDir = $cacheDir;
|
||||
$this->filename = $this->cacheDir.'/'.sha1($url).'.cache';
|
||||
$this->filename = $this->cacheDir . '/' . sha1($url) . '.cache';
|
||||
$this->shouldBeCached = $shouldBeCached;
|
||||
}
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
namespace Shaarli\Feed;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* FeedBuilder class.
|
||||
|
@ -28,7 +31,7 @@ class FeedBuilder
|
|||
public static $DEFAULT_NB_LINKS = 50;
|
||||
|
||||
/**
|
||||
* @var LinkDB instance.
|
||||
* @var \Shaarli\Bookmark\LinkDB instance.
|
||||
*/
|
||||
protected $linkDB;
|
||||
|
||||
|
@ -38,12 +41,12 @@ class FeedBuilder
|
|||
protected $feedType;
|
||||
|
||||
/**
|
||||
* @var array $_SERVER.
|
||||
* @var array $_SERVER
|
||||
*/
|
||||
protected $serverInfo;
|
||||
|
||||
/**
|
||||
* @var array $_GET.
|
||||
* @var array $_GET
|
||||
*/
|
||||
protected $userInput;
|
||||
|
||||
|
@ -75,11 +78,12 @@ class FeedBuilder
|
|||
/**
|
||||
* Feed constructor.
|
||||
*
|
||||
* @param LinkDB $linkDB LinkDB instance.
|
||||
* @param \Shaarli\Bookmark\LinkDB $linkDB LinkDB instance.
|
||||
* @param string $feedType Type of feed.
|
||||
* @param array $serverInfo $_SERVER.
|
||||
* @param array $userInput $_GET.
|
||||
* @param boolean $isLoggedIn True if the user is currently logged in, false otherwise.
|
||||
* @param boolean $isLoggedIn True if the user is currently logged in,
|
||||
* false otherwise.
|
||||
*/
|
||||
public function __construct($linkDB, $feedType, $serverInfo, $userInput, $isLoggedIn)
|
||||
{
|
||||
|
@ -142,18 +146,19 @@ public function buildData()
|
|||
*/
|
||||
protected function buildItem($link, $pageaddr)
|
||||
{
|
||||
$link['guid'] = $pageaddr .'?'. $link['shorturl'];
|
||||
// Check for both signs of a note: starting with ? and 7 chars long.
|
||||
if ($link['url'][0] === '?' && strlen($link['url']) === 7) {
|
||||
$link['guid'] = $pageaddr . '?' . $link['shorturl'];
|
||||
// Prepend the root URL for notes
|
||||
if (is_note($link['url'])) {
|
||||
$link['url'] = $pageaddr . $link['url'];
|
||||
}
|
||||
if ($this->usePermalinks === true) {
|
||||
$permalink = '<a href="'. $link['url'] .'" title="'. t('Direct link') .'">'. t('Direct link') .'</a>';
|
||||
$permalink = '<a href="' . $link['url'] . '" title="' . t('Direct link') . '">' . t('Direct link') . '</a>';
|
||||
} else {
|
||||
$permalink = '<a href="'. $link['guid'] .'" title="'. t('Permalink') .'">'. t('Permalink') .'</a>';
|
||||
$permalink = '<a href="' . $link['guid'] . '" title="' . t('Permalink') . '">' . t('Permalink') . '</a>';
|
||||
}
|
||||
$link['description'] = format_description($link['description'], '', false, $pageaddr);
|
||||
$link['permalink'] = PHP_EOL .'<br> — '. $permalink;
|
||||
$link['description'] = format_description($link['description'], $pageaddr);
|
||||
$link['description'] .= PHP_EOL . '<br>— ' . $permalink;
|
||||
|
||||
$pubDate = $link['created'];
|
||||
$link['pub_iso_date'] = $this->getIsoDate($pubDate);
|
||||
|
||||
|
@ -163,7 +168,6 @@ protected function buildItem($link, $pageaddr)
|
|||
$link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM);
|
||||
} else {
|
||||
$link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);
|
||||
;
|
||||
}
|
||||
|
||||
// Save the more recent item.
|
||||
|
@ -222,11 +226,11 @@ public function setLocale($locale)
|
|||
public function getTypeLanguage()
|
||||
{
|
||||
// Use the locale do define the language, if available.
|
||||
if (! empty($this->locale) && preg_match('/^\w{2}[_\-]\w{2}/', $this->locale)) {
|
||||
$length = ($this->feedType == self::$FEED_RSS) ? 5 : 2;
|
||||
if (!empty($this->locale) && preg_match('/^\w{2}[_\-]\w{2}/', $this->locale)) {
|
||||
$length = ($this->feedType === self::$FEED_RSS) ? 5 : 2;
|
||||
return str_replace('_', '-', substr($this->locale, 0, $length));
|
||||
}
|
||||
return ($this->feedType == self::$FEED_RSS) ? 'en-en' : 'en';
|
||||
return ($this->feedType === self::$FEED_RSS) ? 'en-en' : 'en';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -286,7 +290,7 @@ public function getNbLinks($max)
|
|||
}
|
||||
|
||||
$intNb = intval($this->userInput['nb']);
|
||||
if (! is_int($intNb) || $intNb == 0) {
|
||||
if (!is_int($intNb) || $intNb == 0) {
|
||||
return self::$DEFAULT_NB_LINKS;
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli;
|
||||
namespace Shaarli\Http;
|
||||
|
||||
/**
|
||||
* URL-safe Base64 operations
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Shaarli\Http\Url;
|
||||
|
||||
/**
|
||||
* GET an HTTP URL to retrieve its content
|
||||
* Uses the cURL library or a fallback method
|
||||
|
@ -38,7 +41,7 @@ function get_http_response($url, $timeout = 30, $maxBytes = 4194304, $curlWriteF
|
|||
$cleanUrl = $urlObj->idnToAscii();
|
||||
|
||||
if (!filter_var($cleanUrl, FILTER_VALIDATE_URL) || !$urlObj->isHttp()) {
|
||||
return array(array(0 => 'Invalid HTTP Url'), false);
|
||||
return array(array(0 => 'Invalid HTTP UrlUtils'), false);
|
||||
}
|
||||
|
||||
$userAgent =
|
|
@ -1,91 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Converts an array-represented URL to a string
|
||||
*
|
||||
* Source: http://php.net/manual/en/function.parse-url.php#106731
|
||||
*
|
||||
* @see http://php.net/manual/en/function.parse-url.php
|
||||
*
|
||||
* @param array $parsedUrl an array-represented URL
|
||||
*
|
||||
* @return string the string representation of the URL
|
||||
*/
|
||||
function unparse_url($parsedUrl)
|
||||
{
|
||||
$scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'].'://' : '';
|
||||
$host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
|
||||
$port = isset($parsedUrl['port']) ? ':'.$parsedUrl['port'] : '';
|
||||
$user = isset($parsedUrl['user']) ? $parsedUrl['user'] : '';
|
||||
$pass = isset($parsedUrl['pass']) ? ':'.$parsedUrl['pass'] : '';
|
||||
$pass = ($user || $pass) ? "$pass@" : '';
|
||||
$path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
|
||||
$query = isset($parsedUrl['query']) ? '?'.$parsedUrl['query'] : '';
|
||||
$fragment = isset($parsedUrl['fragment']) ? '#'.$parsedUrl['fragment'] : '';
|
||||
|
||||
return "$scheme$user$pass$host$port$path$query$fragment";
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes undesired query parameters and fragments
|
||||
*
|
||||
* @param string url Url to be cleaned
|
||||
*
|
||||
* @return string the string representation of this URL after cleanup
|
||||
*/
|
||||
function cleanup_url($url)
|
||||
{
|
||||
$obj_url = new Url($url);
|
||||
return $obj_url->cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL scheme.
|
||||
*
|
||||
* @param string url Url for which the scheme is requested
|
||||
*
|
||||
* @return mixed the URL scheme or false if none is provided.
|
||||
*/
|
||||
function get_url_scheme($url)
|
||||
{
|
||||
$obj_url = new Url($url);
|
||||
return $obj_url->getScheme();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trailing slash at the end of URL if necessary.
|
||||
*
|
||||
* @param string $url URL to check/edit.
|
||||
*
|
||||
* @return string $url URL with a end trailing slash.
|
||||
*/
|
||||
function add_trailing_slash($url)
|
||||
{
|
||||
return $url . (!endsWith($url, '/') ? '/' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace not whitelisted protocols by 'http://' from given URL.
|
||||
*
|
||||
* @param string $url URL to clean
|
||||
* @param array $protocols List of allowed protocols (aside from http(s)).
|
||||
*
|
||||
* @return string URL with allowed protocol
|
||||
*/
|
||||
function whitelist_protocols($url, $protocols)
|
||||
{
|
||||
if (startsWith($url, '?') || startsWith($url, '/')) {
|
||||
return $url;
|
||||
}
|
||||
$protocols = array_merge(['http', 'https'], $protocols);
|
||||
$protocol = preg_match('#^(\w+):/?/?#', $url, $match);
|
||||
// Protocol not allowed: we remove it and replace it with http
|
||||
if ($protocol === 1 && ! in_array($match[1], $protocols)) {
|
||||
$url = str_replace($match[0], 'http://', $url);
|
||||
} elseif ($protocol !== 1) {
|
||||
$url = 'http://' . $url;
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
namespace Shaarli\Http;
|
||||
|
||||
/**
|
||||
* URL representation and cleanup utilities
|
||||
|
@ -196,7 +111,7 @@ public function toString()
|
|||
*/
|
||||
protected function cleanupQuery()
|
||||
{
|
||||
if (! isset($this->parts['query'])) {
|
||||
if (!isset($this->parts['query'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -224,7 +139,7 @@ protected function cleanupQuery()
|
|||
*/
|
||||
protected function cleanupFragment()
|
||||
{
|
||||
if (! isset($this->parts['fragment'])) {
|
||||
if (!isset($this->parts['fragment'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -257,7 +172,7 @@ public function cleanup()
|
|||
public function idnToAscii()
|
||||
{
|
||||
$out = $this->cleanup();
|
||||
if (! function_exists('idn_to_ascii') || ! isset($this->parts['host'])) {
|
||||
if (!function_exists('idn_to_ascii') || !isset($this->parts['host'])) {
|
||||
return $out;
|
||||
}
|
||||
$asciiHost = idn_to_ascii($this->parts['host'], 0, INTL_IDNA_VARIANT_UTS46);
|
||||
|
@ -291,7 +206,7 @@ public function getHost()
|
|||
}
|
||||
|
||||
/**
|
||||
* Test if the Url is an HTTP one.
|
||||
* Test if the UrlUtils is an HTTP one.
|
||||
*
|
||||
* @return true is HTTP, false otherwise.
|
||||
*/
|
88
application/http/UrlUtils.php
Normal file
88
application/http/UrlUtils.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/**
|
||||
* Converts an array-represented URL to a string
|
||||
*
|
||||
* Source: http://php.net/manual/en/function.parse-url.php#106731
|
||||
*
|
||||
* @see http://php.net/manual/en/function.parse-url.php
|
||||
*
|
||||
* @param array $parsedUrl an array-represented URL
|
||||
*
|
||||
* @return string the string representation of the URL
|
||||
*/
|
||||
function unparse_url($parsedUrl)
|
||||
{
|
||||
$scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'].'://' : '';
|
||||
$host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
|
||||
$port = isset($parsedUrl['port']) ? ':'.$parsedUrl['port'] : '';
|
||||
$user = isset($parsedUrl['user']) ? $parsedUrl['user'] : '';
|
||||
$pass = isset($parsedUrl['pass']) ? ':'.$parsedUrl['pass'] : '';
|
||||
$pass = ($user || $pass) ? "$pass@" : '';
|
||||
$path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
|
||||
$query = isset($parsedUrl['query']) ? '?'.$parsedUrl['query'] : '';
|
||||
$fragment = isset($parsedUrl['fragment']) ? '#'.$parsedUrl['fragment'] : '';
|
||||
|
||||
return "$scheme$user$pass$host$port$path$query$fragment";
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes undesired query parameters and fragments
|
||||
*
|
||||
* @param string url UrlUtils to be cleaned
|
||||
*
|
||||
* @return string the string representation of this URL after cleanup
|
||||
*/
|
||||
function cleanup_url($url)
|
||||
{
|
||||
$obj_url = new \Shaarli\Http\Url($url);
|
||||
return $obj_url->cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL scheme.
|
||||
*
|
||||
* @param string url UrlUtils for which the scheme is requested
|
||||
*
|
||||
* @return mixed the URL scheme or false if none is provided.
|
||||
*/
|
||||
function get_url_scheme($url)
|
||||
{
|
||||
$obj_url = new \Shaarli\Http\Url($url);
|
||||
return $obj_url->getScheme();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trailing slash at the end of URL if necessary.
|
||||
*
|
||||
* @param string $url URL to check/edit.
|
||||
*
|
||||
* @return string $url URL with a end trailing slash.
|
||||
*/
|
||||
function add_trailing_slash($url)
|
||||
{
|
||||
return $url . (!endsWith($url, '/') ? '/' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace not whitelisted protocols by 'http://' from given URL.
|
||||
*
|
||||
* @param string $url URL to clean
|
||||
* @param array $protocols List of allowed protocols (aside from http(s)).
|
||||
*
|
||||
* @return string URL with allowed protocol
|
||||
*/
|
||||
function whitelist_protocols($url, $protocols)
|
||||
{
|
||||
if (startsWith($url, '?') || startsWith($url, '/')) {
|
||||
return $url;
|
||||
}
|
||||
$protocols = array_merge(['http', 'https'], $protocols);
|
||||
$protocol = preg_match('#^(\w+):/?/?#', $url, $match);
|
||||
// Protocol not allowed: we remove it and replace it with http
|
||||
if ($protocol === 1 && ! in_array($match[1], $protocols)) {
|
||||
$url = str_replace($match[0], 'http://', $url);
|
||||
} elseif ($protocol !== 1) {
|
||||
$url = 'http://' . $url;
|
||||
}
|
||||
return $url;
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
<?php
|
||||
|
||||
use Psr\Log\LogLevel;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
|
||||
namespace Shaarli\Netscape;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
use Katzgrau\KLogger\Logger;
|
||||
use Psr\Log\LogLevel;
|
||||
use Shaarli\Bookmark\LinkDB;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\History;
|
||||
use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
|
||||
|
||||
/**
|
||||
* Utilities to import and export bookmarks using the Netscape format
|
||||
|
@ -31,8 +38,8 @@ class NetscapeBookmarkUtils
|
|||
public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $indexUrl)
|
||||
{
|
||||
// see tpl/export.html for possible values
|
||||
if (! in_array($selection, array('all', 'public', 'private'))) {
|
||||
throw new Exception(t('Invalid export selection:') .' "'.$selection.'"');
|
||||
if (!in_array($selection, array('all', 'public', 'private'))) {
|
||||
throw new Exception(t('Invalid export selection:') . ' "' . $selection . '"');
|
||||
}
|
||||
|
||||
$bookmarkLinks = array();
|
||||
|
@ -47,7 +54,7 @@ public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $in
|
|||
$link['timestamp'] = $date->getTimestamp();
|
||||
$link['taglist'] = str_replace(' ', ',', $link['tags']);
|
||||
|
||||
if (startsWith($link['url'], '?') && $prependNoteUrl) {
|
||||
if (is_note($link['url']) && $prependNoteUrl) {
|
||||
$link['url'] = $indexUrl . $link['url'];
|
||||
}
|
||||
|
||||
|
@ -84,7 +91,7 @@ private static function importStatus(
|
|||
$status .= vsprintf(
|
||||
t(
|
||||
'was successfully processed in %d seconds: '
|
||||
.'%d links imported, %d links overwritten, %d links skipped.'
|
||||
. '%d links imported, %d links overwritten, %d links skipped.'
|
||||
),
|
||||
[$duration, $importCount, $overwriteCount, $skipCount]
|
||||
);
|
||||
|
@ -115,7 +122,7 @@ public static function import($post, $files, $linkDb, $conf, $history)
|
|||
}
|
||||
|
||||
// Overwrite existing links?
|
||||
$overwrite = ! empty($post['overwrite']);
|
||||
$overwrite = !empty($post['overwrite']);
|
||||
|
||||
// Add tags to all imported links?
|
||||
if (empty($post['default_tags'])) {
|
||||
|
@ -138,7 +145,7 @@ public static function import($post, $files, $linkDb, $conf, $history)
|
|||
);
|
||||
$logger = new Logger(
|
||||
$conf->get('resource.data_dir'),
|
||||
! $conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG,
|
||||
!$conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG,
|
||||
[
|
||||
'prefix' => 'import.',
|
||||
'extension' => 'log',
|
||||
|
@ -193,7 +200,7 @@ public static function import($post, $files, $linkDb, $conf, $history)
|
|||
}
|
||||
|
||||
// Add a new link - @ used for UNIX timestamps
|
||||
$newLinkDate = new DateTime('@'.strval($bkm['time']));
|
||||
$newLinkDate = new DateTime('@' . strval($bkm['time']));
|
||||
$newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get()));
|
||||
$newLink['created'] = $newLinkDate;
|
||||
$newLink['id'] = $linkDb->getNextId();
|
|
@ -1,4 +1,8 @@
|
|||
<?php
|
||||
namespace Shaarli\Plugin;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Plugin\Exception\PluginFileNotFoundException;
|
||||
|
||||
/**
|
||||
* Class PluginManager
|
||||
|
@ -9,12 +13,14 @@ class PluginManager
|
|||
{
|
||||
/**
|
||||
* List of authorized plugins from configuration file.
|
||||
*
|
||||
* @var array $authorizedPlugins
|
||||
*/
|
||||
private $authorizedPlugins;
|
||||
|
||||
/**
|
||||
* List of loaded plugins.
|
||||
*
|
||||
* @var array $loadedPlugins
|
||||
*/
|
||||
private $loadedPlugins = array();
|
||||
|
@ -31,12 +37,14 @@ class PluginManager
|
|||
|
||||
/**
|
||||
* Plugins subdirectory.
|
||||
*
|
||||
* @var string $PLUGINS_PATH
|
||||
*/
|
||||
public static $PLUGINS_PATH = 'plugins';
|
||||
|
||||
/**
|
||||
* Plugins meta files extension.
|
||||
*
|
||||
* @var string $META_EXT
|
||||
*/
|
||||
public static $META_EXT = 'meta';
|
||||
|
@ -118,7 +126,7 @@ public function executeHooks($hook, &$data, $params = array())
|
|||
* @param string $pluginName plugin's name.
|
||||
*
|
||||
* @return void
|
||||
* @throws PluginFileNotFoundException - plugin files not found.
|
||||
* @throws \Shaarli\Plugin\Exception\PluginFileNotFoundException - plugin files not found.
|
||||
*/
|
||||
private function loadPlugin($dir, $pluginName)
|
||||
{
|
||||
|
@ -204,8 +212,8 @@ public function getPluginsMeta()
|
|||
|
||||
$metaData[$plugin]['parameters'][$param]['value'] = '';
|
||||
// Optional parameter description in parameter.PARAM_NAME=
|
||||
if (isset($metaData[$plugin]['parameter.'. $param])) {
|
||||
$metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.'. $param]);
|
||||
if (isset($metaData[$plugin]['parameter.' . $param])) {
|
||||
$metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.' . $param]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,22 +231,3 @@ public function getErrors()
|
|||
return $this->errors;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class PluginFileNotFoundException
|
||||
*
|
||||
* Raise when plugin files can't be found.
|
||||
*/
|
||||
class PluginFileNotFoundException extends Exception
|
||||
{
|
||||
/**
|
||||
* Construct exception with plugin name.
|
||||
* Generate message.
|
||||
*
|
||||
* @param string $pluginName name of the plugin not found
|
||||
*/
|
||||
public function __construct($pluginName)
|
||||
{
|
||||
$this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName);
|
||||
}
|
||||
}
|
23
application/plugin/exception/PluginFileNotFoundException.php
Normal file
23
application/plugin/exception/PluginFileNotFoundException.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
namespace Shaarli\Plugin\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class PluginFileNotFoundException
|
||||
*
|
||||
* Raise when plugin files can't be found.
|
||||
*/
|
||||
class PluginFileNotFoundException extends Exception
|
||||
{
|
||||
/**
|
||||
* Construct exception with plugin name.
|
||||
* Generate message.
|
||||
*
|
||||
* @param string $pluginName name of the plugin not found
|
||||
*/
|
||||
public function __construct($pluginName)
|
||||
{
|
||||
$this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Render;
|
||||
|
||||
use Exception;
|
||||
use RainTPL;
|
||||
use Shaarli\ApplicationUtils;
|
||||
use Shaarli\Bookmark\LinkDB;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Thumbnailer;
|
||||
|
||||
|
@ -37,7 +43,9 @@ class PageBuilder
|
|||
*/
|
||||
protected $token;
|
||||
|
||||
/** @var bool $isLoggedIn Whether the user is logged in **/
|
||||
/**
|
||||
* @var bool $isLoggedIn Whether the user is logged in
|
||||
*/
|
||||
protected $isLoggedIn = false;
|
||||
|
||||
/**
|
||||
|
@ -101,7 +109,7 @@ private function initialize()
|
|||
ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt'))
|
||||
);
|
||||
$this->tpl->assign('index_url', index_url($_SERVER));
|
||||
$visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
|
||||
$visibility = !empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
|
||||
$this->tpl->assign('visibility', $visibility);
|
||||
$this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly']));
|
||||
$this->tpl->assign('pagetitle', $this->conf->get('general.title', 'Shaarli'));
|
||||
|
@ -115,6 +123,8 @@ private function initialize()
|
|||
$this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false));
|
||||
$this->tpl->assign('token', $this->token);
|
||||
|
||||
$this->tpl->assign('language', $this->conf->get('translation.language'));
|
||||
|
||||
if ($this->linkDB !== null) {
|
||||
$this->tpl->assign('tags', $this->linkDB->linksCountPerTag());
|
||||
}
|
||||
|
@ -126,7 +136,7 @@ private function initialize()
|
|||
$this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width'));
|
||||
$this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height'));
|
||||
|
||||
if (! empty($_SESSION['warnings'])) {
|
||||
if (!empty($_SESSION['warnings'])) {
|
||||
$this->tpl->assign('global_warnings', $_SESSION['warnings']);
|
||||
unset($_SESSION['warnings']);
|
||||
}
|
||||
|
@ -189,16 +199,16 @@ public function renderPage($page)
|
|||
|
||||
/**
|
||||
* Render a 404 page (uses the template : tpl/404.tpl)
|
||||
* usage : $PAGE->render404('The link was deleted')
|
||||
* usage: $PAGE->render404('The link was deleted')
|
||||
*
|
||||
* @param string $message A messate to display what is not found
|
||||
* @param string $message A message to display what is not found
|
||||
*/
|
||||
public function render404($message = '')
|
||||
{
|
||||
if (empty($message)) {
|
||||
$message = t('The page you are trying to reach does not exist or has been deleted.');
|
||||
}
|
||||
header($_SERVER['SERVER_PROTOCOL'] .' '. t('404 Not Found'));
|
||||
header($_SERVER['SERVER_PROTOCOL'] . ' ' . t('404 Not Found'));
|
||||
$this->tpl->assign('error_message', $message);
|
||||
$this->renderPage('404');
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli;
|
||||
namespace Shaarli\Render;
|
||||
|
||||
/**
|
||||
* Class ThemeUtils
|
213
application/security/BanManager.php
Normal file
213
application/security/BanManager.php
Normal file
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Shaarli\Security;
|
||||
|
||||
use Shaarli\FileUtils;
|
||||
|
||||
/**
|
||||
* Class BanManager
|
||||
*
|
||||
* Failed login attempts will store the associated IP address.
|
||||
* After N failed attempts, the IP will be prevented from log in for duration D.
|
||||
* Both N and D can be set in the configuration file.
|
||||
*
|
||||
* @package Shaarli\Security
|
||||
*/
|
||||
class BanManager
|
||||
{
|
||||
/** @var array List of allowed proxies IP */
|
||||
protected $trustedProxies;
|
||||
|
||||
/** @var int Number of allowed failed attempt before the ban */
|
||||
protected $nbAttempts;
|
||||
|
||||
/** @var int Ban duration in seconds */
|
||||
protected $banDuration;
|
||||
|
||||
/** @var string Path to the file containing IP bans and failures */
|
||||
protected $banFile;
|
||||
|
||||
/** @var string Path to the log file, used to log bans */
|
||||
protected $logFile;
|
||||
|
||||
/** @var array List of IP with their associated number of failed attempts */
|
||||
protected $failures = [];
|
||||
|
||||
/** @var array List of banned IP with their associated unban timestamp */
|
||||
protected $bans = [];
|
||||
|
||||
/**
|
||||
* BanManager constructor.
|
||||
*
|
||||
* @param array $trustedProxies List of allowed proxies IP
|
||||
* @param int $nbAttempts Number of allowed failed attempt before the ban
|
||||
* @param int $banDuration Ban duration in seconds
|
||||
* @param string $banFile Path to the file containing IP bans and failures
|
||||
* @param string $logFile Path to the log file, used to log bans
|
||||
*/
|
||||
public function __construct($trustedProxies, $nbAttempts, $banDuration, $banFile, $logFile) {
|
||||
$this->trustedProxies = $trustedProxies;
|
||||
$this->nbAttempts = $nbAttempts;
|
||||
$this->banDuration = $banDuration;
|
||||
$this->banFile = $banFile;
|
||||
$this->logFile = $logFile;
|
||||
$this->readBanFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a failed login and ban the IP after too many failed attempts
|
||||
*
|
||||
* @param array $server The $_SERVER array
|
||||
*/
|
||||
public function handleFailedAttempt($server)
|
||||
{
|
||||
$ip = $this->getIp($server);
|
||||
// the IP is behind a trusted forward proxy, but is not forwarded
|
||||
// in the HTTP headers, so we do nothing
|
||||
if (empty($ip)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// increment the fail count for this IP
|
||||
if (isset($this->failures[$ip])) {
|
||||
$this->failures[$ip]++;
|
||||
} else {
|
||||
$this->failures[$ip] = 1;
|
||||
}
|
||||
|
||||
if ($this->failures[$ip] >= $this->nbAttempts) {
|
||||
$this->bans[$ip] = time() + $this->banDuration;
|
||||
logm(
|
||||
$this->logFile,
|
||||
$server['REMOTE_ADDR'],
|
||||
'IP address banned from login: '. $ip
|
||||
);
|
||||
}
|
||||
$this->writeBanFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove failed attempts for the provided client.
|
||||
*
|
||||
* @param array $server $_SERVER
|
||||
*/
|
||||
public function clearFailures($server)
|
||||
{
|
||||
$ip = $this->getIp($server);
|
||||
// the IP is behind a trusted forward proxy, but is not forwarded
|
||||
// in the HTTP headers, so we do nothing
|
||||
if (empty($ip)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->failures[$ip])) {
|
||||
unset($this->failures[$ip]);
|
||||
}
|
||||
$this->writeBanFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the client IP is banned or not.
|
||||
*
|
||||
* @param array $server $_SERVER
|
||||
*
|
||||
* @return bool True if the IP is banned, false otherwise
|
||||
*/
|
||||
public function isBanned($server)
|
||||
{
|
||||
$ip = $this->getIp($server);
|
||||
// the IP is behind a trusted forward proxy, but is not forwarded
|
||||
// in the HTTP headers, so we allow the authentication attempt.
|
||||
if (empty($ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the user is not banned
|
||||
if (! isset($this->bans[$ip])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the user is still banned
|
||||
if ($this->bans[$ip] > time()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// the ban has expired, the user can attempt to log in again
|
||||
if (isset($this->failures[$ip])) {
|
||||
unset($this->failures[$ip]);
|
||||
}
|
||||
unset($this->bans[$ip]);
|
||||
logm($this->logFile, $server['REMOTE_ADDR'], 'Ban lifted for: '. $ip);
|
||||
|
||||
$this->writeBanFile();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the IP from $_SERVER.
|
||||
* If the actual IP is behind an allowed reverse proxy,
|
||||
* we try to extract the forwarded IP from HTTP headers.
|
||||
*
|
||||
* @param array $server $_SERVER
|
||||
*
|
||||
* @return string|bool The IP or false if none could be extracted
|
||||
*/
|
||||
protected function getIp($server)
|
||||
{
|
||||
$ip = $server['REMOTE_ADDR'];
|
||||
if (! in_array($ip, $this->trustedProxies)) {
|
||||
return $ip;
|
||||
}
|
||||
return getIpAddressFromProxy($server, $this->trustedProxies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file containing banned IPs
|
||||
*/
|
||||
protected function readBanFile()
|
||||
{
|
||||
$data = FileUtils::readFlatDB($this->banFile);
|
||||
if (isset($data['failures']) && is_array($data['failures'])) {
|
||||
$this->failures = $data['failures'];
|
||||
}
|
||||
|
||||
if (isset($data['bans']) && is_array($data['bans'])) {
|
||||
$this->bans = $data['bans'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the banned IPs to a file
|
||||
*/
|
||||
protected function writeBanFile()
|
||||
{
|
||||
return FileUtils::writeFlatDB(
|
||||
$this->banFile,
|
||||
[
|
||||
'failures' => $this->failures,
|
||||
'bans' => $this->bans,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Failures (for UT purpose).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFailures()
|
||||
{
|
||||
return $this->failures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Bans (for UT purpose).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBans()
|
||||
{
|
||||
return $this->bans;
|
||||
}
|
||||
}
|
|
@ -20,8 +20,8 @@ class LoginManager
|
|||
/** @var SessionManager Session Manager instance **/
|
||||
protected $sessionManager = null;
|
||||
|
||||
/** @var string Path to the file containing IP bans */
|
||||
protected $banFile = '';
|
||||
/** @var BanManager Ban Manager instance **/
|
||||
protected $banManager;
|
||||
|
||||
/** @var bool Whether the user is logged in **/
|
||||
protected $isLoggedIn = false;
|
||||
|
@ -35,17 +35,21 @@ class LoginManager
|
|||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $globals The $GLOBALS array (reference)
|
||||
* @param ConfigManager $configManager Configuration Manager instance
|
||||
* @param SessionManager $sessionManager SessionManager instance
|
||||
*/
|
||||
public function __construct(& $globals, $configManager, $sessionManager)
|
||||
public function __construct($configManager, $sessionManager)
|
||||
{
|
||||
$this->globals = &$globals;
|
||||
$this->configManager = $configManager;
|
||||
$this->sessionManager = $sessionManager;
|
||||
$this->banFile = $this->configManager->get('resource.ban_file', 'data/ipbans.php');
|
||||
$this->readBanFile();
|
||||
$this->banManager = new BanManager(
|
||||
$this->configManager->get('security.trusted_proxies', []),
|
||||
$this->configManager->get('security.ban_after'),
|
||||
$this->configManager->get('security.ban_duration'),
|
||||
$this->configManager->get('resource.ban_file', 'data/ipbans.php'),
|
||||
$this->configManager->get('resource.log')
|
||||
);
|
||||
|
||||
if ($this->configManager->get('security.open_shaarli') === true) {
|
||||
$this->openShaarli = true;
|
||||
}
|
||||
|
@ -58,6 +62,9 @@ public function __construct(& $globals, $configManager, $sessionManager)
|
|||
*/
|
||||
public function generateStaySignedInToken($clientIpAddress)
|
||||
{
|
||||
if ($this->configManager->get('security.session_protection_disabled') === true) {
|
||||
$clientIpAddress = '';
|
||||
}
|
||||
$this->staySignedInToken = sha1(
|
||||
$this->configManager->get('credentials.hash')
|
||||
. $clientIpAddress
|
||||
|
@ -154,31 +161,6 @@ public function checkCredentials($remoteIp, $clientIpId, $login, $password)
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file containing banned IPs
|
||||
*/
|
||||
protected function readBanFile()
|
||||
{
|
||||
if (! file_exists($this->banFile)) {
|
||||
return;
|
||||
}
|
||||
include $this->banFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the banned IPs to a file
|
||||
*/
|
||||
protected function writeBanFile()
|
||||
{
|
||||
if (! array_key_exists('IPBANS', $this->globals)) {
|
||||
return;
|
||||
}
|
||||
file_put_contents(
|
||||
$this->banFile,
|
||||
"<?php\n\$GLOBALS['IPBANS']=" . var_export($this->globals['IPBANS'], true) . ";\n?>"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a failed login and ban the IP after too many failed attempts
|
||||
*
|
||||
|
@ -186,34 +168,7 @@ protected function writeBanFile()
|
|||
*/
|
||||
public function handleFailedLogin($server)
|
||||
{
|
||||
$ip = $server['REMOTE_ADDR'];
|
||||
$trusted = $this->configManager->get('security.trusted_proxies', []);
|
||||
|
||||
if (in_array($ip, $trusted)) {
|
||||
$ip = getIpAddressFromProxy($server, $trusted);
|
||||
if (! $ip) {
|
||||
// the IP is behind a trusted forward proxy, but is not forwarded
|
||||
// in the HTTP headers, so we do nothing
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// increment the fail count for this IP
|
||||
if (isset($this->globals['IPBANS']['FAILURES'][$ip])) {
|
||||
$this->globals['IPBANS']['FAILURES'][$ip]++;
|
||||
} else {
|
||||
$this->globals['IPBANS']['FAILURES'][$ip] = 1;
|
||||
}
|
||||
|
||||
if ($this->globals['IPBANS']['FAILURES'][$ip] >= $this->configManager->get('security.ban_after')) {
|
||||
$this->globals['IPBANS']['BANS'][$ip] = time() + $this->configManager->get('security.ban_duration', 1800);
|
||||
logm(
|
||||
$this->configManager->get('resource.log'),
|
||||
$server['REMOTE_ADDR'],
|
||||
'IP address banned from login'
|
||||
);
|
||||
}
|
||||
$this->writeBanFile();
|
||||
$this->banManager->handleFailedAttempt($server);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,13 +178,7 @@ public function handleFailedLogin($server)
|
|||
*/
|
||||
public function handleSuccessfulLogin($server)
|
||||
{
|
||||
$ip = $server['REMOTE_ADDR'];
|
||||
// FIXME unban when behind a trusted proxy?
|
||||
|
||||
unset($this->globals['IPBANS']['FAILURES'][$ip]);
|
||||
unset($this->globals['IPBANS']['BANS'][$ip]);
|
||||
|
||||
$this->writeBanFile();
|
||||
$this->banManager->clearFailures($server);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -241,24 +190,6 @@ public function handleSuccessfulLogin($server)
|
|||
*/
|
||||
public function canLogin($server)
|
||||
{
|
||||
$ip = $server['REMOTE_ADDR'];
|
||||
|
||||
if (! isset($this->globals['IPBANS']['BANS'][$ip])) {
|
||||
// the user is not banned
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->globals['IPBANS']['BANS'][$ip] > time()) {
|
||||
// the user is still banned
|
||||
return false;
|
||||
}
|
||||
|
||||
// the ban has expired, the user can attempt to log in again
|
||||
logm($this->configManager->get('resource.log'), $server['REMOTE_ADDR'], 'Ban lifted.');
|
||||
unset($this->globals['IPBANS']['FAILURES'][$ip]);
|
||||
unset($this->globals['IPBANS']['BANS'][$ip]);
|
||||
|
||||
$this->writeBanFile();
|
||||
return true;
|
||||
return ! $this->banManager->isBanned($server);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Updater;
|
||||
|
||||
use Exception;
|
||||
use RainTPL;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionMethod;
|
||||
use Shaarli\ApplicationUtils;
|
||||
use Shaarli\Bookmark\LinkDB;
|
||||
use Shaarli\Bookmark\LinkFilter;
|
||||
use Shaarli\Config\ConfigJson;
|
||||
use Shaarli\Config\ConfigPhp;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Config\ConfigPhp;
|
||||
use Shaarli\Exceptions\IOException;
|
||||
use Shaarli\Thumbnailer;
|
||||
use Shaarli\Updater\Exception\UpdaterException;
|
||||
|
||||
/**
|
||||
* Class Updater.
|
||||
* Class updater.
|
||||
* Used to update stuff when a new Shaarli's version is reached.
|
||||
* Update methods are ran only once, and the stored in a JSON file.
|
||||
*/
|
||||
|
@ -83,12 +96,12 @@ public function update()
|
|||
}
|
||||
|
||||
if ($this->methods === null) {
|
||||
throw new UpdaterException(t('Couldn\'t retrieve Updater class methods.'));
|
||||
throw new UpdaterException(t('Couldn\'t retrieve updater class methods.'));
|
||||
}
|
||||
|
||||
foreach ($this->methods as $method) {
|
||||
// Not an update method or already done, pass.
|
||||
if (! startsWith($method->getName(), 'updateMethod')
|
||||
if (!startsWith($method->getName(), 'updateMethod')
|
||||
|| in_array($method->getName(), $this->doneUpdates)
|
||||
) {
|
||||
continue;
|
||||
|
@ -139,7 +152,7 @@ public function updateMethodMergeDeprecatedConfigFile()
|
|||
}
|
||||
}
|
||||
$this->conf->write($this->isLoggedIn);
|
||||
unlink($this->conf->get('resource.data_dir').'/options.php');
|
||||
unlink($this->conf->get('resource.data_dir') . '/options.php');
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -174,10 +187,10 @@ public function updateMethodConfigToJson()
|
|||
$subConfig = array('config', 'plugins');
|
||||
foreach ($subConfig as $sub) {
|
||||
foreach ($oldConfig[$sub] as $key => $value) {
|
||||
if (isset($legacyMap[$sub .'.'. $key])) {
|
||||
$configKey = $legacyMap[$sub .'.'. $key];
|
||||
if (isset($legacyMap[$sub . '.' . $key])) {
|
||||
$configKey = $legacyMap[$sub . '.' . $key];
|
||||
} else {
|
||||
$configKey = $sub .'.'. $key;
|
||||
$configKey = $sub . '.' . $key;
|
||||
}
|
||||
$this->conf->set($configKey, $value);
|
||||
}
|
||||
|
@ -205,7 +218,6 @@ public function updateMethodEscapeUnescapedConfig()
|
|||
try {
|
||||
$this->conf->set('general.title', escape($this->conf->get('general.title')));
|
||||
$this->conf->set('general.header_link', escape($this->conf->get('general.header_link')));
|
||||
$this->conf->set('redirector.url', escape($this->conf->get('redirector.url')));
|
||||
$this->conf->write($this->isLoggedIn);
|
||||
} catch (Exception $e) {
|
||||
error_log($e->getMessage());
|
||||
|
@ -233,7 +245,7 @@ public function updateMethodDatastoreIds()
|
|||
return true;
|
||||
}
|
||||
|
||||
$save = $this->conf->get('resource.data_dir') .'/datastore.'. date('YmdHis') .'.php';
|
||||
$save = $this->conf->get('resource.data_dir') . '/datastore.' . date('YmdHis') . '.php';
|
||||
copy($this->conf->get('resource.datastore'), $save);
|
||||
|
||||
$links = array();
|
||||
|
@ -307,7 +319,7 @@ public function updateMethodDefaultTheme()
|
|||
// We run the update only if this folder still contains the template files.
|
||||
$tplDir = $this->conf->get('resource.raintpl_tpl');
|
||||
$tplFile = $tplDir . '/linklist.html';
|
||||
if (! file_exists($tplFile)) {
|
||||
if (!file_exists($tplFile)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -331,7 +343,7 @@ public function updateMethodDefaultTheme()
|
|||
*/
|
||||
public function updateMethodMoveUserCss()
|
||||
{
|
||||
if (! is_file('inc/user.css')) {
|
||||
if (!is_file('inc/user.css')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -367,11 +379,11 @@ public function updateMethodEscapeMarkdown()
|
|||
*/
|
||||
public function updateMethodPiwikUrl()
|
||||
{
|
||||
if (! $this->conf->exists('plugins.PIWIK_URL') || startsWith($this->conf->get('plugins.PIWIK_URL'), 'http')) {
|
||||
if (!$this->conf->exists('plugins.PIWIK_URL') || startsWith($this->conf->get('plugins.PIWIK_URL'), 'http')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->conf->set('plugins.PIWIK_URL', 'http://'. $this->conf->get('plugins.PIWIK_URL'));
|
||||
$this->conf->set('plugins.PIWIK_URL', 'http://' . $this->conf->get('plugins.PIWIK_URL'));
|
||||
$this->conf->write($this->isLoggedIn);
|
||||
|
||||
return true;
|
||||
|
@ -481,11 +493,11 @@ public function updateMethodDownloadSizeAndTimeoutConf()
|
|||
return true;
|
||||
}
|
||||
|
||||
if (! $this->conf->exists('general.download_max_size')) {
|
||||
$this->conf->set('general.download_max_size', 1024*1024*4);
|
||||
if (!$this->conf->exists('general.download_max_size')) {
|
||||
$this->conf->set('general.download_max_size', 1024 * 1024 * 4);
|
||||
}
|
||||
|
||||
if (! $this->conf->exists('general.download_timeout')) {
|
||||
if (!$this->conf->exists('general.download_timeout')) {
|
||||
$this->conf->set('general.download_timeout', 30);
|
||||
}
|
||||
|
||||
|
@ -537,97 +549,14 @@ public function updateMethodSetSticky()
|
|||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class UpdaterException.
|
||||
*/
|
||||
class UpdaterException extends Exception
|
||||
{
|
||||
/**
|
||||
* @var string Method where the error occurred.
|
||||
*/
|
||||
protected $method;
|
||||
|
||||
/**
|
||||
* @var Exception The parent exception.
|
||||
* Remove redirector settings.
|
||||
*/
|
||||
protected $previous;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $message Force the error message if set.
|
||||
* @param string $method Method where the error occurred.
|
||||
* @param Exception|bool $previous Parent exception.
|
||||
*/
|
||||
public function __construct($message = '', $method = '', $previous = false)
|
||||
public function updateMethodRemoveRedirector()
|
||||
{
|
||||
$this->method = $method;
|
||||
$this->previous = $previous;
|
||||
$this->message = $this->buildMessage($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the exception error message.
|
||||
*
|
||||
* @param string $message Optional given error message.
|
||||
*
|
||||
* @return string The built error message.
|
||||
*/
|
||||
private function buildMessage($message)
|
||||
{
|
||||
$out = '';
|
||||
if (! empty($message)) {
|
||||
$out .= $message . PHP_EOL;
|
||||
}
|
||||
|
||||
if (! empty($this->method)) {
|
||||
$out .= t('An error occurred while running the update ') . $this->method . PHP_EOL;
|
||||
}
|
||||
|
||||
if (! empty($this->previous)) {
|
||||
$out .= ' '. $this->previous->getMessage();
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the updates file, and return already done updates.
|
||||
*
|
||||
* @param string $updatesFilepath Updates file path.
|
||||
*
|
||||
* @return array Already done update methods.
|
||||
*/
|
||||
function read_updates_file($updatesFilepath)
|
||||
{
|
||||
if (! empty($updatesFilepath) && is_file($updatesFilepath)) {
|
||||
$content = file_get_contents($updatesFilepath);
|
||||
if (! empty($content)) {
|
||||
return explode(';', $content);
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write updates file.
|
||||
*
|
||||
* @param string $updatesFilepath Updates file path.
|
||||
* @param array $updates Updates array to write.
|
||||
*
|
||||
* @throws Exception Couldn't write version number.
|
||||
*/
|
||||
function write_updates_file($updatesFilepath, $updates)
|
||||
{
|
||||
if (empty($updatesFilepath)) {
|
||||
throw new Exception(t('Updates file path is not set, can\'t write updates.'));
|
||||
}
|
||||
|
||||
$res = file_put_contents($updatesFilepath, implode(';', $updates));
|
||||
if ($res === false) {
|
||||
throw new Exception(t('Unable to write updates in '. $updatesFilepath . '.'));
|
||||
$this->conf->remove('redirector');
|
||||
$this->conf->write(true);
|
||||
return true;
|
||||
}
|
||||
}
|
39
application/updater/UpdaterUtils.php
Normal file
39
application/updater/UpdaterUtils.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Read the updates file, and return already done updates.
|
||||
*
|
||||
* @param string $updatesFilepath Updates file path.
|
||||
*
|
||||
* @return array Already done update methods.
|
||||
*/
|
||||
function read_updates_file($updatesFilepath)
|
||||
{
|
||||
if (! empty($updatesFilepath) && is_file($updatesFilepath)) {
|
||||
$content = file_get_contents($updatesFilepath);
|
||||
if (! empty($content)) {
|
||||
return explode(';', $content);
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write updates file.
|
||||
*
|
||||
* @param string $updatesFilepath Updates file path.
|
||||
* @param array $updates Updates array to write.
|
||||
*
|
||||
* @throws Exception Couldn't write version number.
|
||||
*/
|
||||
function write_updates_file($updatesFilepath, $updates)
|
||||
{
|
||||
if (empty($updatesFilepath)) {
|
||||
throw new Exception(t('Updates file path is not set, can\'t write updates.'));
|
||||
}
|
||||
|
||||
$res = file_put_contents($updatesFilepath, implode(';', $updates));
|
||||
if ($res === false) {
|
||||
throw new Exception(t('Unable to write updates in '. $updatesFilepath . '.'));
|
||||
}
|
||||
}
|
60
application/updater/exception/UpdaterException.php
Normal file
60
application/updater/exception/UpdaterException.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Updater\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class UpdaterException.
|
||||
*/
|
||||
class UpdaterException extends Exception
|
||||
{
|
||||
/**
|
||||
* @var string Method where the error occurred.
|
||||
*/
|
||||
protected $method;
|
||||
|
||||
/**
|
||||
* @var Exception The parent exception.
|
||||
*/
|
||||
protected $previous;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $message Force the error message if set.
|
||||
* @param string $method Method where the error occurred.
|
||||
* @param Exception|bool $previous Parent exception.
|
||||
*/
|
||||
public function __construct($message = '', $method = '', $previous = false)
|
||||
{
|
||||
$this->method = $method;
|
||||
$this->previous = $previous;
|
||||
$this->message = $this->buildMessage($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the exception error message.
|
||||
*
|
||||
* @param string $message Optional given error message.
|
||||
*
|
||||
* @return string The built error message.
|
||||
*/
|
||||
private function buildMessage($message)
|
||||
{
|
||||
$out = '';
|
||||
if (!empty($message)) {
|
||||
$out .= $message . PHP_EOL;
|
||||
}
|
||||
|
||||
if (!empty($this->method)) {
|
||||
$out .= t('An error occurred while running the update ') . $this->method . PHP_EOL;
|
||||
}
|
||||
|
||||
if (!empty($this->previous)) {
|
||||
$out .= ' ' . $this->previous->getMessage();
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 530 B |
|
@ -466,6 +466,28 @@ function init(description) {
|
|||
});
|
||||
}
|
||||
|
||||
const changeVisibilityButtons = document.querySelectorAll('.actions-change-visibility');
|
||||
if (changeVisibilityButtons != null && token != null) {
|
||||
[...changeVisibilityButtons].forEach((button) => {
|
||||
button.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const visibility = event.target.getAttribute('data-visibility');
|
||||
|
||||
const links = [];
|
||||
const linkCheckedCheckboxes = document.querySelectorAll('.link-checkbox:checked');
|
||||
[...linkCheckedCheckboxes].forEach((checkbox) => {
|
||||
links.push({
|
||||
id: checkbox.value,
|
||||
title: document.querySelector(`.linklist-item[data-id="${checkbox.value}"] .linklist-link`).innerHTML,
|
||||
});
|
||||
});
|
||||
|
||||
const ids = links.map(item => item.id);
|
||||
window.location = `?change_visibility&token=${token.value}&newVisibility=${visibility}&ids=${ids.join('+')}`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all button
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
$fa-font-path: '~font-awesome/fonts';
|
||||
$fa-font-path: '~fork-awesome/fonts';
|
||||
|
||||
@import '~font-awesome/scss/font-awesome';
|
||||
@import '~fork-awesome/scss/fork-awesome';
|
||||
@import '~purecss/build/pure.css';
|
||||
@import '~purecss/build/grids-responsive.css';
|
||||
@import '~pure-extras/css/pure-extras.css';
|
||||
|
@ -13,7 +13,7 @@ $dark-grey: #252525;
|
|||
$light-grey: #797979;
|
||||
$main-green: #1b926c;
|
||||
$light-green: #b0ddce;
|
||||
$dark-green: #2a4c41;
|
||||
$dark-green: #186446;
|
||||
$red: #ac2925;
|
||||
$orange: #f89406;
|
||||
$blue: #0b5ea6;
|
||||
|
@ -25,9 +25,15 @@ $warning-text: #97600d;
|
|||
$form-input-border: #d8d8d8;
|
||||
$form-input-background: #eee;
|
||||
|
||||
:root {
|
||||
--main-color: #{$main-green};
|
||||
--background-color: #{$background-color};
|
||||
--dark-main-color: #{$dark-green};
|
||||
}
|
||||
|
||||
// General
|
||||
body {
|
||||
background: $background-color;
|
||||
background: var(--background-color);
|
||||
}
|
||||
|
||||
.strong {
|
||||
|
@ -143,7 +149,7 @@ body,
|
|||
}
|
||||
|
||||
.pure-alert-success {
|
||||
background-color: $main-green;
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
.pure-alert-warning {
|
||||
|
@ -169,7 +175,7 @@ body,
|
|||
top: 0;
|
||||
transition: max-height .5s;
|
||||
z-index: 999;
|
||||
background: $main-green;
|
||||
background: var(--main-color);
|
||||
width: 100%;
|
||||
// Hack to transition with auto height: http://stackoverflow.com/a/8331169/1484919
|
||||
max-height: 45px;
|
||||
|
@ -322,7 +328,7 @@ body,
|
|||
button {
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
background-color: $main-green;
|
||||
background-color: var(--main-color);
|
||||
padding: 4px 8px 6px;
|
||||
color: $almost-white;
|
||||
}
|
||||
|
@ -358,7 +364,7 @@ body,
|
|||
.search-tagcloud {
|
||||
button {
|
||||
&:hover {
|
||||
color: $background-color;
|
||||
color: var(--background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -389,7 +395,7 @@ body,
|
|||
position: fixed;
|
||||
visibility: hidden;
|
||||
z-index: 999;
|
||||
background: $main-green;
|
||||
background: var(--main-color);
|
||||
padding: 5px 0;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
|
@ -411,7 +417,7 @@ body,
|
|||
margin: 0 0 5px;
|
||||
border: 1px solid $almost-white;
|
||||
border-radius: 2px;
|
||||
background: $main-green;
|
||||
background: var(--main-color);
|
||||
padding: 4px 0;
|
||||
width: 100px;
|
||||
height: 28px;
|
||||
|
@ -419,7 +425,7 @@ body,
|
|||
|
||||
&:hover {
|
||||
background: $almost-white;
|
||||
color: $main-green;
|
||||
color: var(--main-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -544,7 +550,10 @@ body,
|
|||
color: $dark-grey;
|
||||
font-size: .9em;
|
||||
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
margin: 3px 0;
|
||||
padding: 5px 8px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
@ -555,7 +564,7 @@ body,
|
|||
}
|
||||
|
||||
.filter-on {
|
||||
background: $main-green;
|
||||
background: var(--main-color);
|
||||
color: $light-green;
|
||||
}
|
||||
|
||||
|
@ -694,7 +703,7 @@ body,
|
|||
|
||||
&:visited {
|
||||
.linklist-link {
|
||||
color: $dark-green;
|
||||
color: var(--dark-main-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -705,7 +714,7 @@ body,
|
|||
}
|
||||
|
||||
.linklist-link {
|
||||
color: $main-green;
|
||||
color: var(--main-color);
|
||||
font-size: 1.1em;
|
||||
|
||||
&:hover {
|
||||
|
@ -713,11 +722,19 @@ body,
|
|||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: .65em;
|
||||
}
|
||||
|
||||
.label-private {
|
||||
border: solid 1px $orange;
|
||||
color: $orange;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: .65em;
|
||||
}
|
||||
|
||||
.label-sticky {
|
||||
border: solid 1px $blue;
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -772,14 +789,14 @@ body,
|
|||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: $main-green;
|
||||
color: var(--main-color);
|
||||
|
||||
&:hover {
|
||||
color: $dark-grey;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: $dark-green;
|
||||
color: var(--dark-main-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -877,7 +894,7 @@ body,
|
|||
&::before {
|
||||
display: block;
|
||||
margin: 10px auto;
|
||||
background: linear-gradient(to right, $background-color, $dark-grey, $background-color);
|
||||
background: linear-gradient(to right, var(--background-color), $dark-grey, var(--background-color));
|
||||
width: 80%;
|
||||
height: 1px;
|
||||
content: '';
|
||||
|
@ -906,7 +923,7 @@ body,
|
|||
margin: 15px 5px;
|
||||
border: 0;
|
||||
box-shadow: 1px 1px 1px $form-input-border, -1px -1px 6px $form-input-border, -1px 1px 2px $form-input-border, 1px -1px 2px $form-input-border;
|
||||
background: $main-green;
|
||||
background: var(--main-color);
|
||||
min-width: 150px;
|
||||
height: 35px;
|
||||
vertical-align: center;
|
||||
|
@ -930,7 +947,7 @@ body,
|
|||
padding: 10px 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: $main-green;
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
||||
.window-subtitle {
|
||||
|
@ -939,7 +956,7 @@ body,
|
|||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: $main-green;
|
||||
color: var(--main-color);
|
||||
font-weight: bold;
|
||||
|
||||
&.button {
|
||||
|
@ -1061,7 +1078,6 @@ body,
|
|||
.page-form {
|
||||
.submit-buttons {
|
||||
.button {
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
@ -1267,7 +1283,7 @@ form {
|
|||
|
||||
.pure-button {
|
||||
&:hover {
|
||||
background-color: $main-green;
|
||||
background-color: var(--main-color);
|
||||
background-image: none;
|
||||
color: $almost-white;
|
||||
}
|
||||
|
@ -1351,7 +1367,7 @@ form {
|
|||
}
|
||||
|
||||
.validate-rename-tag {
|
||||
color: $main-green;
|
||||
color: var(--main-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1447,7 +1463,7 @@ form {
|
|||
&::after {
|
||||
display: block;
|
||||
margin: 10px auto;
|
||||
background: linear-gradient(to right, $background-color, $dark-grey, $background-color);
|
||||
background: linear-gradient(to right, var(--background-color), $dark-grey, var(--background-color));
|
||||
width: 90%;
|
||||
height: 1px;
|
||||
content: '';
|
||||
|
@ -1497,14 +1513,14 @@ form {
|
|||
.daily-entry-description {
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: $main-green;
|
||||
color: var(--main-color);
|
||||
|
||||
&:hover {
|
||||
text-shadow: 1px 1px $background-linklist-info;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: $dark-green;
|
||||
color: var(--dark-main-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1561,12 +1577,12 @@ form {
|
|||
}
|
||||
|
||||
.pure-button-shaarli {
|
||||
background-color: $main-green;
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
.progressbar {
|
||||
border-radius: 6px;
|
||||
background-color: $main-green;
|
||||
background-color: var(--main-color);
|
||||
padding: 1px;
|
||||
|
||||
> div {
|
||||
|
@ -1575,8 +1591,8 @@ form {
|
|||
-45deg,
|
||||
$almost-white,
|
||||
$almost-white 6px,
|
||||
$background-color 6px,
|
||||
$background-color 12px
|
||||
var(--background-color) 6px,
|
||||
var(--background-color) 12px
|
||||
);
|
||||
width: 0%;
|
||||
height: 10px;
|
||||
|
@ -1600,3 +1616,17 @@ form {
|
|||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
// Print rules
|
||||
@media print {
|
||||
.shaarli-menu {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.search-linklist,
|
||||
.link-count-block,
|
||||
.linklist-item-infos-controls-group,
|
||||
.mobile-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
},
|
||||
"require": {
|
||||
"php": ">=5.6",
|
||||
"ext-json": "*",
|
||||
"ext-zlib": "*",
|
||||
"shaarli/netscape-bookmark-parser": "^2.1",
|
||||
"erusev/parsedown": "^1.6",
|
||||
"slim/slim": "^3.0",
|
||||
|
@ -24,19 +26,39 @@
|
|||
"gettext/gettext": "^4.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"roave/security-advisories": "dev-master",
|
||||
"phpunit/phpcov": "*",
|
||||
"phpunit/phpunit": "^5.0",
|
||||
"squizlabs/php_codesniffer": "2.*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Allows fetching web pages and thumbnails in a more robust way",
|
||||
"ext-gd": "Required for thumbnail generation",
|
||||
"ext-gettext": "Enables faster translation system in gettext mode",
|
||||
"ext-intl": "Provides localized text sorting",
|
||||
"ext-mbstring": "Provides multibyte (Unicode) string support"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Shaarli\\": "application",
|
||||
"Shaarli\\Api\\": "application/api/",
|
||||
"Shaarli\\Api\\Controllers\\": "application/api/controllers",
|
||||
"Shaarli\\Api\\Exceptions\\": "application/api/exceptions",
|
||||
"Shaarli\\Bookmark\\": "application/bookmark",
|
||||
"Shaarli\\Bookmark\\Exception\\": "application/bookmark/exception",
|
||||
"Shaarli\\Config\\": "application/config/",
|
||||
"Shaarli\\Config\\Exception\\": "application/config/exception",
|
||||
"Shaarli\\Security\\": "application/security"
|
||||
"Shaarli\\Exceptions\\": "application/exceptions",
|
||||
"Shaarli\\Feed\\": "application/feed",
|
||||
"Shaarli\\Http\\": "application/http",
|
||||
"Shaarli\\Netscape\\": "application/netscape",
|
||||
"Shaarli\\Plugin\\": "application/plugin",
|
||||
"Shaarli\\Plugin\\Exception\\": "application/plugin/exception",
|
||||
"Shaarli\\Plugin\\Wallabag\\": "plugins/wallabag",
|
||||
"Shaarli\\Render\\": "application/render",
|
||||
"Shaarli\\Security\\": "application/security",
|
||||
"Shaarli\\Updater\\": "application/updater",
|
||||
"Shaarli\\Updater\\Exception\\": "application/updater/exception"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
253
composer.lock
generated
253
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "3876b34296fedb365517b785af8384de",
|
||||
"content-hash": "432005c9db3e890f42fde27036d2a70f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "arthurhoaro/web-thumbnailer",
|
||||
|
@ -1470,6 +1470,210 @@
|
|||
"abandoned": true,
|
||||
"time": "2017-06-30T09:13:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "roave/security-advisories",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||
"reference": "d155baccb43ba2542941fbcba258b85ce7786419"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/d155baccb43ba2542941fbcba258b85ce7786419",
|
||||
"reference": "d155baccb43ba2542941fbcba258b85ce7786419",
|
||||
"shasum": ""
|
||||
},
|
||||
"conflict": {
|
||||
"3f/pygmentize": "<1.2",
|
||||
"adodb/adodb-php": "<5.20.12",
|
||||
"alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1",
|
||||
"amphp/artax": "<1.0.6|>=2,<2.0.6",
|
||||
"amphp/http": "<1.0.1",
|
||||
"api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6",
|
||||
"asymmetricrypt/asymmetricrypt": ">=0,<9.9.99",
|
||||
"aws/aws-sdk-php": ">=3,<3.2.1",
|
||||
"brightlocal/phpwhois": "<=4.2.5",
|
||||
"bugsnag/bugsnag-laravel": ">=2,<2.0.2",
|
||||
"cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4|>=3.4,<3.4.14|>=3.5,<3.5.17|>=3.6,<3.6.4",
|
||||
"cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4",
|
||||
"cartalyst/sentry": "<=2.1.6",
|
||||
"codeigniter/framework": "<=3.0.6",
|
||||
"composer/composer": "<=1.0.0-alpha11",
|
||||
"contao-components/mediaelement": ">=2.14.2,<2.21.1",
|
||||
"contao/core": ">=2,<3.5.35",
|
||||
"contao/core-bundle": ">=4,<4.4.18|>=4.5,<4.5.8",
|
||||
"contao/listing-bundle": ">=4,<4.4.8",
|
||||
"contao/newsletter-bundle": ">=4,<4.1",
|
||||
"david-garcia/phpwhois": "<=4.3.1",
|
||||
"doctrine/annotations": ">=1,<1.2.7",
|
||||
"doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2",
|
||||
"doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1",
|
||||
"doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2",
|
||||
"doctrine/doctrine-bundle": "<1.5.2",
|
||||
"doctrine/doctrine-module": "<=0.7.1",
|
||||
"doctrine/mongodb-odm": ">=1,<1.0.2",
|
||||
"doctrine/mongodb-odm-bundle": ">=2,<3.0.1",
|
||||
"doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1",
|
||||
"dompdf/dompdf": ">=0.6,<0.6.2",
|
||||
"drupal/core": ">=7,<7.60|>=8,<8.5.8|>=8.6,<8.6.2",
|
||||
"drupal/drupal": ">=7,<7.60|>=8,<8.5.8|>=8.6,<8.6.2",
|
||||
"erusev/parsedown": "<1.7",
|
||||
"ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.13.1|>=6,<6.7.9.1|>=6.8,<6.13.5.1|>=7,<7.2.4.1|>=7.3,<7.3.2.1",
|
||||
"ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.12.3|>=2011,<2017.12.4.3|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3",
|
||||
"ezsystems/repository-forms": ">=2.3,<2.3.2.1",
|
||||
"ezyang/htmlpurifier": "<4.1.1",
|
||||
"firebase/php-jwt": "<2",
|
||||
"fooman/tcpdf": "<6.2.22",
|
||||
"fossar/tcpdf-parser": "<6.2.22",
|
||||
"friendsofsymfony/rest-bundle": ">=1.2,<1.2.2",
|
||||
"friendsofsymfony/user-bundle": ">=1.2,<1.3.5",
|
||||
"fuel/core": "<1.8.1",
|
||||
"gree/jose": "<=2.2",
|
||||
"gregwar/rst": "<1.0.3",
|
||||
"guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1",
|
||||
"illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10",
|
||||
"illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30",
|
||||
"illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29",
|
||||
"illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15",
|
||||
"ivankristianto/phpwhois": "<=4.3",
|
||||
"james-heinrich/getid3": "<1.9.9",
|
||||
"joomla/session": "<1.3.1",
|
||||
"jsmitty12/phpwhois": "<5.1",
|
||||
"kazist/phpwhois": "<=4.2.6",
|
||||
"kreait/firebase-php": ">=3.2,<3.8.1",
|
||||
"la-haute-societe/tcpdf": "<6.2.22",
|
||||
"laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30",
|
||||
"laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10",
|
||||
"league/commonmark": ">=0.15.6,<0.18.1",
|
||||
"magento/magento1ce": "<1.9.4",
|
||||
"magento/magento1ee": ">=1.9,<1.14.4",
|
||||
"magento/product-community-edition": ">=2,<2.2.7",
|
||||
"monolog/monolog": ">=1.8,<1.12",
|
||||
"namshi/jose": "<2.2",
|
||||
"onelogin/php-saml": "<2.10.4",
|
||||
"openid/php-openid": "<2.3",
|
||||
"oro/crm": ">=1.7,<1.7.4",
|
||||
"oro/platform": ">=1.7,<1.7.4",
|
||||
"padraic/humbug_get_contents": "<1.1.2",
|
||||
"pagarme/pagarme-php": ">=0,<3",
|
||||
"paragonie/random_compat": "<2",
|
||||
"paypal/merchant-sdk-php": "<3.12",
|
||||
"pear/archive_tar": "<1.4.4",
|
||||
"phpmailer/phpmailer": ">=5,<5.2.27|>=6,<6.0.6",
|
||||
"phpoffice/phpexcel": "<=1.8.1",
|
||||
"phpoffice/phpspreadsheet": "<=1.5",
|
||||
"phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3",
|
||||
"phpwhois/phpwhois": "<=4.2.5",
|
||||
"phpxmlrpc/extras": "<0.6.1",
|
||||
"propel/propel": ">=2.0.0-alpha1,<=2.0.0-alpha7",
|
||||
"propel/propel1": ">=1,<=1.7.1",
|
||||
"pusher/pusher-php-server": "<2.2.1",
|
||||
"robrichards/xmlseclibs": ">=1,<3.0.2",
|
||||
"sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9",
|
||||
"sensiolabs/connect": "<4.2.3",
|
||||
"serluck/phpwhois": "<=4.2.6",
|
||||
"shopware/shopware": "<5.3.7",
|
||||
"silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11",
|
||||
"silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3",
|
||||
"silverstripe/framework": ">=3,<3.3",
|
||||
"silverstripe/userforms": "<3",
|
||||
"simple-updates/phpwhois": "<=1",
|
||||
"simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4",
|
||||
"simplesamlphp/simplesamlphp": "<1.16.3",
|
||||
"simplesamlphp/simplesamlphp-module-infocard": "<1.0.1",
|
||||
"slim/slim": "<2.6",
|
||||
"smarty/smarty": "<3.1.33",
|
||||
"socalnick/scn-social-auth": "<1.15.2",
|
||||
"spoonity/tcpdf": "<6.2.22",
|
||||
"squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1",
|
||||
"stormpath/sdk": ">=0,<9.9.99",
|
||||
"swiftmailer/swiftmailer": ">=4,<5.4.5",
|
||||
"sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2",
|
||||
"sylius/sylius": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2",
|
||||
"symfony/dependency-injection": ">=2,<2.0.17",
|
||||
"symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1",
|
||||
"symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2",
|
||||
"symfony/http-foundation": ">=2,<2.7.49|>=2.8,<2.8.44|>=3,<3.3.18|>=3.4,<3.4.14|>=4,<4.0.14|>=4.1,<4.1.3",
|
||||
"symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8",
|
||||
"symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13",
|
||||
"symfony/polyfill": ">=1,<1.10",
|
||||
"symfony/polyfill-php55": ">=1,<1.10",
|
||||
"symfony/routing": ">=2,<2.0.19",
|
||||
"symfony/security": ">=2,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.19|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1",
|
||||
"symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
|
||||
"symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7",
|
||||
"symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
|
||||
"symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
|
||||
"symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1",
|
||||
"symfony/serializer": ">=2,<2.0.11",
|
||||
"symfony/symfony": ">=2,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1",
|
||||
"symfony/translation": ">=2,<2.0.17",
|
||||
"symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3",
|
||||
"symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4",
|
||||
"symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7",
|
||||
"tecnickcom/tcpdf": "<6.2.22",
|
||||
"thelia/backoffice-default-template": ">=2.1,<2.1.2",
|
||||
"thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2",
|
||||
"theonedemon/phpwhois": "<=4.2.5",
|
||||
"titon/framework": ">=0,<9.9.99",
|
||||
"truckersmp/phpwhois": "<=4.3.1",
|
||||
"twig/twig": "<1.20",
|
||||
"typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.21|>=9,<9.5.2",
|
||||
"typo3/cms-core": ">=8,<8.7.21|>=9,<9.5.2",
|
||||
"typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5",
|
||||
"typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4",
|
||||
"ua-parser/uap-php": "<3.8",
|
||||
"wallabag/tcpdf": "<6.2.22",
|
||||
"willdurand/js-translation-bundle": "<2.1.1",
|
||||
"yiisoft/yii": ">=1.1.14,<1.1.15",
|
||||
"yiisoft/yii2": "<2.0.15",
|
||||
"yiisoft/yii2-bootstrap": "<2.0.4",
|
||||
"yiisoft/yii2-dev": "<2.0.15",
|
||||
"yiisoft/yii2-elasticsearch": "<2.0.5",
|
||||
"yiisoft/yii2-gii": "<2.0.4",
|
||||
"yiisoft/yii2-jui": "<2.0.4",
|
||||
"yiisoft/yii2-redis": "<2.0.8",
|
||||
"zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3",
|
||||
"zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2",
|
||||
"zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2",
|
||||
"zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5",
|
||||
"zendframework/zend-diactoros": ">=1,<1.8.4",
|
||||
"zendframework/zend-feed": ">=1,<2.10.3",
|
||||
"zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1",
|
||||
"zendframework/zend-http": ">=1,<2.8.1",
|
||||
"zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6",
|
||||
"zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3",
|
||||
"zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2",
|
||||
"zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1",
|
||||
"zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4",
|
||||
"zendframework/zend-validator": ">=2.3,<2.3.6",
|
||||
"zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1",
|
||||
"zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6",
|
||||
"zendframework/zendframework": "<2.5.1",
|
||||
"zendframework/zendframework1": "<1.12.20",
|
||||
"zendframework/zendopenid": ">=2,<2.0.2",
|
||||
"zendframework/zendxml": ">=1,<1.0.1",
|
||||
"zetacomponents/mail": "<1.8.2",
|
||||
"zf-commons/zfc-user": "<1.2.2",
|
||||
"zfcampus/zf-apigility-doctrine": ">=1,<1.0.3",
|
||||
"zfr/zfr-oauth2-server-module": "<0.1.2"
|
||||
},
|
||||
"type": "metapackage",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marco Pivetta",
|
||||
"email": "ocramius@gmail.com",
|
||||
"role": "maintainer"
|
||||
}
|
||||
],
|
||||
"description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it",
|
||||
"time": "2019-01-15T19:39:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit-reverse-lookup",
|
||||
"version": "1.0.1",
|
||||
|
@ -2102,16 +2306,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v3.4.22",
|
||||
"version": "v3.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "069bf3f0e8f871a2169a06e43d9f3f03f355e9be"
|
||||
"reference": "a700b874d3692bc8342199adfb6d3b99f62cc61a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/069bf3f0e8f871a2169a06e43d9f3f03f355e9be",
|
||||
"reference": "069bf3f0e8f871a2169a06e43d9f3f03f355e9be",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/a700b874d3692bc8342199adfb6d3b99f62cc61a",
|
||||
"reference": "a700b874d3692bc8342199adfb6d3b99f62cc61a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2170,20 +2374,20 @@
|
|||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-01-25T10:42:12+00:00"
|
||||
"time": "2019-01-04T04:42:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
"version": "v3.4.22",
|
||||
"version": "v3.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/debug.git",
|
||||
"reference": "667a26c4dd6bc75c67f06bc9bcd015bdecc7cbb8"
|
||||
"reference": "26d7f23b9bd0b93bee5583e4d6ca5cb1ab31b186"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/667a26c4dd6bc75c67f06bc9bcd015bdecc7cbb8",
|
||||
"reference": "667a26c4dd6bc75c67f06bc9bcd015bdecc7cbb8",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/26d7f23b9bd0b93bee5583e4d6ca5cb1ab31b186",
|
||||
"reference": "26d7f23b9bd0b93bee5583e4d6ca5cb1ab31b186",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2226,20 +2430,20 @@
|
|||
],
|
||||
"description": "Symfony Debug Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-01-25T10:19:25+00:00"
|
||||
"time": "2019-01-01T13:45:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v3.4.22",
|
||||
"version": "v3.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "7c0c627220308928e958a87c293108e5891cde1d"
|
||||
"reference": "3f2a2ab6315dd7682d4c16dcae1e7b95c8b8555e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/7c0c627220308928e958a87c293108e5891cde1d",
|
||||
"reference": "7c0c627220308928e958a87c293108e5891cde1d",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/3f2a2ab6315dd7682d4c16dcae1e7b95c8b8555e",
|
||||
"reference": "3f2a2ab6315dd7682d4c16dcae1e7b95c8b8555e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2275,7 +2479,7 @@
|
|||
],
|
||||
"description": "Symfony Finder Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-01-16T13:43:35+00:00"
|
||||
"time": "2019-01-01T13:45:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
|
@ -2396,16 +2600,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v3.4.22",
|
||||
"version": "v3.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "ba11776e9e6c15ad5759a07bffb15899bac75c2d"
|
||||
"reference": "554a59a1ccbaac238a89b19c8e551a556fd0e2ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/ba11776e9e6c15ad5759a07bffb15899bac75c2d",
|
||||
"reference": "ba11776e9e6c15ad5759a07bffb15899bac75c2d",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/554a59a1ccbaac238a89b19c8e551a556fd0e2ea",
|
||||
"reference": "554a59a1ccbaac238a89b19c8e551a556fd0e2ea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2451,7 +2655,7 @@
|
|||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-01-16T10:59:17+00:00"
|
||||
"time": "2019-01-01T13:45:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/fdomdocument",
|
||||
|
@ -2548,12 +2752,15 @@
|
|||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"pubsubhubbub/publisher": 20
|
||||
"pubsubhubbub/publisher": 20,
|
||||
"roave/security-advisories": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=5.6"
|
||||
"php": ">=5.6",
|
||||
"ext-json": "*",
|
||||
"ext-zlib": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
|
|
|
@ -22,7 +22,8 @@ See [REST API](REST-API) for a list of official and community clients.
|
|||
- [shaarli2twitter](https://github.com/ArthurHoaro/shaarli2twitter) by [@ArthurHoaro](https://github.com/ArthurHoaro) - Automatically tweet your shared links from Shaarli
|
||||
- [shaarli2mastodon](https://github.com/kalvn/shaarli2mastodon) by [@kalvn](https://github.com/kalvn) - This Shaarli plugin allows you to automatically publish links you post on your Mastodon timeline.
|
||||
- [shaarli-descriptor](https://github.com/immanuelfodor/shaarli-descriptor) by [@immanuelfodor](https://github.com/immanuelfodor) - Customize the default height/number of rows of the Description field when editing a link.
|
||||
|
||||
- [urlextern](https://github.com/trailjeep/shaarli-urlextern) by [@trailjeep](https://github.com/trailjeep) - Shaarli plugin to open external links in a new tab/window.
|
||||
- [favicons](https://github.com/trailjeep/shaarli-favicons) by [@trailjeep](https://github.com/trailjeep) - Shaarli plugin to add favicon/filetype icons to links.
|
||||
|
||||
### Third-party themes
|
||||
See [Theming](Theming) for a list of community-contributed themes, and an installation guide.
|
||||
|
|
|
@ -24,11 +24,11 @@ Using one of the following methods:
|
|||
|
||||
In most cases, you should download the latest Shaarli release from the [releases](https://github.com/shaarli/Shaarli/releases) page. Download our **shaarli-full** archive to include dependencies.
|
||||
|
||||
The current latest released version is `v0.9.7`
|
||||
The current latest released version is `v0.10.4`
|
||||
|
||||
```bash
|
||||
$ wget https://github.com/shaarli/Shaarli/releases/download/v0.9.7/shaarli-v0.9.7-full.zip
|
||||
$ unzip shaarli-v0.9.7-full.zip
|
||||
$ wget https://github.com/shaarli/Shaarli/releases/download/v0.10.4/shaarli-v0.10.4-full.zip
|
||||
$ unzip shaarli-v0.10.4-full.zip
|
||||
$ mv Shaarli /path/to/shaarli/
|
||||
```
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ If it's still not working, please [open an issue](https://github.com/shaarli/Sha
|
|||
| [render_feed](#render_feed) | Allow to do add tags in RSS and ATOM feeds. |
|
||||
| [save_link](#save_link) | Allow to alter the link being saved in the datastore. |
|
||||
| [delete_link](#delete_link) | Allow to do an action before a link is deleted from the datastore. |
|
||||
| [save_plugin_parameters](#save_plugin_parameters) | Allow to manipulate plugin parameters before they're saved. |
|
||||
|
||||
|
||||
|
||||
|
@ -471,6 +472,22 @@ Allow to execute any action before the link is actually removed from the datasto
|
|||
- created
|
||||
- updated
|
||||
|
||||
|
||||
#### save_plugin_parameters
|
||||
|
||||
Triggered when the plugin parameters are saved from the plugin administration page.
|
||||
|
||||
Plugins can perform an action every times their settings are updated.
|
||||
For example it is used to update the CSS file of the `default_colors` plugins.
|
||||
|
||||
##### Data
|
||||
|
||||
`$data` input contains the `$_POST` array.
|
||||
|
||||
So if the plugin has a parameter called `MYPLUGIN_PARAMETER`,
|
||||
the array will contain an entry with `MYPLUGIN_PARAMETER` as a key.
|
||||
|
||||
|
||||
## Guide for template designer
|
||||
|
||||
### Plugin administration
|
||||
|
|
|
@ -63,8 +63,12 @@ Usage of each plugin is documented in it's README file:
|
|||
|
||||
* `addlink-toolbar`: Adds the addlink input on the linklist page
|
||||
* `archiveorg`: For each link, add an Archive.org icon
|
||||
* `default_colors`: Override default theme colors.
|
||||
* `isso`: Let visitor comment your shaares on permalinks with Isso.
|
||||
* [`markdown`](https://github.com/shaarli/Shaarli/blob/master/plugins/markdown/README.md): Render shaare description with Markdown syntax.
|
||||
* `piwik`: A plugin that adds Piwik tracking code to Shaarli pages.
|
||||
* [`playvideos`](https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md): Add a button in the toolbar allowing to watch all videos.
|
||||
* `pubsubhubbub`: Enable PubSubHubbub feed publishing
|
||||
* `qrcode`: For each link, add a QRCode icon.
|
||||
* [`wallabag`](https://github.com/shaarli/Shaarli/blob/master/plugins/wallabag/README.md): For each link, add a Wallabag icon to save it in your instance.
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ Version | Status | Shaarli compatibility
|
|||
:---:|:---:|:---:
|
||||
7.2 | Supported | Yes
|
||||
7.1 | Supported | Yes
|
||||
7.0 | Supported | Yes
|
||||
7.0 | EOL: 2018-12-03 | Yes (up to Shaarli 0.10.x)
|
||||
5.6 | EOL: 2018-12-31 | Yes (up to Shaarli 0.10.x)
|
||||
5.5 | EOL: 2016-07-10 | Yes
|
||||
5.4 | EOL: 2015-09-14 | Yes (up to Shaarli 0.8.x)
|
||||
|
@ -404,6 +404,8 @@ If Shaarli is served behind a proxy (i.e. there is a proxy server between client
|
|||
- `X-Forwarded-Host`
|
||||
- `X-Forwarded-For`
|
||||
|
||||
In you [Shaarli configuration](Shaarli-configuration) `data/config.json.php`, add the public IP of your proxy under `security.trusted_proxies`.
|
||||
|
||||
See also [proxy-related](https://github.com/shaarli/Shaarli/issues?utf8=%E2%9C%93&q=label%3Aproxy+) issues.
|
||||
|
||||
## Robots and crawlers
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
Once your Shaarli instance is installed, the file `data/config.json.php` is generated:
|
||||
* it contains all settings in JSON format, and can be edited to customize values
|
||||
* it defines which [plugins](Plugin-System) are enabled[](.html)
|
||||
* it defines which [plugins](Plugin-System) are enabled
|
||||
* its values override those defined in `index.php`
|
||||
* it is wrap in a PHP comment to prevent anyone accessing it, regardless of server configuration
|
||||
|
||||
|
@ -32,13 +32,13 @@ On a Linux distribution:
|
|||
- to give it access to Shaarli, either:
|
||||
- unzip Shaarli in the default web server location (usually `/var/www/`) and set the web server user as the owner
|
||||
- put users in the same group as the web server, and set the appropriate access rights
|
||||
- if you have a domain / subdomain to serve Shaarli, [configure the server](Server-configuration) accordingly[](.html)
|
||||
- if you have a domain / subdomain to serve Shaarli, [configure the server](Server-configuration) accordingly
|
||||
|
||||
## Configuration
|
||||
|
||||
In `data/config.json.php`.
|
||||
|
||||
See also [Plugin System](Plugin-System.html).
|
||||
See also [Plugin System](Plugin-System).
|
||||
|
||||
### Credentials
|
||||
|
||||
|
@ -56,6 +56,8 @@ _These settings should not be edited_
|
|||
- **timezone**: See [the list of supported timezones](http://php.net/manual/en/timezones.php).
|
||||
- **enabled_plugins**: List of enabled plugins.
|
||||
- **default_note_title**: Default title of a new note.
|
||||
- **retrieve_description** (boolean): If set to true, for every new links Shaarli will try
|
||||
to retrieve the description and keywords from the HTML meta tags.
|
||||
|
||||
### Security
|
||||
|
||||
|
@ -120,11 +122,6 @@ Must be an associative array: `translation domain => translation path`.
|
|||
- **enable_thumbnails**: Enable or disable thumbnail display.
|
||||
- **enable_localcache**: Enable or disable local cache.
|
||||
|
||||
### Redirector
|
||||
|
||||
- **url**: Redirector URL, such as `anonym.to`.
|
||||
- **encode_url**: Enable this if the redirector needs encoded URL to work properly.
|
||||
|
||||
## Configuration file example
|
||||
|
||||
```json
|
||||
|
@ -185,8 +182,6 @@ Must be an associative array: `translation domain => translation path`.
|
|||
"hide_public_links": false,
|
||||
"hide_timestamps": false,
|
||||
"open_shaarli": false,
|
||||
"redirector": "http://anonym.to/?",
|
||||
"redirector_encode_url": false
|
||||
},
|
||||
"general": {
|
||||
"header_link": "?",
|
||||
|
@ -218,10 +213,6 @@ Must be an associative array: `translation domain => translation path`.
|
|||
"enable_thumbnails": true,
|
||||
"enable_localcache": true
|
||||
},
|
||||
"redirector": {
|
||||
"url": "http://anonym.to/?",
|
||||
"encode_url": false
|
||||
},
|
||||
"plugins": {
|
||||
"WALLABAG_URL": "http://demo.wallabag.org",
|
||||
"WALLABAG_VERSION": "1"
|
||||
|
|
File diff suppressed because it is too large
Load diff
156
index.php
156
index.php
|
@ -56,31 +56,33 @@
|
|||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// Shaarli library
|
||||
require_once 'application/ApplicationUtils.php';
|
||||
require_once 'application/Cache.php';
|
||||
require_once 'application/CachedPage.php';
|
||||
require_once 'application/bookmark/LinkUtils.php';
|
||||
require_once 'application/config/ConfigPlugin.php';
|
||||
require_once 'application/FeedBuilder.php';
|
||||
require_once 'application/feed/Cache.php';
|
||||
require_once 'application/http/HttpUtils.php';
|
||||
require_once 'application/http/UrlUtils.php';
|
||||
require_once 'application/updater/UpdaterUtils.php';
|
||||
require_once 'application/FileUtils.php';
|
||||
require_once 'application/History.php';
|
||||
require_once 'application/HttpUtils.php';
|
||||
require_once 'application/LinkDB.php';
|
||||
require_once 'application/LinkFilter.php';
|
||||
require_once 'application/LinkUtils.php';
|
||||
require_once 'application/NetscapeBookmarkUtils.php';
|
||||
require_once 'application/PageBuilder.php';
|
||||
require_once 'application/TimeZone.php';
|
||||
require_once 'application/Url.php';
|
||||
require_once 'application/Utils.php';
|
||||
require_once 'application/PluginManager.php';
|
||||
require_once 'application/Router.php';
|
||||
require_once 'application/Updater.php';
|
||||
|
||||
use \Shaarli\ApplicationUtils;
|
||||
use \Shaarli\Bookmark\Exception\LinkNotFoundException;
|
||||
use \Shaarli\Bookmark\LinkDB;
|
||||
use \Shaarli\Config\ConfigManager;
|
||||
use \Shaarli\Feed\CachedPage;
|
||||
use \Shaarli\Feed\FeedBuilder;
|
||||
use \Shaarli\History;
|
||||
use \Shaarli\Languages;
|
||||
use \Shaarli\Netscape\NetscapeBookmarkUtils;
|
||||
use \Shaarli\Plugin\PluginManager;
|
||||
use \Shaarli\Render\PageBuilder;
|
||||
use \Shaarli\Render\ThemeUtils;
|
||||
use \Shaarli\Router;
|
||||
use \Shaarli\Security\LoginManager;
|
||||
use \Shaarli\Security\SessionManager;
|
||||
use \Shaarli\ThemeUtils;
|
||||
use \Shaarli\Thumbnailer;
|
||||
use \Shaarli\Updater\Updater;
|
||||
|
||||
// Ensure the PHP version is supported
|
||||
try {
|
||||
|
@ -123,7 +125,7 @@
|
|||
|
||||
$conf = new ConfigManager();
|
||||
$sessionManager = new SessionManager($_SESSION, $conf);
|
||||
$loginManager = new LoginManager($GLOBALS, $conf, $sessionManager);
|
||||
$loginManager = new LoginManager($conf, $sessionManager);
|
||||
$loginManager->generateStaySignedInToken($_SERVER['REMOTE_ADDR']);
|
||||
$clientIpId = client_ip_id($_SERVER);
|
||||
|
||||
|
@ -310,9 +312,7 @@ function showDailyRSS($conf, $loginManager)
|
|||
$LINKSDB = new LinkDB(
|
||||
$conf->get('resource.datastore'),
|
||||
$loginManager->isLoggedIn(),
|
||||
$conf->get('privacy.hide_public_links'),
|
||||
$conf->get('redirector.url'),
|
||||
$conf->get('redirector.encode_url')
|
||||
$conf->get('privacy.hide_public_links')
|
||||
);
|
||||
|
||||
/* Some Shaarlies may have very few links, so we need to look
|
||||
|
@ -354,13 +354,9 @@ function showDailyRSS($conf, $loginManager)
|
|||
|
||||
// We pre-format some fields for proper output.
|
||||
foreach ($links as &$link) {
|
||||
$link['formatedDescription'] = format_description(
|
||||
$link['description'],
|
||||
$conf->get('redirector.url'),
|
||||
$conf->get('redirector.encode_url')
|
||||
);
|
||||
$link['formatedDescription'] = format_description($link['description']);
|
||||
$link['timestamp'] = $link['created']->getTimestamp();
|
||||
if (startsWith($link['url'], '?')) {
|
||||
if (is_note($link['url'])) {
|
||||
$link['url'] = index_url($_SERVER) . $link['url']; // make permalink URL absolute
|
||||
}
|
||||
}
|
||||
|
@ -396,9 +392,16 @@ function showDailyRSS($conf, $loginManager)
|
|||
*/
|
||||
function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
|
||||
{
|
||||
$day = date('Ymd', strtotime('-1 day')); // Yesterday, in format YYYYMMDD.
|
||||
if (isset($_GET['day'])) {
|
||||
$day = $_GET['day'];
|
||||
if ($day === date('Ymd', strtotime('now'))) {
|
||||
$pageBuilder->assign('dayDesc', t('Today'));
|
||||
} elseif ($day === date('Ymd', strtotime('-1 days'))) {
|
||||
$pageBuilder->assign('dayDesc', t('Yesterday'));
|
||||
}
|
||||
} else {
|
||||
$day = date('Ymd', strtotime('now')); // Today, in format YYYYMMDD.
|
||||
$pageBuilder->assign('dayDesc', t('Today'));
|
||||
}
|
||||
|
||||
$days = $LINKSDB->days();
|
||||
|
@ -431,11 +434,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
|
|||
$taglist = explode(' ', $link['tags']);
|
||||
uasort($taglist, 'strcasecmp');
|
||||
$linksToDisplay[$key]['taglist']=$taglist;
|
||||
$linksToDisplay[$key]['formatedDescription'] = format_description(
|
||||
$link['description'],
|
||||
$conf->get('redirector.url'),
|
||||
$conf->get('redirector.encode_url')
|
||||
);
|
||||
$linksToDisplay[$key]['formatedDescription'] = format_description($link['description']);
|
||||
$linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp();
|
||||
}
|
||||
|
||||
|
@ -1044,6 +1043,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
|
|||
$conf->set('general.timezone', $tz);
|
||||
$conf->set('general.title', escape($_POST['title']));
|
||||
$conf->set('general.header_link', escape($_POST['titleLink']));
|
||||
$conf->set('general.retrieve_description', !empty($_POST['retrieveDescription']));
|
||||
$conf->set('resource.theme', escape($_POST['theme']));
|
||||
$conf->set('security.session_protection_disabled', !empty($_POST['disablesessionprotection']));
|
||||
$conf->set('privacy.default_private_links', !empty($_POST['privateLinkByDefault']));
|
||||
|
@ -1092,6 +1092,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
|
|||
);
|
||||
$PAGE->assign('continents', $continents);
|
||||
$PAGE->assign('cities', $cities);
|
||||
$PAGE->assign('retrieve_description', $conf->get('general.retrieve_description'));
|
||||
$PAGE->assign('private_links_default', $conf->get('privacy.default_private_links', false));
|
||||
$PAGE->assign('session_protection_disabled', $conf->get('security.session_protection_disabled', false));
|
||||
$PAGE->assign('enable_rss_permalinks', $conf->get('feed.rss_permalinks', false));
|
||||
|
@ -1100,7 +1101,6 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
|
|||
$PAGE->assign('api_enabled', $conf->get('api.enabled', true));
|
||||
$PAGE->assign('api_secret', $conf->get('api.secret'));
|
||||
$PAGE->assign('languages', Languages::getAvailableLanguages());
|
||||
$PAGE->assign('language', $conf->get('translation.language'));
|
||||
$PAGE->assign('gd_enabled', extension_loaded('gd'));
|
||||
$PAGE->assign('thumbnails_mode', $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE));
|
||||
$PAGE->assign('pagetitle', t('Configure') .' - '. $conf->get('general.title', 'Shaarli'));
|
||||
|
@ -1154,22 +1154,24 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
|
|||
|
||||
// lf_id should only be present if the link exists.
|
||||
$id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : $LINKSDB->getNextId();
|
||||
$link['id'] = $id;
|
||||
// Linkdate is kept here to:
|
||||
// - use the same permalink for notes as they're displayed when creating them
|
||||
// - let users hack creation date of their posts
|
||||
// See: https://shaarli.readthedocs.io/en/master/guides/various-hacks/#changing-the-timestamp-for-a-shaare
|
||||
$linkdate = escape($_POST['lf_linkdate']);
|
||||
$link['created'] = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate);
|
||||
if (isset($LINKSDB[$id])) {
|
||||
// Edit
|
||||
$created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate);
|
||||
$updated = new DateTime();
|
||||
$shortUrl = $LINKSDB[$id]['shorturl'];
|
||||
$link['updated'] = new DateTime();
|
||||
$link['shorturl'] = $LINKSDB[$id]['shorturl'];
|
||||
$link['sticky'] = isset($LINKSDB[$id]['sticky']) ? $LINKSDB[$id]['sticky'] : false;
|
||||
$new = false;
|
||||
} else {
|
||||
// New link
|
||||
$created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate);
|
||||
$updated = null;
|
||||
$shortUrl = link_small_hash($created, $id);
|
||||
$link['updated'] = null;
|
||||
$link['shorturl'] = link_small_hash($link['created'], $id);
|
||||
$link['sticky'] = false;
|
||||
$new = true;
|
||||
}
|
||||
|
||||
|
@ -1185,24 +1187,22 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
|
|||
}
|
||||
$url = whitelist_protocols(trim($_POST['lf_url']), $conf->get('security.allowed_protocols'));
|
||||
|
||||
$link = array(
|
||||
'id' => $id,
|
||||
$link = array_merge($link, [
|
||||
'title' => trim($_POST['lf_title']),
|
||||
'url' => $url,
|
||||
'description' => $_POST['lf_description'],
|
||||
'private' => (isset($_POST['lf_private']) ? 1 : 0),
|
||||
'created' => $created,
|
||||
'updated' => $updated,
|
||||
'tags' => str_replace(',', ' ', $tags),
|
||||
'shorturl' => $shortUrl,
|
||||
);
|
||||
]);
|
||||
|
||||
// If title is empty, use the URL as title.
|
||||
if ($link['title'] == '') {
|
||||
$link['title'] = $link['url'];
|
||||
}
|
||||
|
||||
if ($conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE) {
|
||||
if ($conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE
|
||||
&& ! is_note($link['url'])
|
||||
) {
|
||||
$thumbnailer = new Thumbnailer($conf);
|
||||
$link['thumbnail'] = $thumbnailer->get($url);
|
||||
}
|
||||
|
@ -1301,6 +1301,51 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
|
|||
exit;
|
||||
}
|
||||
|
||||
// -------- User clicked either "Set public" or "Set private" bulk operation
|
||||
if ($targetPage == Router::$PAGE_CHANGE_VISIBILITY) {
|
||||
if (! $sessionManager->checkToken($_GET['token'])) {
|
||||
die(t('Wrong token.'));
|
||||
}
|
||||
|
||||
$ids = trim($_GET['ids']);
|
||||
if (strpos($ids, ' ') !== false) {
|
||||
// multiple, space-separated ids provided
|
||||
$ids = array_values(array_filter(preg_split('/\s+/', escape($ids))));
|
||||
} else {
|
||||
// only a single id provided
|
||||
$ids = [$ids];
|
||||
}
|
||||
|
||||
// assert at least one id is given
|
||||
if (!count($ids)) {
|
||||
die('no id provided');
|
||||
}
|
||||
// assert that the visibility is valid
|
||||
if (!isset($_GET['newVisibility']) || !in_array($_GET['newVisibility'], ['public', 'private'])) {
|
||||
die('invalid visibility');
|
||||
} else {
|
||||
$private = $_GET['newVisibility'] === 'private';
|
||||
}
|
||||
foreach ($ids as $id) {
|
||||
$id = (int) escape($id);
|
||||
$link = $LINKSDB[$id];
|
||||
$link['private'] = $private;
|
||||
$pluginManager->executeHooks('save_link', $link);
|
||||
$LINKSDB[$id] = $link;
|
||||
}
|
||||
$LINKSDB->save($conf->get('resource.page_cache')); // save to disk
|
||||
|
||||
$location = '?';
|
||||
if (isset($_SERVER['HTTP_REFERER'])) {
|
||||
$location = generateLocation(
|
||||
$_SERVER['HTTP_REFERER'],
|
||||
$_SERVER['HTTP_HOST']
|
||||
);
|
||||
}
|
||||
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'])) {
|
||||
$id = (int) escape($_GET['edit_link']);
|
||||
|
@ -1347,13 +1392,14 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
|
|||
// 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) {
|
||||
$retrieveDescription = $conf->get('general.retrieve_description');
|
||||
// Short timeout to keep the application responsive
|
||||
// The callback will fill $charset and $title with data from the downloaded page.
|
||||
get_http_response(
|
||||
$url,
|
||||
$conf->get('general.download_timeout', 30),
|
||||
$conf->get('general.download_max_size', 4194304),
|
||||
get_curl_download_callback($charset, $title)
|
||||
get_curl_download_callback($charset, $title, $description, $tags, $retrieveDescription)
|
||||
);
|
||||
if (! empty($title) && strtolower($charset) != 'utf-8') {
|
||||
$title = mb_convert_encoding($title, 'utf-8', $charset);
|
||||
|
@ -1547,6 +1593,7 @@ function ($a, $b) {
|
|||
if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) {
|
||||
try {
|
||||
if (isset($_POST['parameters_form'])) {
|
||||
$pluginManager->executeHooks('save_plugin_parameters', $_POST);
|
||||
unset($_POST['parameters_form']);
|
||||
foreach ($_POST as $param => $value) {
|
||||
$conf->set('plugins.'. $param, escape($value));
|
||||
|
@ -1586,7 +1633,7 @@ function ($a, $b) {
|
|||
$ids = [];
|
||||
foreach ($LINKSDB as $link) {
|
||||
// A note or not HTTP(S)
|
||||
if ($link['url'][0] === '?' || ! startsWith(strtolower($link['url']), 'http')) {
|
||||
if (is_note($link['url']) || ! startsWith(strtolower($link['url']), 'http')) {
|
||||
continue;
|
||||
}
|
||||
$ids[] = $link['id'];
|
||||
|
@ -1690,11 +1737,7 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
|
|||
$linkDisp = array();
|
||||
while ($i<$end && $i<count($keys)) {
|
||||
$link = $linksToDisplay[$keys[$i]];
|
||||
$link['description'] = format_description(
|
||||
$link['description'],
|
||||
$conf->get('redirector.url'),
|
||||
$conf->get('redirector.encode_url')
|
||||
);
|
||||
$link['description'] = format_description($link['description']);
|
||||
$classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight';
|
||||
$link['class'] = $link['private'] == 0 ? $classLi : 'private';
|
||||
$link['timestamp'] = $link['created']->getTimestamp();
|
||||
|
@ -1755,7 +1798,6 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
|
|||
'search_term' => $searchterm,
|
||||
'search_tags' => $searchtags,
|
||||
'visibility' => ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '',
|
||||
'redirector' => $conf->get('redirector.url'), // Optional redirector URL.
|
||||
'links' => $linkDisp,
|
||||
);
|
||||
|
||||
|
@ -1905,9 +1947,7 @@ function install($conf, $sessionManager, $loginManager)
|
|||
$linkDb = new LinkDB(
|
||||
$conf->get('resource.datastore'),
|
||||
$loginManager->isLoggedIn(),
|
||||
$conf->get('privacy.hide_public_links'),
|
||||
$conf->get('redirector.url'),
|
||||
$conf->get('redirector.encode_url')
|
||||
$conf->get('privacy.hide_public_links')
|
||||
);
|
||||
|
||||
$container = new \Slim\Container();
|
||||
|
@ -1930,7 +1970,7 @@ function install($conf, $sessionManager, $loginManager)
|
|||
$this->put('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:putTag')->setName('putTag');
|
||||
$this->delete('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:deleteTag')->setName('deleteTag');
|
||||
|
||||
$this->get('/history', '\Shaarli\Api\Controllers\History:getHistory')->setName('getHistory');
|
||||
$this->get('/history', '\Shaarli\Api\Controllers\HistoryController:getHistory')->setName('getHistory');
|
||||
})->add('\Shaarli\Api\ApiMiddleware');
|
||||
|
||||
$response = $app->run(true);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"dependencies": {
|
||||
"awesomplete": "^1.1.2",
|
||||
"blazy": "^1.8.2",
|
||||
"font-awesome": "^4.7.0",
|
||||
"fork-awesome": "^1.1.7",
|
||||
"pure-extras": "^1.0.0",
|
||||
"purecss": "^1.0.0"
|
||||
},
|
||||
|
@ -21,7 +21,7 @@
|
|||
"eslint-plugin-import": "^2.8.0",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^1.1.6",
|
||||
"node-sass": "^4.7.2",
|
||||
"node-sass": "^4.12.0",
|
||||
"sass-lint": "^1.12.1",
|
||||
"sass-loader": "^6.0.6",
|
||||
"style-loader": "^0.19.1",
|
||||
|
|
21
phpdoc.dist.xml
Normal file
21
phpdoc.dist.xml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<phpdoc>
|
||||
<title><![CDATA[Shaarli API Reference]]></title>
|
||||
<parser>
|
||||
<encoding>utf8</encoding>
|
||||
<markers>
|
||||
<item>FIXME</item>
|
||||
<item>TODO</item>
|
||||
<item>WIP</item>
|
||||
</markers>
|
||||
<target>doc/phpdoc</target>
|
||||
</parser>
|
||||
<transformer>
|
||||
<target>doc/phpdoc</target>
|
||||
</transformer>
|
||||
<files>
|
||||
<file>index.php</file>
|
||||
<directory>application</directory>
|
||||
<directory>plugins</directory>
|
||||
</files>
|
||||
</phpdoc>
|
|
@ -5,6 +5,8 @@
|
|||
* Adds the addlink input on the linklist page.
|
||||
*/
|
||||
|
||||
use Shaarli\Router;
|
||||
|
||||
/**
|
||||
* When linklist is displayed, add play videos to header's toolbar.
|
||||
*
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* Add an icon in the link list for archive.org.
|
||||
*/
|
||||
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
|
||||
/**
|
||||
* Add archive.org icon to link_plugin when rendering linklist.
|
||||
*
|
||||
|
|
3
plugins/default_colors/default_colors.css.template
Normal file
3
plugins/default_colors/default_colors.css.template
Normal file
|
@ -0,0 +1,3 @@
|
|||
:root {
|
||||
%s
|
||||
}
|
5
plugins/default_colors/default_colors.meta
Normal file
5
plugins/default_colors/default_colors.meta
Normal file
|
@ -0,0 +1,5 @@
|
|||
description="Override default theme colors. Use any CSS valid color."
|
||||
parameters="DEFAULT_COLORS_MAIN;DEFAULT_COLORS_BACKGROUND;DEFAULT_COLORS_DARK_MAIN"
|
||||
parameter.DEFAULT_COLORS_MAIN="Main color (navbar green)"
|
||||
parameter.DEFAULT_COLORS_BACKGROUND="Background color (light grey)"
|
||||
parameter.DEFAULT_COLORS_DARK_MAIN="Dark main color (e.g. visited links)"
|
111
plugins/default_colors/default_colors.php
Normal file
111
plugins/default_colors/default_colors.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Plugin default_colors.
|
||||
*
|
||||
* Allow users to easily overrides colors of the default theme.
|
||||
*/
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
|
||||
const DEFAULT_COLORS_PLACEHOLDERS = [
|
||||
'DEFAULT_COLORS_MAIN',
|
||||
'DEFAULT_COLORS_BACKGROUND',
|
||||
'DEFAULT_COLORS_DARK_MAIN',
|
||||
];
|
||||
|
||||
/**
|
||||
* Display an error if the plugin is active a no color is configured.
|
||||
*
|
||||
* @param $conf ConfigManager instance
|
||||
*
|
||||
* @return array|null The errors array or null of there is none.
|
||||
*/
|
||||
function default_colors_init($conf)
|
||||
{
|
||||
$params = '';
|
||||
foreach (DEFAULT_COLORS_PLACEHOLDERS as $placeholder) {
|
||||
$params .= trim($conf->get('plugins.'. $placeholder, ''));
|
||||
}
|
||||
|
||||
if (empty($params)) {
|
||||
$error = t('Default colors plugin error: '.
|
||||
'This plugin is active and no custom color is configured.');
|
||||
return array($error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When plugin parameters are saved, we regenerate the custom CSS file with provided settings.
|
||||
*
|
||||
* @param array $data $_POST array
|
||||
*
|
||||
* @return array Updated $_POST array
|
||||
*/
|
||||
function hook_default_colors_save_plugin_parameters($data)
|
||||
{
|
||||
$file = PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css';
|
||||
$template = file_get_contents(PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css.template');
|
||||
$content = '';
|
||||
foreach (DEFAULT_COLORS_PLACEHOLDERS as $rule) {
|
||||
$content .= ! empty($data[$rule])
|
||||
? default_colors_format_css_rule($data, $rule) .';'. PHP_EOL
|
||||
: '';
|
||||
}
|
||||
|
||||
if (! empty($content)) {
|
||||
file_put_contents($file, sprintf($template, $content));
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* When linklist is displayed, include default_colors CSS file.
|
||||
*
|
||||
* @param array $data - header data.
|
||||
*
|
||||
* @return mixed - header data with default_colors CSS file added.
|
||||
*/
|
||||
function hook_default_colors_render_includes($data)
|
||||
{
|
||||
$file = PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css';
|
||||
if (file_exists($file )) {
|
||||
$data['css_files'][] = $file ;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a valid CSS rule from parameters settings and plugin parameter.
|
||||
*
|
||||
* @param array $data $_POST array
|
||||
* @param string $parameter Plugin parameter name
|
||||
*
|
||||
* @return string CSS rules for the provided parameter and its matching value.
|
||||
*/
|
||||
function default_colors_format_css_rule($data, $parameter)
|
||||
{
|
||||
if (empty($data[$parameter])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$key = str_replace('DEFAULT_COLORS_', '', $parameter);
|
||||
$key = str_replace('_', '-', strtolower($key)) .'-color';
|
||||
return ' --'. $key .': '. $data[$parameter];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function is never called, but contains translation calls for GNU gettext extraction.
|
||||
*/
|
||||
function default_colors_translation()
|
||||
{
|
||||
// meta
|
||||
t('Override default theme colors. Use any CSS valid color.');
|
||||
t('Main color (navbar green)');
|
||||
t('Background color (light grey)');
|
||||
t('Dark main color (e.g. visited links)');
|
||||
}
|
|
@ -1 +1,4 @@
|
|||
description="A demo plugin covering all use cases for template designers and plugin developers."
|
||||
parameters="DEMO_PLUGIN_PARAMETER;DEMO_PLUGIN_OTHER_PARAMETER"
|
||||
parameter.DEMO_PLUGIN_PARAMETER="This is a parameter dedicated to the demo plugin. It'll be suffixed."
|
||||
parameter.DEMO_PLUGIN_OTHER_PARAMETER="Other demo parameter"
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
use Shaarli\Router;
|
||||
|
||||
/**
|
||||
* In the footer hook, there is a working example of a translation extension for Shaarli.
|
||||
|
@ -454,6 +456,23 @@ function hook_demo_plugin_render_feed($data)
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* When plugin parameters are saved.
|
||||
*
|
||||
* @param array $data $_POST array
|
||||
*
|
||||
* @return array Updated $_POST array
|
||||
*/
|
||||
function hook_demo_plugin_save_plugin_parameters($data)
|
||||
{
|
||||
// Here we edit the provided value, but we can use this to generate config files, etc.
|
||||
if (! empty($data['DEMO_PLUGIN_PARAMETER']) && ! endsWith($data['DEMO_PLUGIN_PARAMETER'], '_SUFFIX')) {
|
||||
$data['DEMO_PLUGIN_PARAMETER'] .= '_SUFFIX';
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is never called, but contains translation calls for GNU gettext extraction.
|
||||
*/
|
||||
|
@ -461,4 +480,6 @@ function demo_dummy_translation()
|
|||
{
|
||||
// meta
|
||||
t('A demo plugin covering all use cases for template designers and plugin developers.');
|
||||
t('This is a parameter dedicated to the demo plugin. It\'ll be suffixed.');
|
||||
t('Other demo parameter');
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
use Shaarli\Router;
|
||||
|
||||
/**
|
||||
* Display an error everywhere if the plugin is enabled without configuration.
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
*/
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
use Shaarli\Router;
|
||||
|
||||
/*
|
||||
* If this tag is used on a shaare, the description won't be processed by Parsedown.
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
* Plugin origin
|
||||
*/
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
use Shaarli\Router;
|
||||
|
||||
|
||||
/**
|
||||
* Hook render_editlink.
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* Adds tracking code on each page.
|
||||
*/
|
||||
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
|
||||
/**
|
||||
* Initialization function.
|
||||
* It will be called when the plugin is loaded.
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
* Note: this plugin adds jQuery.
|
||||
*/
|
||||
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
use Shaarli\Router;
|
||||
|
||||
/**
|
||||
* When linklist is displayed, add play videos to header's toolbar.
|
||||
*
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
use pubsubhubbub\publisher\Publisher;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Feed\FeedBuilder;
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
use Shaarli\Router;
|
||||
|
||||
/**
|
||||
* Plugin init function - set the hub to the default appspot one.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="linkqrcode">
|
||||
<a href="http://qrfree.kaywa.com/?l=1&s=8&d=%s" onclick="showQrCode(this); return false;" class="qrcode" data-permalink="%s">
|
||||
<a href="#" onclick="showQrCode(this); return false;" class="qrcode" data-permalink="%s">
|
||||
<img src="%s/qrcode/qrcode.png" class="linklist-plugin-icon" title="QR-Code" alt="QRCode">
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
* Display a QRCode icon in link list.
|
||||
*/
|
||||
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
use Shaarli\Router;
|
||||
|
||||
/**
|
||||
* Add qrcode icon to link_plugin when rendering linklist.
|
||||
*
|
||||
|
@ -19,7 +22,6 @@ function hook_qrcode_render_linklist($data)
|
|||
foreach ($data['links'] as &$value) {
|
||||
$qrcode = sprintf(
|
||||
$qrcode_html,
|
||||
urlencode($value['url']),
|
||||
$value['url'],
|
||||
PluginManager::$PLUGINS_PATH
|
||||
);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
namespace Shaarli\Plugin\Wallabag;
|
||||
|
||||
/**
|
||||
* Class WallabagInstance.
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Plugin Wallabag.
|
||||
* Wallabag plugin
|
||||
*/
|
||||
|
||||
require_once 'WallabagInstance.php';
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Plugin\PluginManager;
|
||||
use Shaarli\Plugin\Wallabag\WallabagInstance;
|
||||
|
||||
/**
|
||||
* Init function, return an error if the server is not set.
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php /* 0.10.4 */ ?>
|
||||
<?php /* 0.11.0 */ ?>
|
||||
|
|
|
@ -1,33 +1,14 @@
|
|||
<?php
|
||||
namespace Shaarli;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
/**
|
||||
* ApplicationUtils' tests
|
||||
*/
|
||||
|
||||
require_once 'application/ApplicationUtils.php';
|
||||
|
||||
/**
|
||||
* Fake ApplicationUtils class to avoid HTTP requests
|
||||
*/
|
||||
class FakeApplicationUtils extends ApplicationUtils
|
||||
{
|
||||
public static $VERSION_CODE = '';
|
||||
|
||||
/**
|
||||
* Toggle HTTP requests, allow overriding the version code
|
||||
*/
|
||||
public static function getVersion($url, $timeout = 0)
|
||||
{
|
||||
return self::$VERSION_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
require_once 'tests/utils/FakeApplicationUtils.php';
|
||||
|
||||
/**
|
||||
* Unitary tests for Shaarli utilities
|
||||
*/
|
||||
class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
|
||||
class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
protected static $testUpdateFile = 'sandbox/update.txt';
|
||||
protected static $testVersion = '0.5.0';
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
<?php
|
||||
|
||||
require_once 'application/FileUtils.php';
|
||||
namespace Shaarli;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class FileUtilsTest
|
||||
*
|
||||
* Test file utility class.
|
||||
*/
|
||||
class FileUtilsTest extends PHPUnit_Framework_TestCase
|
||||
class FileUtilsTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string Test file path.
|
||||
|
@ -48,7 +50,7 @@ public function testSimpleWriteRead()
|
|||
/**
|
||||
* File not writable: raise an exception.
|
||||
*
|
||||
* @expectedException IOException
|
||||
* @expectedException Shaarli\Exceptions\IOException
|
||||
* @expectedExceptionMessage Error accessing "sandbox/flat.db"
|
||||
*/
|
||||
public function testWriteWithoutPermission()
|
||||
|
@ -61,7 +63,7 @@ public function testWriteWithoutPermission()
|
|||
/**
|
||||
* Folder non existent: raise an exception.
|
||||
*
|
||||
* @expectedException IOException
|
||||
* @expectedException Shaarli\Exceptions\IOException
|
||||
* @expectedExceptionMessage Error accessing "nopefolder"
|
||||
*/
|
||||
public function testWriteFolderDoesNotExist()
|
||||
|
@ -72,7 +74,7 @@ public function testWriteFolderDoesNotExist()
|
|||
/**
|
||||
* Folder non writable: raise an exception.
|
||||
*
|
||||
* @expectedException IOException
|
||||
* @expectedException Shaarli\Exceptions\IOException
|
||||
* @expectedExceptionMessage Error accessing "sandbox"
|
||||
*/
|
||||
public function testWriteFolderPermission()
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<?php
|
||||
|
||||
require_once 'application/History.php';
|
||||
namespace Shaarli;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
|
||||
class HistoryTest extends PHPUnit_Framework_TestCase
|
||||
class HistoryTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string History file path
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
/**
|
||||
* Class LanguagesTest.
|
||||
*/
|
||||
class LanguagesTest extends \PHPUnit_Framework_TestCase
|
||||
class LanguagesTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string Config file path (without extension).
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
<?php
|
||||
namespace Shaarli\Plugin;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
/**
|
||||
* Plugin Manager tests
|
||||
*/
|
||||
|
||||
require_once 'application/PluginManager.php';
|
||||
|
||||
/**
|
||||
* Unit tests for Plugins
|
||||
*/
|
||||
class PluginManagerTest extends PHPUnit_Framework_TestCase
|
||||
class PluginManagerTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* Path to tests plugin.
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Router tests
|
||||
*/
|
||||
|
||||
require_once 'application/Router.php';
|
||||
namespace Shaarli;
|
||||
|
||||
/**
|
||||
* Unit tests for Router
|
||||
*/
|
||||
class RouterTest extends PHPUnit_Framework_TestCase
|
||||
class RouterTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* Test findPage: login page output.
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
/**
|
||||
* Unitary tests for timezone utilities
|
||||
*/
|
||||
class TimeZoneTest extends PHPUnit_Framework_TestCase
|
||||
class TimeZoneTest extends PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var array of timezones
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
/**
|
||||
* Unitary tests for Shaarli utilities
|
||||
*/
|
||||
class UtilsTest extends PHPUnit_Framework_TestCase
|
||||
class UtilsTest extends PHPUnit\Framework\TestCase
|
||||
{
|
||||
// Log file
|
||||
protected static $testLogFile = 'tests.log';
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
namespace Shaarli\Api;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
|
@ -18,7 +17,7 @@
|
|||
*
|
||||
* @package Api
|
||||
*/
|
||||
class ApiMiddlewareTest extends \PHPUnit_Framework_TestCase
|
||||
class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string datastore to test write operations
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
namespace Shaarli\Api;
|
||||
|
||||
use Shaarli\Base64Url;
|
||||
use Shaarli\Http\Base64Url;
|
||||
|
||||
/**
|
||||
* Class ApiUtilsTest
|
||||
*/
|
||||
class ApiUtilsTest extends \PHPUnit_Framework_TestCase
|
||||
class ApiUtilsTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* Force the timezone for ISO datetimes.
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\History;
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
|
@ -11,7 +11,7 @@
|
|||
|
||||
require_once 'tests/utils/ReferenceHistory.php';
|
||||
|
||||
class HistoryTest extends \PHPUnit_Framework_TestCase
|
||||
class HistoryTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string datastore to test write operations
|
||||
|
@ -34,7 +34,7 @@ class HistoryTest extends \PHPUnit_Framework_TestCase
|
|||
protected $container;
|
||||
|
||||
/**
|
||||
* @var History controller instance.
|
||||
* @var HistoryController controller instance.
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
|
@ -49,9 +49,9 @@ public function setUp()
|
|||
$this->container = new Container();
|
||||
$this->container['conf'] = $this->conf;
|
||||
$this->container['db'] = true;
|
||||
$this->container['history'] = new \History(self::$testHistory);
|
||||
$this->container['history'] = new History(self::$testHistory);
|
||||
|
||||
$this->controller = new History($this->container);
|
||||
$this->controller = new HistoryController($this->container);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,35 +78,35 @@ public function testGetHistory()
|
|||
|
||||
$this->assertEquals($this->refHistory->count(), count($data));
|
||||
|
||||
$this->assertEquals(\History::DELETED, $data[0]['event']);
|
||||
$this->assertEquals(History::DELETED, $data[0]['event']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
|
||||
$data[0]['datetime']
|
||||
);
|
||||
$this->assertEquals(124, $data[0]['id']);
|
||||
|
||||
$this->assertEquals(\History::SETTINGS, $data[1]['event']);
|
||||
$this->assertEquals(History::SETTINGS, $data[1]['event']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM),
|
||||
$data[1]['datetime']
|
||||
);
|
||||
$this->assertNull($data[1]['id']);
|
||||
|
||||
$this->assertEquals(\History::UPDATED, $data[2]['event']);
|
||||
$this->assertEquals(History::UPDATED, $data[2]['event']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat('Ymd_His', '20170301_121214')->format(\DateTime::ATOM),
|
||||
$data[2]['datetime']
|
||||
);
|
||||
$this->assertEquals(123, $data[2]['id']);
|
||||
|
||||
$this->assertEquals(\History::CREATED, $data[3]['event']);
|
||||
$this->assertEquals(History::CREATED, $data[3]['event']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat('Ymd_His', '20170201_121214')->format(\DateTime::ATOM),
|
||||
$data[3]['datetime']
|
||||
);
|
||||
$this->assertEquals(124, $data[3]['id']);
|
||||
|
||||
$this->assertEquals(\History::CREATED, $data[4]['event']);
|
||||
$this->assertEquals(History::CREATED, $data[4]['event']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM),
|
||||
$data[4]['datetime']
|
||||
|
@ -131,7 +131,7 @@ public function testGetHistoryLimit()
|
|||
|
||||
$this->assertEquals(1, count($data));
|
||||
|
||||
$this->assertEquals(\History::DELETED, $data[0]['event']);
|
||||
$this->assertEquals(History::DELETED, $data[0]['event']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
|
||||
$data[0]['datetime']
|
||||
|
@ -156,7 +156,7 @@ public function testGetHistoryOffset()
|
|||
|
||||
$this->assertEquals(1, count($data));
|
||||
|
||||
$this->assertEquals(\History::CREATED, $data[0]['event']);
|
||||
$this->assertEquals(History::CREATED, $data[0]['event']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM),
|
||||
$data[0]['datetime']
|
||||
|
@ -181,7 +181,7 @@ public function testGetHistorySince()
|
|||
|
||||
$this->assertEquals(1, count($data));
|
||||
|
||||
$this->assertEquals(\History::DELETED, $data[0]['event']);
|
||||
$this->assertEquals(History::DELETED, $data[0]['event']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
|
||||
$data[0]['datetime']
|
||||
|
@ -206,7 +206,7 @@ public function testGetHistorySinceOffsetLimit()
|
|||
|
||||
$this->assertEquals(1, count($data));
|
||||
|
||||
$this->assertEquals(\History::SETTINGS, $data[0]['event']);
|
||||
$this->assertEquals(History::SETTINGS, $data[0]['event']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM),
|
||||
$data[0]['datetime']
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
|
@ -15,7 +14,7 @@
|
|||
*
|
||||
* @package Api\Controllers
|
||||
*/
|
||||
class InfoTest extends \PHPUnit_Framework_TestCase
|
||||
class InfoTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string datastore to test write operations
|
||||
|
@ -53,7 +52,7 @@ public function setUp()
|
|||
|
||||
$this->container = new Container();
|
||||
$this->container['conf'] = $this->conf;
|
||||
$this->container['db'] = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['history'] = null;
|
||||
|
||||
$this->controller = new Info($this->container);
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
|
||||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Bookmark\LinkDB;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\History;
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class DeleteLinkTest extends \PHPUnit_Framework_TestCase
|
||||
class DeleteLinkTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string datastore to test write operations
|
||||
|
@ -32,12 +34,12 @@ class DeleteLinkTest extends \PHPUnit_Framework_TestCase
|
|||
protected $refDB = null;
|
||||
|
||||
/**
|
||||
* @var \LinkDB instance.
|
||||
* @var LinkDB instance.
|
||||
*/
|
||||
protected $linkDB;
|
||||
|
||||
/**
|
||||
* @var \History instance.
|
||||
* @var HistoryController instance.
|
||||
*/
|
||||
protected $history;
|
||||
|
||||
|
@ -59,10 +61,10 @@ public function setUp()
|
|||
$this->conf = new ConfigManager('tests/utils/config/configJson');
|
||||
$this->refDB = new \ReferenceLinkDB();
|
||||
$this->refDB->write(self::$testDatastore);
|
||||
$this->linkDB = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
$refHistory = new \ReferenceHistory();
|
||||
$refHistory->write(self::$testHistory);
|
||||
$this->history = new \History(self::$testHistory);
|
||||
$this->history = new History(self::$testHistory);
|
||||
$this->container = new Container();
|
||||
$this->container['conf'] = $this->conf;
|
||||
$this->container['db'] = $this->linkDB;
|
||||
|
@ -96,11 +98,11 @@ public function testDeleteLinkValid()
|
|||
$this->assertEquals(204, $response->getStatusCode());
|
||||
$this->assertEmpty((string) $response->getBody());
|
||||
|
||||
$this->linkDB = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
$this->assertFalse(isset($this->linkDB[$id]));
|
||||
|
||||
$historyEntry = $this->history->getHistory()[0];
|
||||
$this->assertEquals(\History::DELETED, $historyEntry['event']);
|
||||
$this->assertEquals(History::DELETED, $historyEntry['event']);
|
||||
$this->assertTrue(
|
||||
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
|
||||
);
|
||||
|
@ -110,7 +112,7 @@ public function testDeleteLinkValid()
|
|||
/**
|
||||
* Test DELETE link endpoint: reach not existing ID.
|
||||
*
|
||||
* @expectedException Shaarli\Api\Exceptions\ApiLinkNotFoundException
|
||||
* @expectedException \Shaarli\Api\Exceptions\ApiLinkNotFoundException
|
||||
*/
|
||||
public function testDeleteLink404()
|
||||
{
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
|
@ -18,7 +17,7 @@
|
|||
*
|
||||
* @package Shaarli\Api\Controllers
|
||||
*/
|
||||
class GetLinkIdTest extends \PHPUnit_Framework_TestCase
|
||||
class GetLinkIdTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string datastore to test write operations
|
||||
|
@ -61,7 +60,7 @@ public function setUp()
|
|||
|
||||
$this->container = new Container();
|
||||
$this->container['conf'] = $this->conf;
|
||||
$this->container['db'] = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['history'] = null;
|
||||
|
||||
$this->controller = new Links($this->container);
|
||||
|
@ -108,7 +107,7 @@ public function testGetLinkId()
|
|||
$this->assertEquals('sTuff', $data['tags'][0]);
|
||||
$this->assertEquals(false, $data['private']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
|
||||
\DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
|
||||
$data['created']
|
||||
);
|
||||
$this->assertEmpty($data['updated']);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Bookmark\LinkDB;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
|
@ -17,7 +17,7 @@
|
|||
*
|
||||
* @package Shaarli\Api\Controllers
|
||||
*/
|
||||
class GetLinksTest extends \PHPUnit_Framework_TestCase
|
||||
class GetLinksTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string datastore to test write operations
|
||||
|
@ -60,7 +60,7 @@ public function setUp()
|
|||
|
||||
$this->container = new Container();
|
||||
$this->container['conf'] = $this->conf;
|
||||
$this->container['db'] = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['db'] = new LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['history'] = null;
|
||||
|
||||
$this->controller = new Links($this->container);
|
||||
|
@ -114,7 +114,7 @@ public function testGetLinks()
|
|||
$this->assertEquals('sTuff', $first['tags'][0]);
|
||||
$this->assertEquals(false, $first['private']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
|
||||
\DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
|
||||
$first['created']
|
||||
);
|
||||
$this->assertEmpty($first['updated']);
|
||||
|
@ -125,7 +125,7 @@ public function testGetLinks()
|
|||
|
||||
// Update date
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM),
|
||||
\DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM),
|
||||
$link['updated']
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\History;
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
|
@ -40,7 +41,7 @@ class PostLinkTest extends TestCase
|
|||
protected $refDB = null;
|
||||
|
||||
/**
|
||||
* @var \History instance.
|
||||
* @var HistoryController instance.
|
||||
*/
|
||||
protected $history;
|
||||
|
||||
|
@ -70,12 +71,12 @@ public function setUp()
|
|||
|
||||
$refHistory = new \ReferenceHistory();
|
||||
$refHistory->write(self::$testHistory);
|
||||
$this->history = new \History(self::$testHistory);
|
||||
$this->history = new History(self::$testHistory);
|
||||
|
||||
$this->container = new Container();
|
||||
$this->container['conf'] = $this->conf;
|
||||
$this->container['db'] = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['history'] = new \History(self::$testHistory);
|
||||
$this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['history'] = new History(self::$testHistory);
|
||||
|
||||
$this->controller = new Links($this->container);
|
||||
|
||||
|
@ -121,7 +122,7 @@ public function testPostLinkMinimal()
|
|||
$data = json_decode((string) $response->getBody(), true);
|
||||
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
|
||||
$this->assertEquals(43, $data['id']);
|
||||
$this->assertRegExp('/[\w-_]{6}/', $data['shorturl']);
|
||||
$this->assertRegExp('/[\w_-]{6}/', $data['shorturl']);
|
||||
$this->assertEquals('http://domain.tld/?' . $data['shorturl'], $data['url']);
|
||||
$this->assertEquals('?' . $data['shorturl'], $data['title']);
|
||||
$this->assertEquals('', $data['description']);
|
||||
|
@ -133,7 +134,7 @@ public function testPostLinkMinimal()
|
|||
$this->assertEquals('', $data['updated']);
|
||||
|
||||
$historyEntry = $this->history->getHistory()[0];
|
||||
$this->assertEquals(\History::CREATED, $historyEntry['event']);
|
||||
$this->assertEquals(History::CREATED, $historyEntry['event']);
|
||||
$this->assertTrue(
|
||||
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
|
||||
);
|
||||
|
@ -166,7 +167,7 @@ public function testPostLinkFull()
|
|||
$data = json_decode((string) $response->getBody(), true);
|
||||
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
|
||||
$this->assertEquals(43, $data['id']);
|
||||
$this->assertRegExp('/[\w-_]{6}/', $data['shorturl']);
|
||||
$this->assertRegExp('/[\w_-]{6}/', $data['shorturl']);
|
||||
$this->assertEquals('http://' . $link['url'], $data['url']);
|
||||
$this->assertEquals($link['title'], $data['title']);
|
||||
$this->assertEquals($link['description'], $data['description']);
|
||||
|
@ -210,11 +211,11 @@ public function testPostLinkDuplicate()
|
|||
$this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
|
||||
$this->assertEquals(false, $data['private']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130614_184135'),
|
||||
\DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130614_184135'),
|
||||
\DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
|
||||
);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130615_184230'),
|
||||
\DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130615_184230'),
|
||||
\DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\History;
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class PutLinkTest extends \PHPUnit_Framework_TestCase
|
||||
class PutLinkTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string datastore to test write operations
|
||||
|
@ -32,7 +33,7 @@ class PutLinkTest extends \PHPUnit_Framework_TestCase
|
|||
protected $refDB = null;
|
||||
|
||||
/**
|
||||
* @var \History instance.
|
||||
* @var HistoryController instance.
|
||||
*/
|
||||
protected $history;
|
||||
|
||||
|
@ -62,12 +63,12 @@ public function setUp()
|
|||
|
||||
$refHistory = new \ReferenceHistory();
|
||||
$refHistory->write(self::$testHistory);
|
||||
$this->history = new \History(self::$testHistory);
|
||||
$this->history = new History(self::$testHistory);
|
||||
|
||||
$this->container = new Container();
|
||||
$this->container['conf'] = $this->conf;
|
||||
$this->container['db'] = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['history'] = new \History(self::$testHistory);
|
||||
$this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['history'] = new History(self::$testHistory);
|
||||
|
||||
$this->controller = new Links($this->container);
|
||||
|
||||
|
@ -119,7 +120,7 @@ public function testPutLinkMinimal()
|
|||
);
|
||||
|
||||
$historyEntry = $this->history->getHistory()[0];
|
||||
$this->assertEquals(\History::UPDATED, $historyEntry['event']);
|
||||
$this->assertEquals(History::UPDATED, $historyEntry['event']);
|
||||
$this->assertTrue(
|
||||
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
|
||||
);
|
||||
|
@ -198,11 +199,11 @@ public function testPutLinkDuplicate()
|
|||
$this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
|
||||
$this->assertEquals(false, $data['private']);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130614_184135'),
|
||||
\DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130614_184135'),
|
||||
\DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
|
||||
);
|
||||
$this->assertEquals(
|
||||
\DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130615_184230'),
|
||||
\DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130615_184230'),
|
||||
\DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
|
||||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Bookmark\LinkDB;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\History;
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class DeleteTagTest extends \PHPUnit_Framework_TestCase
|
||||
class DeleteTagTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string datastore to test write operations
|
||||
|
@ -32,12 +34,12 @@ class DeleteTagTest extends \PHPUnit_Framework_TestCase
|
|||
protected $refDB = null;
|
||||
|
||||
/**
|
||||
* @var \LinkDB instance.
|
||||
* @var LinkDB instance.
|
||||
*/
|
||||
protected $linkDB;
|
||||
|
||||
/**
|
||||
* @var \History instance.
|
||||
* @var HistoryController instance.
|
||||
*/
|
||||
protected $history;
|
||||
|
||||
|
@ -59,10 +61,10 @@ public function setUp()
|
|||
$this->conf = new ConfigManager('tests/utils/config/configJson');
|
||||
$this->refDB = new \ReferenceLinkDB();
|
||||
$this->refDB->write(self::$testDatastore);
|
||||
$this->linkDB = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
$refHistory = new \ReferenceHistory();
|
||||
$refHistory->write(self::$testHistory);
|
||||
$this->history = new \History(self::$testHistory);
|
||||
$this->history = new History(self::$testHistory);
|
||||
$this->container = new Container();
|
||||
$this->container['conf'] = $this->conf;
|
||||
$this->container['db'] = $this->linkDB;
|
||||
|
@ -97,18 +99,18 @@ public function testDeleteTagValid()
|
|||
$this->assertEquals(204, $response->getStatusCode());
|
||||
$this->assertEmpty((string) $response->getBody());
|
||||
|
||||
$this->linkDB = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
$tags = $this->linkDB->linksCountPerTag();
|
||||
$this->assertFalse(isset($tags[$tagName]));
|
||||
|
||||
// 2 links affected
|
||||
$historyEntry = $this->history->getHistory()[0];
|
||||
$this->assertEquals(\History::UPDATED, $historyEntry['event']);
|
||||
$this->assertEquals(History::UPDATED, $historyEntry['event']);
|
||||
$this->assertTrue(
|
||||
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
|
||||
);
|
||||
$historyEntry = $this->history->getHistory()[1];
|
||||
$this->assertEquals(\History::UPDATED, $historyEntry['event']);
|
||||
$this->assertEquals(History::UPDATED, $historyEntry['event']);
|
||||
$this->assertTrue(
|
||||
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
|
||||
);
|
||||
|
@ -131,13 +133,13 @@ public function testDeleteTagCaseSensitivity()
|
|||
$this->assertEquals(204, $response->getStatusCode());
|
||||
$this->assertEmpty((string) $response->getBody());
|
||||
|
||||
$this->linkDB = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
$tags = $this->linkDB->linksCountPerTag();
|
||||
$this->assertFalse(isset($tags[$tagName]));
|
||||
$this->assertTrue($tags[strtolower($tagName)] > 0);
|
||||
|
||||
$historyEntry = $this->history->getHistory()[0];
|
||||
$this->assertEquals(\History::UPDATED, $historyEntry['event']);
|
||||
$this->assertEquals(History::UPDATED, $historyEntry['event']);
|
||||
$this->assertTrue(
|
||||
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
|
||||
);
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Bookmark\LinkDB;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
|
@ -16,7 +16,7 @@
|
|||
*
|
||||
* @package Shaarli\Api\Controllers
|
||||
*/
|
||||
class GetTagNameTest extends \PHPUnit_Framework_TestCase
|
||||
class GetTagNameTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var string datastore to test write operations
|
||||
|
@ -59,7 +59,7 @@ public function setUp()
|
|||
|
||||
$this->container = new Container();
|
||||
$this->container['conf'] = $this->conf;
|
||||
$this->container['db'] = new \LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['db'] = new LinkDB(self::$testDatastore, true, false);
|
||||
$this->container['history'] = null;
|
||||
|
||||
$this->controller = new Tags($this->container);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue