Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use web-thumbnailer to retrieve thumbnails #687

Merged
merged 9 commits into from
Jul 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions application/PageBuilder.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use Shaarli\Config\ConfigManager;
use Shaarli\Thumbnailer;

/**
* This class is in charge of building the final page.
Expand All @@ -21,26 +22,39 @@ class PageBuilder
*/
protected $conf;

/**
* @var array $_SESSION
*/
protected $session;

/**
* @var LinkDB $linkDB instance.
*/
protected $linkDB;


/**
* @var null|string XSRF token
*/
protected $token;

/** @var bool $isLoggedIn Whether the user is logged in **/
protected $isLoggedIn = false;

/**
* PageBuilder constructor.
* $tpl is initialized at false for lazy loading.
*
* @param ConfigManager $conf Configuration Manager instance (reference).
* @param LinkDB $linkDB instance.
* @param string $token Session token
* @param ConfigManager $conf Configuration Manager instance (reference).
* @param array $session $_SESSION array
* @param LinkDB $linkDB instance.
* @param string $token Session token
* @param bool $isLoggedIn
*/
public function __construct(&$conf, $linkDB = null, $token = null, $isLoggedIn = false)
public function __construct(&$conf, $session, $linkDB = null, $token = null, $isLoggedIn = false)
{
$this->tpl = false;
$this->conf = $conf;
$this->session = $session;
$this->linkDB = $linkDB;
$this->token = $token;
$this->isLoggedIn = $isLoggedIn;
Expand Down Expand Up @@ -105,6 +119,19 @@ private function initialize()
if ($this->linkDB !== null) {
$this->tpl->assign('tags', $this->linkDB->linksCountPerTag());
}

$this->tpl->assign(
'thumbnails_enabled',
$this->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE
);
$this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width'));
$this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height'));

if (! empty($_SESSION['warnings'])) {
$this->tpl->assign('global_warnings', $_SESSION['warnings']);
unset($_SESSION['warnings']);
}

// To be removed with a proper theme configuration.
$this->tpl->assign('conf', $this->conf);
}
Expand Down
12 changes: 12 additions & 0 deletions application/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
*/
class Router
{
public static $AJAX_THUMB_UPDATE = 'ajax_thumb_update';

public static $PAGE_LOGIN = 'login';

public static $PAGE_PICWALL = 'picwall';
Expand Down Expand Up @@ -47,6 +49,8 @@ class Router

public static $PAGE_SAVE_PLUGINSADMIN = 'save_pluginadmin';

public static $PAGE_THUMBS_UPDATE = 'thumbs_update';

public static $GET_TOKEN = 'token';

/**
Expand Down Expand Up @@ -101,6 +105,14 @@ public static function findPage($query, $get, $loggedIn)
return self::$PAGE_FEED_RSS;
}

if (startsWith($query, 'do='. self::$PAGE_THUMBS_UPDATE)) {
return self::$PAGE_THUMBS_UPDATE;
}

if (startsWith($query, 'do='. self::$AJAX_THUMB_UPDATE)) {
return self::$AJAX_THUMB_UPDATE;
}

// At this point, only loggedin pages.
if (!$loggedIn) {
return self::$PAGE_LINKLIST;
Expand Down
127 changes: 127 additions & 0 deletions application/Thumbnailer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<?php

namespace Shaarli;

use Shaarli\Config\ConfigManager;
use WebThumbnailer\Exception\WebThumbnailerException;
use WebThumbnailer\WebThumbnailer;
use WebThumbnailer\Application\ConfigManager as WTConfigManager;

/**
* Class Thumbnailer
*
* Utility class used to retrieve thumbnails using web-thumbnailer dependency.
*/
class Thumbnailer
{
const COMMON_MEDIA_DOMAINS = [
'imgur.com',
'flickr.com',
'youtube.com',
'wikimedia.org',
'redd.it',
'gfycat.com',
'media.giphy.com',
'twitter.com',
'twimg.com',
'instagram.com',
'pinterest.com',
'pinterest.fr',
'tumblr.com',
'deviantart.com',
];

const MODE_ALL = 'all';
const MODE_COMMON = 'common';
const MODE_NONE = 'none';

/**
* @var WebThumbnailer instance.
*/
protected $wt;

/**
* @var ConfigManager instance.
*/
protected $conf;

/**
* Thumbnailer constructor.
*
* @param ConfigManager $conf instance.
*/
public function __construct($conf)
{
$this->conf = $conf;

if (! $this->checkRequirements()) {
$this->conf->set('thumbnails.enabled', false);
$this->conf->write(true);
// TODO: create a proper error handling system able to catch exceptions...
die(t('php-gd extension must be loaded to use thumbnails. Thumbnails are now disabled. Please reload the page.'));
}

$this->wt = new WebThumbnailer();
WTConfigManager::addFile('inc/web-thumbnailer.json');
$this->wt->maxWidth($this->conf->get('thumbnails.width'))
->maxHeight($this->conf->get('thumbnails.height'))
->crop(true)
->debug($this->conf->get('dev.debug', false));
}

/**
* Retrieve a thumbnail for given URL
*
* @param string $url where to look for a thumbnail.
*
* @return bool|string The thumbnail relative cache file path, or false if none has been found.
*/
public function get($url)
{
if ($this->conf->get('thumbnails.mode') === self::MODE_COMMON
&& ! $this->isCommonMediaOrImage($url)
) {
return false;
}

try {
return $this->wt->thumbnail($url);
} catch (WebThumbnailerException $e) {
// Exceptions are only thrown in debug mode.
error_log(get_class($e) . ': ' . $e->getMessage());
}
return false;
}

/**
* We check weather the given URL is from a common media domain,
* or if the file extension is an image.
*
* @param string $url to check
*
* @return bool true if it's an image or from a common media domain, false otherwise.
*/
public function isCommonMediaOrImage($url)
{
foreach (self::COMMON_MEDIA_DOMAINS as $domain) {
if (strpos($url, $domain) !== false) {
return true;
}
}

if (endsWith($url, '.jpg') || endsWith($url, '.png') || endsWith($url, '.jpeg')) {
return true;
}

return false;
}

/**
* Make sure that requirements are match to use thumbnails:
* - php-gd is loaded
*/
protected function checkRequirements()
{
return extension_loaded('gd');
}
}
36 changes: 35 additions & 1 deletion application/Updater.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use Shaarli\Config\ConfigJson;
use Shaarli\Config\ConfigPhp;
use Shaarli\Config\ConfigManager;
use Shaarli\Thumbnailer;

/**
* Class Updater.
Expand Down Expand Up @@ -30,6 +31,11 @@ class Updater
*/
protected $isLoggedIn;

/**
* @var array $_SESSION
*/
protected $session;

/**
* @var ReflectionMethod[] List of current class methods.
*/
Expand All @@ -42,13 +48,17 @@ class Updater
* @param LinkDB $linkDB LinkDB instance.
* @param ConfigManager $conf Configuration Manager instance.
* @param boolean $isLoggedIn True if the user is logged in.
* @param array $session $_SESSION (by reference)
*
* @throws ReflectionException
*/
public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn)
public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn, &$session = [])
{
$this->doneUpdates = $doneUpdates;
$this->linkDB = $linkDB;
$this->conf = $conf;
$this->isLoggedIn = $isLoggedIn;
$this->session = &$session;

// Retrieve all update methods.
$class = new ReflectionClass($this);
Expand Down Expand Up @@ -480,6 +490,30 @@ public function updateMethodDownloadSizeAndTimeoutConf()
}

$this->conf->write($this->isLoggedIn);
return true;
}

/**
* * Move thumbnails management to WebThumbnailer, coming with new settings.
*/
public function updateMethodWebThumbnailer()
{
if ($this->conf->exists('thumbnails.mode')) {
return true;
}

$thumbnailsEnabled = $this->conf->get('thumbnail.enable_thumbnails', true);
$this->conf->set('thumbnails.mode', $thumbnailsEnabled ? Thumbnailer::MODE_ALL : Thumbnailer::MODE_NONE);
$this->conf->set('thumbnails.width', 125);
$this->conf->set('thumbnails.height', 90);
$this->conf->remove('thumbnail');
$this->conf->write(true);

if ($thumbnailsEnabled) {
$this->session['warnings'][] = t(
'You have enabled or changed thumbnails mode. <a href="?do=thumbs_update">Please synchronize them</a>.'
);
}

return true;
}
Expand Down
56 changes: 52 additions & 4 deletions application/config/ConfigManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,33 @@ public function set($setting, $value, $write = false, $isLoggedIn = false)
}
}

/**
* Remove a config element from the config file.
*
* @param string $setting Asked setting, keys separated with dots.
* @param bool $write Write the new setting in the config file, default false.
* @param bool $isLoggedIn User login state, default false.
*
* @throws \Exception Invalid
*/
public function remove($setting, $write = false, $isLoggedIn = false)
{
if (empty($setting) || ! is_string($setting)) {
throw new \Exception(t('Invalid setting key parameter. String expected, got: '). gettype($setting));
}

// During the ConfigIO transition, map legacy settings to the new ones.
if ($this->configIO instanceof ConfigPhp && isset(ConfigPhp::$LEGACY_KEYS_MAPPING[$setting])) {
$setting = ConfigPhp::$LEGACY_KEYS_MAPPING[$setting];
}

$settings = explode('.', $setting);
self::removeConfig($settings, $this->loadedConfig);
if ($write) {
$this->write($isLoggedIn);
}
}

/**
* Check if a settings exists.
*
Expand Down Expand Up @@ -272,7 +299,7 @@ protected static function getConfig($settings, $conf)
*
* @param array $settings Ordered array which contains keys to find.
* @param mixed $value
* @param array $conf Loaded settings, then sub-array.
* @param array $conf Loaded settings, then sub-array.
*
* @return mixed Found setting or NOT_FOUND flag.
*/
Expand All @@ -289,6 +316,27 @@ protected static function setConfig($settings, $value, &$conf)
$conf[$setting] = $value;
}

/**
* Recursive function which find asked setting in the loaded config and deletes it.
*
* @param array $settings Ordered array which contains keys to find.
* @param array $conf Loaded settings, then sub-array.
*
* @return mixed Found setting or NOT_FOUND flag.
*/
protected static function removeConfig($settings, &$conf)
{
if (!is_array($settings) || count($settings) == 0) {
return self::$NOT_FOUND;
}

$setting = array_shift($settings);
if (count($settings) > 0) {
return self::removeConfig($settings, $conf[$setting]);
}
unset($conf[$setting]);
}

/**
* Set a bunch of default values allowing Shaarli to start without a config file.
*/
Expand Down Expand Up @@ -333,12 +381,12 @@ protected function setDefaultValues()
// default state of the 'remember me' checkbox of the login form
$this->setEmpty('privacy.remember_user_default', true);

$this->setEmpty('thumbnail.enable_thumbnails', true);
$this->setEmpty('thumbnail.enable_localcache', true);

$this->setEmpty('redirector.url', '');
$this->setEmpty('redirector.encode_url', true);

$this->setEmpty('thumbnails.width', '125');
$this->setEmpty('thumbnails.height', '90');

$this->setEmpty('translation.language', 'auto');
$this->setEmpty('translation.mode', 'php');
$this->setEmpty('translation.extensions', []);
Expand Down
Loading