summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDevlin Junker <devlin.junker@gmail.com>2023-10-22 13:57:14 -0700
committerDevlin Junker <devlin.junker@gmail.com>2023-10-22 14:03:52 -0700
commit3e35d781cf712efa1473b4495a48299326b52c0c (patch)
tree9643b2e75ee3139bb6c5abb102d6f69937a5169e /lib
parent94f04d4a20cbbadb7d8ad835bc7ae3dc1c4256fd (diff)
parentd423ca520ca9c61233c33717d604e3183f0f486c (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.php12
-rw-r--r--lib/Command/Updater/Job.php76
-rw-r--r--lib/Config/FetcherConfig.php25
-rw-r--r--lib/Controller/PageController.php1
-rw-r--r--lib/Fetcher/Client/FeedIoClient.php8
-rwxr-xr-xlib/Fetcher/FeedFetcher.php53
-rw-r--r--lib/Scraper/Scraper.php3
-rw-r--r--lib/Service/StatusService.php8
-rw-r--r--lib/Service/UpdaterService.php20
-rw-r--r--lib/Utility/Cache.php59
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;
+ }
+}