diff options
author | Devlin Junker <devlin.junker@gmail.com> | 2023-10-22 13:57:14 -0700 |
---|---|---|
committer | Devlin Junker <devlin.junker@gmail.com> | 2023-10-22 14:03:52 -0700 |
commit | 3e35d781cf712efa1473b4495a48299326b52c0c (patch) | |
tree | 9643b2e75ee3139bb6c5abb102d6f69937a5169e /lib | |
parent | 94f04d4a20cbbadb7d8ad835bc7ae3dc1c4256fd (diff) | |
parent | d423ca520ca9c61233c33717d604e3183f0f486c (diff) |
Merge remote-tracking branch 'nextcloud/master' into master-merge
Signed-off-by: Devlin Junker <devlin.junker@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/AppInfo/Application.php | 12 | ||||
-rw-r--r-- | lib/Command/Updater/Job.php | 76 | ||||
-rw-r--r-- | lib/Config/FetcherConfig.php | 25 | ||||
-rw-r--r-- | lib/Controller/PageController.php | 1 | ||||
-rw-r--r-- | lib/Fetcher/Client/FeedIoClient.php | 8 | ||||
-rwxr-xr-x | lib/Fetcher/FeedFetcher.php | 53 | ||||
-rw-r--r-- | lib/Scraper/Scraper.php | 3 | ||||
-rw-r--r-- | lib/Service/StatusService.php | 8 | ||||
-rw-r--r-- | lib/Service/UpdaterService.php | 20 | ||||
-rw-r--r-- | lib/Utility/Cache.php | 59 |
10 files changed, 229 insertions, 36 deletions
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index bc5e1b476..8bfa09c86 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -24,11 +24,11 @@ use OCA\News\Hooks\UserDeleteHook; use OCA\News\Search\FeedSearchProvider; use OCA\News\Search\FolderSearchProvider; use OCA\News\Search\ItemSearchProvider; +use OCA\News\Utility\Cache; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; -use OCP\ITempManager; use OCP\AppFramework\App; use OCA\News\Fetcher\FeedFetcher; @@ -92,15 +92,9 @@ class Application extends App implements IBootstrap $context->registerParameter('exploreDir', __DIR__ . '/../Explore/feeds'); $context->registerService(HTMLPurifier::class, function (ContainerInterface $c): HTMLPurifier { - $directory = $c->get(ITempManager::class)->getTempBaseDir() . '/news/cache/purifier'; - - if (!is_dir($directory)) { - mkdir($directory, 0770, true); - } - $config = HTMLPurifier_Config::createDefault(); $config->set('HTML.ForbiddenAttributes', 'class'); - $config->set('Cache.SerializerPath', $directory); + $config->set('Cache.SerializerPath', $c->get(Cache::class)->getCache("purifier")); $config->set('HTML.SafeIframe', true); $config->set( 'URI.SafeIframeRegexp', @@ -140,7 +134,7 @@ class Application extends App implements IBootstrap $context->registerService(Favicon::class, function (ContainerInterface $c): Favicon { $favicon = new Favicon(); - $favicon->cache(['dir' => $c->get(ITempManager::class)->getTempBaseDir()]); + $favicon->cache(['dir' => $c->get(Cache::class)->getCache("feedFavicon")]); return $favicon; }); } diff --git a/lib/Command/Updater/Job.php b/lib/Command/Updater/Job.php new file mode 100644 index 000000000..4404a456e --- /dev/null +++ b/lib/Command/Updater/Job.php @@ -0,0 +1,76 @@ +<?php +/** + * Nextcloud - News + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + */ + +namespace OCA\News\Command\Updater; + +use DateTime; +use OCP\Util; +use OCA\News\Service\StatusService; +use OCA\News\Service\UpdaterService; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class Job extends Command +{ + /** + * @var StatusService Status service + */ + private $statusService; + + /** + * @var UpdaterService Update service + */ + private $updaterService; + + public function __construct(StatusService $statusService, UpdaterService $updaterService) + { + parent::__construct(); + $this->statusService = $statusService; + $this->updaterService = $updaterService; + } + + /** + * @return void + */ + protected function configure() + { + $this->setName('news:updater:job') + ->addOption( + 'reset', + null, + InputOption::VALUE_NONE, + 'If the job should be reset, warning this might lead to issues.' + ) + ->setDescription('Console API for checking the update job status and to reset it.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $reset = (bool) $input->getOption('reset'); + + [$major, $minor, $micro] = Util::getVersion(); + + if ($major < 26) { + $output->writeln("Error: This only works with Nextcloud 26 or newer."); + return 1; + } + $output->writeln("Checking update Status"); + $date = new DateTime(); + $date->setTimestamp($this->statusService->getUpdateTime()); + $output->writeln("Last Execution was ".$date->format('Y-m-d H:i:s e')); + + if ($reset) { + $output->writeln("Attempting to reset the job."); + $this->updaterService->reset(); + $output->writeln("Done, job should execute on next schedule."); + } + return 0; + } +} diff --git a/lib/Config/FetcherConfig.php b/lib/Config/FetcherConfig.php index 797dae49e..702fccdf1 100644 --- a/lib/Config/FetcherConfig.php +++ b/lib/Config/FetcherConfig.php @@ -95,6 +95,29 @@ class FetcherConfig } /** + * Checks for available encoding options + * + * @return String list of supported encoding types + */ + public function checkEncoding() + { + $supportedEncoding = []; + + // check curl features + $curl_features = curl_version()["features"]; + + $bitfields = array('CURL_VERSION_LIBZ' => ['gzip', 'deflate'], 'CURL_VERSION_BROTLI' => ['br']); + + foreach ($bitfields as $feature => $header) { + // checking available features via the 'features' bitmask and adding available types to the list + if (defined($feature) && $curl_features & constant($feature)) { + $supportedEncoding = array_merge($supportedEncoding, $header); + } + } + return implode(", ", $supportedEncoding); + } + + /** * Configure a guzzle client * * @return ClientInterface Client to guzzle. @@ -106,7 +129,7 @@ class FetcherConfig 'headers' => [ 'User-Agent' => static::DEFAULT_USER_AGENT, 'Accept' => static::DEFAULT_ACCEPT, - 'Accept-Encoding' => 'gzip, deflate', + 'Accept-Encoding' => $this->checkEncoding() ], ]; diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index abdd3f2f7..41d0e7f89 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -96,6 +96,7 @@ class PageController extends Controller $csp->addAllowedImageDomain('*') ->addAllowedMediaDomain('*') ->addAllowedConnectDomain('*')// chrome breaks on audio elements + ->allowEvalScript(true) ->addAllowedFrameDomain('https://youtube.com') ->addAllowedFrameDomain('https://www.youtube.com') ->addAllowedFrameDomain('https://player.vimeo.com') diff --git a/lib/Fetcher/Client/FeedIoClient.php b/lib/Fetcher/Client/FeedIoClient.php index ac1f62a1f..fe57c5889 100644 --- a/lib/Fetcher/Client/FeedIoClient.php +++ b/lib/Fetcher/Client/FeedIoClient.php @@ -38,14 +38,14 @@ class FeedIoClient implements ClientInterface } /** - * @param string $url - * @param DateTime $modifiedSince + * @param string $url + * @param DateTime|null $modifiedSince * * @return ResponseInterface * @throws ServerErrorException|GuzzleException * @throws NotFoundException */ - public function getResponse(string $url, DateTime $modifiedSince) : ResponseInterface + public function getResponse(string $url, ?DateTime $modifiedSince = null) : ResponseInterface { $modifiedSince->setTimezone(new \DateTimeZone('GMT')); try { @@ -65,7 +65,7 @@ class FeedIoClient implements ClientInterface case 404: throw new NotFoundException($e->getMessage()); default: - throw new ServerErrorException($e->getMessage()); + throw new ServerErrorException($e->getResponse()); } } } diff --git a/lib/Fetcher/FeedFetcher.php b/lib/Fetcher/FeedFetcher.php index 60d798889..c0477407c 100755 --- a/lib/Fetcher/FeedFetcher.php +++ b/lib/Fetcher/FeedFetcher.php @@ -30,6 +30,7 @@ use OCP\ITempManager; use OCA\News\Db\Item; use OCA\News\Db\Feed; use OCA\News\Utility\Time; +use OCA\News\Utility\Cache; use OCA\News\Scraper\Scraper; use OCA\News\Config\FetcherConfig; use Psr\Log\LoggerInterface; @@ -59,11 +60,6 @@ class FeedFetcher implements IFeedFetcher private $l10n; /** - * @var ITempManager - */ - private $ITempManager; - - /** * @var Time */ private $time; @@ -73,22 +69,34 @@ class FeedFetcher implements IFeedFetcher */ private $logger; + /** + * @var FetcherConfig + */ + private $fetcherConfig; + + /** + * @var Cache + */ + private $cache; + public function __construct( FeedIo $fetcher, Favicon $favicon, Scraper $scraper, IL10N $l10n, - ITempManager $ITempManager, Time $time, - LoggerInterface $logger + LoggerInterface $logger, + FetcherConfig $fetcherConfig, + Cache $cache ) { $this->reader = $fetcher; $this->faviconFactory = $favicon; $this->scraper = $scraper; $this->l10n = $l10n; - $this->ITempManager = $ITempManager; $this->time = $time; $this->logger = $logger; + $this->fetcherConfig = $fetcherConfig; + $this->cache = $cache; } @@ -127,7 +135,6 @@ class FeedFetcher implements IFeedFetcher $lastModified = null; } $url = $url2->getNormalizedURL(); - $this->reader->resetFilters(); $resource = $this->reader->read($url, null, $lastModified); $location = $resource->getUrl(); @@ -388,8 +395,10 @@ class FeedFetcher implements IFeedFetcher return is_string($return) ? $return : null; } - // logo will be saved in the tmp folder provided by Nextcloud, file is named as md5 of the url - $favicon_path = join(DIRECTORY_SEPARATOR, [$this->ITempManager->getTempBaseDir(), md5($favicon)]); + $logo_cache = $this->cache->getCache("feedLogo"); + + // file name of the logo is md5 of the url + $favicon_path = join(DIRECTORY_SEPARATOR, [$logo_cache, md5($favicon)]); $downloaded = false; if (file_exists($favicon_path)) { @@ -409,17 +418,18 @@ class FeedFetcher implements IFeedFetcher 'headers' => [ 'User-Agent' => FetcherConfig::DEFAULT_USER_AGENT, 'Accept' => 'image/*', - 'If-Modified-Since' => date(DateTime::RFC7231, $last_modified) + 'If-Modified-Since' => date(DateTime::RFC7231, $last_modified), + 'Accept-Encoding' => $this->fetcherConfig->checkEncoding() ] ] ); $downloaded = true; $this->logger->debug( - "Feed:{url} Logo:{logo} Status:{status}", + "Feed:{feed} Logo:{logo} Status:{status}", [ 'status' => $response->getStatusCode(), - 'url' => $favicon_path, + 'feed' => $url, 'logo' => $favicon ] ); @@ -437,6 +447,14 @@ class FeedFetcher implements IFeedFetcher // check if file is actually an image if (!$is_image) { + $this->logger->debug( + "Downloaded file:{file} from {url} is not an image", + [ + 'file' => $favicon_path, + 'url' => $favicon + ] + ); + $return = $this->faviconFactory->get($base_url); return is_string($return) ? $return : null; } @@ -444,6 +462,13 @@ class FeedFetcher implements IFeedFetcher list($width, $height, $type, $attr) = getimagesize($favicon_path); // check if image is square else fall back to favicon if ($width !== $height) { + $this->logger->debug( + "Downloaded file:{file} from {url} is not square", + [ + 'file' => $favicon_path, + 'url' => $favicon + ] + ); $return = $this->faviconFactory->get($base_url); return is_string($return) ? $return : null; } diff --git a/lib/Scraper/Scraper.php b/lib/Scraper/Scraper.php index 998c4464c..c5ae401f9 100644 --- a/lib/Scraper/Scraper.php +++ b/lib/Scraper/Scraper.php @@ -16,6 +16,7 @@ use fivefilters\Readability\Configuration; use fivefilters\Readability\ParseException; use League\Uri\Exceptions\SyntaxError; use Psr\Log\LoggerInterface; +use OCA\News\Config\FetcherConfig; class Scraper implements IScraper { @@ -37,7 +38,7 @@ class Scraper implements IScraper CURLOPT_RETURNTRANSFER => true, // return web page CURLOPT_HEADER => false, // do not return headers CURLOPT_FOLLOWLOCATION => true, // follow redirects - //CURLOPT_USERAGENT => "php-news", // who am i + CURLOPT_USERAGENT => FetcherConfig::DEFAULT_USER_AGENT, // who am i CURLOPT_AUTOREFERER => true, // set referer on redirect CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect CURLOPT_TIMEOUT => 120, // timeout on response diff --git a/lib/Service/StatusService.php b/lib/Service/StatusService.php index 69a621e3c..09cea9e4a 100644 --- a/lib/Service/StatusService.php +++ b/lib/Service/StatusService.php @@ -92,12 +92,8 @@ class StatusService $time = 0; - [$major, $minor, $micro] = Util::getVersion(); - - if ($major >= 26) { - $myJobList = $this->jobList->getJobsIterator(UpdaterJob::class, 1, 0); - $time = $myJobList->current()->getLastRun(); - } + $myJobList = $this->jobList->getJobsIterator(UpdaterJob::class, 1, 0); + $time = $myJobList->current()->getLastRun(); return $time; } diff --git a/lib/Service/UpdaterService.php b/lib/Service/UpdaterService.php index 5ab553d10..a67376de7 100644 --- a/lib/Service/UpdaterService.php +++ b/lib/Service/UpdaterService.php @@ -14,6 +14,9 @@ namespace OCA\News\Service; +use OCP\BackgroundJob\IJobList; +use OCA\News\Cron\UpdaterJob; + class UpdaterService { @@ -32,14 +35,19 @@ class UpdaterService */ private $itemService; + /** @var IJobList */ + private $jobList; + public function __construct( FolderServiceV2 $folderService, FeedServiceV2 $feedService, - ItemServiceV2 $itemService + ItemServiceV2 $itemService, + IJobList $jobList ) { $this->folderService = $folderService; $this->feedService = $feedService; $this->itemService = $itemService; + $this->jobList = $jobList; } @@ -60,4 +68,14 @@ class UpdaterService { $this->itemService->purgeOverThreshold(); } + + public function reset(): int + { + $myJobList = $this->jobList->getJobsIterator(UpdaterJob::class, 1, 0); + $job = $myJobList->current(); + + $this->jobList->resetBackgroundJob($job); + + return 0; + } } diff --git a/lib/Utility/Cache.php b/lib/Utility/Cache.php new file mode 100644 index 000000000..6337487cf --- /dev/null +++ b/lib/Utility/Cache.php @@ -0,0 +1,59 @@ +<?php +/** + * Nextcloud - News + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Benjamin Brahmer <info@b-brahmer.de> + * @copyright 2023 Benjamin Brahmer + */ +namespace OCA\News\Utility; + +use OCP\ITempManager; +use OCP\IConfig; + +class Cache +{ + + + /** + * @var ITempManager + */ + private $ITempManager; + + /** + * @var IConfig + */ + private $IConfig; + + + public function __construct( + ITempManager $ITempManager, + IConfig $IConfig + ) { + $this->ITempManager = $ITempManager; + $this->IConfig = $IConfig; + } + + /** + * Get a news app cache directory + * + * @param String $name for the sub-directory, is created if not existing + * + * @return String $directory The path for the cache + */ + public function getCache(String $name): String + { + $baseDir = $this->ITempManager->getTempBaseDir(); + $instanceID = $this->IConfig->getSystemValue('instanceid'); + + $directory = join(DIRECTORY_SEPARATOR, [$baseDir, "news-" . $instanceID, 'cache', $name]); + + if (!is_dir($directory)) { + mkdir($directory, 0770, true); + } + + return $directory; + } +} |