From a3ca70708536f14b1a006142496c8f0feaacbc71 Mon Sep 17 00:00:00 2001 From: Ian Morland Date: Mon, 8 Jun 2020 08:27:43 +0100 Subject: [PATCH] Add operation modes to the scheduler (#13) * WIP Add scheduler & config * Apply fixes from StyleCI * Create default setting * Clear the sitemap cache if mode is runtime * Register provider, add frequency selection, define defaults * Apply fixes from StyleCI * Add translations for freqency * Cleanup * Apply fixes from StyleCI * Add warning and link to info on setting up the scheduler * Add link to info on scheduler * Tidy * Build JS --- README.md | 4 + composer.json | 4 +- extend.php | 32 ++++++-- js/admin.js | 1 + js/dist/admin.js | 2 + js/dist/admin.js.map | 1 + js/package.json | 18 +++++ js/src/admin/index.js | 77 ++++++++++++++++++++ js/webpack.config.js | 3 + migrations/2020_06_07_000000_set_default.php | 31 ++++++++ resources/locale/en.yml | 26 +++++++ src/Commands/CacheSitemapCommand.php | 10 +++ src/Commands/MultiPageSitemapCommand.php | 10 +++ src/Controllers/SitemapController.php | 19 ++++- src/Disk/Home.php | 27 +++++-- src/Disk/Index.php | 34 +++++++-- src/Disk/Sitemap.php | 35 ++++++--- src/Extend/RegisterResource.php | 12 ++- src/Providers/ConsoleProvider.php | 69 ++++++++++++++++++ src/Providers/ResourceProvider.php | 18 ++++- src/Providers/ViewProvider.php | 12 ++- src/Resources/Discussion.php | 12 ++- src/Resources/Page.php | 12 ++- src/Resources/Resource.php | 18 ++++- src/Resources/Tag.php | 10 +++ src/Resources/User.php | 10 +++ src/Sitemap/Frequency.php | 10 +++ src/Sitemap/Url.php | 10 +++ src/Sitemap/UrlSet.php | 10 +++ src/SitemapGenerator.php | 15 +++- 30 files changed, 506 insertions(+), 46 deletions(-) create mode 100644 js/admin.js create mode 100644 js/dist/admin.js create mode 100644 js/dist/admin.js.map create mode 100644 js/package.json create mode 100644 js/src/admin/index.js create mode 100644 js/webpack.config.js create mode 100644 migrations/2020_06_07_000000_set_default.php create mode 100644 resources/locale/en.yml create mode 100644 src/Providers/ConsoleProvider.php diff --git a/README.md b/README.md index ec9e79c..3edc849 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,10 @@ return [ ``` That's it. +## Scheduling + +If the size of your forum requires one of the cache modes - either in-memory or disk, consider setting up the Flarum scheduler. Read more information about this [here](https://discuss.flarum.org/d/24118) + ## Commissioned The initial version of this extension was sponsored by [profesionalreview.com](https://www.profesionalreview.com/). diff --git a/composer.json b/composer.json index 5fc73af..71ba022 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,9 @@ "source": "https://github.com/FriendsOfFlarum/sitemap" }, "require": { - "flarum/core": ">=0.1.0-beta.12 <0.1.0-beta.14" + "flarum/core": ">=0.1.0-beta.13 <0.1.0-beta.14", + "fof/console": "^0.5.3", + "fof/components": "^0.1.1" }, "extra": { "flarum-extension": { diff --git a/extend.php b/extend.php index e4eea94..0276856 100644 --- a/extend.php +++ b/extend.php @@ -1,23 +1,41 @@ js(__DIR__.'/js/dist/admin.js'), + (new Extend\Routes('forum')) ->get('/sitemap.xml', 'fof-sitemap-index', SitemapController::class), + + new Extend\Locales(__DIR__.'/resources/locale'), + function (Application $app, Dispatcher $events) { $app->register(Providers\ResourceProvider::class); $app->register(Providers\ViewProvider::class); - - $events->listen(Configuring::class, function (Configuring $event) { - $event->addCommand(Commands\CacheSitemapCommand::class); - $event->addCommand(Commands\MultiPageSitemapCommand::class); - }); + $app->register(Providers\ConsoleProvider::class); }, + + (new Extend\Console())->command(Commands\CacheSitemapCommand::class), + (new Extend\Console())->command(Commands\MultiPageSitemapCommand::class), ]; diff --git a/js/admin.js b/js/admin.js new file mode 100644 index 0000000..3e69ff3 --- /dev/null +++ b/js/admin.js @@ -0,0 +1 @@ +export * from './src/admin'; diff --git a/js/dist/admin.js b/js/dist/admin.js new file mode 100644 index 0000000..3aacbaa --- /dev/null +++ b/js/dist/admin.js @@ -0,0 +1,2 @@ +module.exports=function(t){var e={};function n(a){if(e[a])return e[a].exports;var r=e[a]={i:a,l:!1,exports:{}};return t[a].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,a){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:a})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(n.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(a,r,function(e){return t[e]}.bind(null,r));return a},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=2)}([function(t,e){t.exports=flarum.core.compat.app},function(t,e){t.exports=flarum.extensions["fof-components"]},function(t,e,n){"use strict";n.r(e);var a=n(0),r=n.n(a),s=n(1),i=s.settings.SettingsModal,o=s.settings.items,l=(o.BooleanItem,o.SelectItem);r.a.initializers.add("fof/sitemap",(function(){r.a.extensionSettings["fof-sitemap"]=function(){return r.a.modal.show(new i({title:r.a.translator.trans("fof-sitemap.admin.settings.title"),type:"medium",items:[m("div",{className:"Form-group"},m("label",null,r.a.translator.trans("fof-sitemap.admin.settings.mode_label")),l.component({options:{run:r.a.translator.trans("fof-sitemap.admin.settings.modes.runtime"),cache:r.a.translator.trans("fof-sitemap.admin.settings.modes.cache"),"cache-disk":r.a.translator.trans("fof-sitemap.admin.settings.modes.cache_disk"),"multi-file":r.a.translator.trans("fof-sitemap.admin.settings.modes.multi_file")},key:"fof-sitemap.mode",required:!0})),m("p",null,r.a.translator.trans("fof-sitemap.admin.settings.mode_help")),m("div",null,m("h3",null,r.a.translator.trans("fof-sitemap.admin.settings.mode_help_runtime_label")),m("p",null,r.a.translator.trans("fof-sitemap.admin.settings.mode_help_runtime"))),m("h4",null,r.a.translator.trans("fof-sitemap.admin.settings.mode_help_schedule")),m("p",null,"Without the Flarum scheduler correctly setup, the following modes will not run. Refer here ",m("a",{href:"https://discuss.flarum.org/d/24118",target:"_blank"},"for more information.")),m("div",null,m("h3",null,r.a.translator.trans("fof-sitemap.admin.settings.mode_help_cache_disk_label")),m("p",null,r.a.translator.trans("fof-sitemap.admin.settings.mode_help_cache_disk"))),m("h4",null,r.a.translator.trans("fof-sitemap.admin.settings.mode_help_large")),m("div",null,m("h3",null,r.a.translator.trans("fof-sitemap.admin.settings.mode_help_multi_label")),m("p",null,r.a.translator.trans("fof-sitemap.admin.settings.mode_help_multi"))),m("hr",null),m("h3",null,r.a.translator.trans("fof-sitemap.admin.settings.advanced_options_label")),m("div",{className:"Form-group"},m("label",null,r.a.translator.trans("fof-sitemap.admin.settings.frequency_label")),l.component({options:{hourly:r.a.translator.trans("fof-sitemap.admin.settings.frequency.hourly"),"twice-daily":r.a.translator.trans("fof-sitemap.admin.settings.frequency.twice_daily"),daily:r.a.translator.trans("fof-sitemap.admin.settings.frequency.daily")},key:"fof-sitemap.frequency",required:!0}))]}))}}))}]); +//# sourceMappingURL=admin.js.map \ No newline at end of file diff --git a/js/dist/admin.js.map b/js/dist/admin.js.map new file mode 100644 index 0000000..c2c5590 --- /dev/null +++ b/js/dist/admin.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack://@fof/sitemap/webpack/bootstrap","webpack://@fof/sitemap/external \"flarum.core.compat['app']\"","webpack://@fof/sitemap/external \"flarum.extensions['fof-components']\"","webpack://@fof/sitemap/./src/admin/index.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","flarum","core","compat","extensions","SettingsModal","settings","items","SelectItem","BooleanItem","app","initializers","add","extensionSettings","modal","show","title","translator","trans","type","className","component","options","required","href","target"],"mappings":"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","file":"admin.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 2);\n","module.exports = flarum.core.compat['app'];","module.exports = flarum.extensions['fof-components'];","import app from 'flarum/app';\nimport { settings } from '@fof-components';\n\nconst {\n SettingsModal,\n items: { BooleanItem, SelectItem },\n} = settings;\n\napp.initializers.add('fof/sitemap', () => {\n app.extensionSettings['fof-sitemap'] = () =>\n app.modal.show(\n new SettingsModal({\n title: app.translator.trans('fof-sitemap.admin.settings.title'),\n type: 'medium',\n items: [\n
\n \n\n {SelectItem.component({\n options: {\n 'run': app.translator.trans('fof-sitemap.admin.settings.modes.runtime'),\n 'cache': app.translator.trans('fof-sitemap.admin.settings.modes.cache'),\n 'cache-disk': app.translator.trans('fof-sitemap.admin.settings.modes.cache_disk'),\n 'multi-file': app.translator.trans('fof-sitemap.admin.settings.modes.multi_file'),\n },\n key: 'fof-sitemap.mode',\n required: true\n })}\n
,\n

\n {app.translator.trans(\n 'fof-sitemap.admin.settings.mode_help'\n )}\n

,\n\n
\n

{app.translator.trans('fof-sitemap.admin.settings.mode_help_runtime_label')}

\n

{app.translator.trans('fof-sitemap.admin.settings.mode_help_runtime')}

\n
,\n

{app.translator.trans('fof-sitemap.admin.settings.mode_help_schedule')}

,\n

Without the Flarum scheduler correctly setup, the following modes will not run. Refer here for more information.

,\n
\n

{app.translator.trans('fof-sitemap.admin.settings.mode_help_cache_disk_label')}

\n

{app.translator.trans('fof-sitemap.admin.settings.mode_help_cache_disk')}

\n
,\n

{app.translator.trans('fof-sitemap.admin.settings.mode_help_large')}

,\n
\n

{app.translator.trans('fof-sitemap.admin.settings.mode_help_multi_label')}

\n

{app.translator.trans('fof-sitemap.admin.settings.mode_help_multi')}

\n
,\n
,\n

{app.translator.trans('fof-sitemap.admin.settings.advanced_options_label')}

,\n
\n \n\n {SelectItem.component({\n options: {\n 'hourly': app.translator.trans('fof-sitemap.admin.settings.frequency.hourly'),\n 'twice-daily': app.translator.trans('fof-sitemap.admin.settings.frequency.twice_daily'),\n 'daily': app.translator.trans('fof-sitemap.admin.settings.frequency.daily'),\n },\n key: 'fof-sitemap.frequency',\n required: true\n })}\n
,\n ],\n })\n );\n});\n"],"sourceRoot":""} \ No newline at end of file diff --git a/js/package.json b/js/package.json new file mode 100644 index 0000000..325fae3 --- /dev/null +++ b/js/package.json @@ -0,0 +1,18 @@ +{ + "name": "@fof/sitemap", + "version": "0.0.0", + "private": true, + "dependencies": { + "flarum-webpack-config": "^0.1.0-beta.10", + "webpack": "^4.26.0", + "webpack-cli": "^3.0.7" + }, + "scripts": { + "dev": "webpack --mode development --watch", + "build": "webpack --mode production", + "lint": "prettier --single-quote --trailing-comma es5 --print-width 150 --tab-width 4 --write src/**/*" + }, + "devDependencies": { + "prettier": "^1.19.1" + } +} diff --git a/js/src/admin/index.js b/js/src/admin/index.js new file mode 100644 index 0000000..4f0f80c --- /dev/null +++ b/js/src/admin/index.js @@ -0,0 +1,77 @@ +import app from 'flarum/app'; +import { settings } from '@fof-components'; + +const { + SettingsModal, + items: { BooleanItem, SelectItem }, +} = settings; + +app.initializers.add('fof/sitemap', () => { + app.extensionSettings['fof-sitemap'] = () => + app.modal.show( + new SettingsModal({ + title: app.translator.trans('fof-sitemap.admin.settings.title'), + type: 'medium', + items: [ +
+ + + {SelectItem.component({ + options: { + 'run': app.translator.trans('fof-sitemap.admin.settings.modes.runtime'), + 'cache': app.translator.trans('fof-sitemap.admin.settings.modes.cache'), + 'cache-disk': app.translator.trans('fof-sitemap.admin.settings.modes.cache_disk'), + 'multi-file': app.translator.trans('fof-sitemap.admin.settings.modes.multi_file'), + }, + key: 'fof-sitemap.mode', + required: true + })} +
, +

+ {app.translator.trans( + 'fof-sitemap.admin.settings.mode_help' + )} +

, + +
+

{app.translator.trans('fof-sitemap.admin.settings.mode_help_runtime_label')}

+

{app.translator.trans('fof-sitemap.admin.settings.mode_help_runtime')}

+
, +

{app.translator.trans('fof-sitemap.admin.settings.mode_help_schedule')}

, +

Without the Flarum scheduler correctly setup, the following modes will not run. Refer here for more information.

, +
+

{app.translator.trans('fof-sitemap.admin.settings.mode_help_cache_disk_label')}

+

{app.translator.trans('fof-sitemap.admin.settings.mode_help_cache_disk')}

+
, +

{app.translator.trans('fof-sitemap.admin.settings.mode_help_large')}

, +
+

{app.translator.trans('fof-sitemap.admin.settings.mode_help_multi_label')}

+

{app.translator.trans('fof-sitemap.admin.settings.mode_help_multi')}

+
, +
, +

{app.translator.trans('fof-sitemap.admin.settings.advanced_options_label')}

, +
+ + + {SelectItem.component({ + options: { + 'hourly': app.translator.trans('fof-sitemap.admin.settings.frequency.hourly'), + 'twice-daily': app.translator.trans('fof-sitemap.admin.settings.frequency.twice_daily'), + 'daily': app.translator.trans('fof-sitemap.admin.settings.frequency.daily'), + }, + key: 'fof-sitemap.frequency', + required: true + })} +
, + ], + }) + ); +}); diff --git a/js/webpack.config.js b/js/webpack.config.js new file mode 100644 index 0000000..18337e9 --- /dev/null +++ b/js/webpack.config.js @@ -0,0 +1,3 @@ +module.exports = require('flarum-webpack-config')({ + useExtensions: ['fof-components'] +}); diff --git a/migrations/2020_06_07_000000_set_default.php b/migrations/2020_06_07_000000_set_default.php new file mode 100644 index 0000000..d478cf0 --- /dev/null +++ b/migrations/2020_06_07_000000_set_default.php @@ -0,0 +1,31 @@ + function (Builder $schema) { + /** + * @var \Flarum\Settings\SettingsRepositoryInterface + */ + $settings = app('flarum.settings'); + + $settings->set('fof-sitemap.mode', 'run'); + $settings->set('fof-sitemap.frequency', 'daily'); + }, + 'down' => function (Builder $schema) { + $settings = app('flarum.settings'); + + $settings->delete('fof-sitemap.mode'); + $settings->delete('fof-sitemap.frequency'); + }, +]; diff --git a/resources/locale/en.yml b/resources/locale/en.yml new file mode 100644 index 0000000..6295b5c --- /dev/null +++ b/resources/locale/en.yml @@ -0,0 +1,26 @@ +fof-sitemap: + admin: + settings: + title: FoF Sitemap + mode_label: Operation mode + mode_help: Selecting the correct mode for your size of forum is vitally important. + mode_help_runtime_label: Runtime Mode + mode_help_runtime: For small forums, most likely on shared hosting environments, with discussions, users, tags and pages summed up being less than 10.000 items. The sitemap is generated on the fly at each request. + mode_help_schedule: The following modes add their tasks to the Flarum schedule. + mode_help_cache_disk_label: Cache Mode/Disk Mode + mode_help_cache_disk: Best for small or medium forums, most likely on hosting environments allowing cronjobs and enabling the Flarum scheduler (see below), and with discussions, users, tags and pages summed up being less than 50.000 items. + mode_help_large: 50.000 is the technical limit for sitemap files. If you have more entries to store, use the following option! + mode_help_multi_label: Multi-file Mode + mode_help_multi: Best for larger forums, starting at 50.000 items. Mult part, compressed sitemap files will be generated and stored in the /public folder + advanced_options_label: Advanced options + frequency_label: How often should the scheduler re-build the cache or disk based sitemap? + modes: + runtime: Runtime + cache: Cache + cache_disk: Disk + multi_file: Multi file + frequency: + hourly: Hourly + twice_daily: Twice daily + daily: Daily + diff --git a/src/Commands/CacheSitemapCommand.php b/src/Commands/CacheSitemapCommand.php index 8003018..ad99cc8 100644 --- a/src/Commands/CacheSitemapCommand.php +++ b/src/Commands/CacheSitemapCommand.php @@ -1,5 +1,15 @@ sitemap = $sitemap; $this->view = $view; $this->cache = $cache; + $this->settings = $settings; } protected function render(ServerRequestInterface $request) { + if ($this->settings->get('fof-sitemap.mode') === 'run') { + $this->cache->forget('fof-sitemap'); + } + $urlset = $this->cache->get('fof-sitemap') ?? $this->sitemap->getUrlSet(); return $this->view->make('fof-sitemap::sitemap') diff --git a/src/Disk/Home.php b/src/Disk/Home.php index 4ee03e6..7f2e373 100644 --- a/src/Disk/Home.php +++ b/src/Disk/Home.php @@ -1,5 +1,15 @@ EOM @@ -33,15 +45,16 @@ protected function chunk(string $directory): array fwrite( $stream, $this->view()->make('fof-sitemap::url')->with('url', (object) [ - 'location' => $this->url, - 'lastModified' => $now = Carbon::now(), + 'location' => $this->url, + 'lastModified' => $now = Carbon::now(), 'changeFrequency' => Frequency::DAILY, - 'priority' => 0.9 + 'priority' => 0.9, ])->render() ); - - fwrite($stream, << EOM ); diff --git a/src/Disk/Index.php b/src/Disk/Index.php index 272a89e..b220f91 100644 --- a/src/Disk/Index.php +++ b/src/Disk/Index.php @@ -1,5 +1,15 @@ $resource->url($model), + 'location' => $resource->url($model), 'changeFrequency' => $resource->frequency(), - 'lastModified' => $resource->lastModifiedAt($model), - 'priority' => $resource->priority() + 'lastModified' => $resource->lastModifiedAt($model), + 'priority' => $resource->priority(), ]; }, storage_path('sitemaps-processing/sitemaps') @@ -54,14 +64,18 @@ protected function saveIndexFile() { $stream = fopen(storage_path('sitemaps-processing/sitemap.xml'), 'w+'); - fwrite($stream, << EOM ); foreach ($this->sitemaps as $sitemap => $lastModified) { - fwrite($stream, << {$this->url}/sitemaps{$sitemap} {$lastModified->toW3cString()} @@ -70,7 +84,9 @@ protected function saveIndexFile() ); } - fwrite($stream, << EOM ); @@ -80,7 +96,9 @@ protected function saveIndexFile() public function publish() { - if (! is_dir(public_path("sitemaps"))) mkdir(public_path("sitemaps")); + if (!is_dir(public_path('sitemaps'))) { + mkdir(public_path('sitemaps')); + } foreach ($this->sitemaps as $sitemap => $_) { copy( diff --git a/src/Disk/Sitemap.php b/src/Disk/Sitemap.php index b399224..cbff5bc 100644 --- a/src/Disk/Sitemap.php +++ b/src/Disk/Sitemap.php @@ -1,5 +1,15 @@ tmpDir ?? public_path('sitemaps'); - if (! is_dir($directory)) { + if (!is_dir($directory)) { mkdir($directory, 0777, true); } @@ -60,13 +70,14 @@ public function each($item) protected function gzCompressFile($source, $level = 9) { - $dest = $source . '.gz'; - $mode = 'wb' . $level; + $dest = $source.'.gz'; + $mode = 'wb'.$level; $error = false; if ($fp_out = gzopen($dest, $mode)) { - if ($fp_in = fopen($source,'rb')) { - while (!feof($fp_in)) + if ($fp_in = fopen($source, 'rb')) { + while (!feof($fp_in)) { gzwrite($fp_out, fread($fp_in, 1024 * 512)); + } fclose($fp_in); } else { $error = true; @@ -75,10 +86,11 @@ protected function gzCompressFile($source, $level = 9) } else { $error = true; } - if ($error) + if ($error) { return false; - else + } else { return $dest; + } } protected function view(): Factory @@ -88,6 +100,7 @@ protected function view(): Factory /** * @param string $directory + * * @return array */ protected function chunk(string $directory): array @@ -101,7 +114,9 @@ protected function chunk(string $directory): array $stream = fopen($path = "$directory/$filename", 'w+'); - fwrite($stream, << EOM @@ -120,7 +135,9 @@ protected function chunk(string $directory): array ); }); - fwrite($stream, << EOM ); diff --git a/src/Extend/RegisterResource.php b/src/Extend/RegisterResource.php index 08d70d8..925de3e 100644 --- a/src/Extend/RegisterResource.php +++ b/src/Extend/RegisterResource.php @@ -1,5 +1,15 @@ resource} has to extend " . Resource::class); + throw new InvalidArgumentException("{$this->resource} has to extend ".Resource::class); } return $resources; diff --git a/src/Providers/ConsoleProvider.php b/src/Providers/ConsoleProvider.php new file mode 100644 index 0000000..c45be52 --- /dev/null +++ b/src/Providers/ConsoleProvider.php @@ -0,0 +1,69 @@ +app->make(SettingsRepositoryInterface::class); + + $mode = $settings->get('fof-sitemap.mode'); + if (empty($mode) || $mode === 'run') { + return; + } + + $frequency = $settings->get('fof-sitemap.frequency', 'daily'); + + $this->app->resolving(Schedule::class, function (Schedule $schedule) use ($mode, $frequency) { + switch ($mode) { + case 'multi-file': + $command = 'fof:sitemap:multi'; + break; + case 'cache': + $command = 'fof:sitemap:cache'; + break; + case 'cache-disk': + $command = 'fof:sitemap:cache --write-xml-file'; + break; + default: + return; + } + + $builder = $schedule->command($command); + + switch ($frequency) { + case 'hourly': + $builder->hourly(); + break; + case 'twice-daily': + $builder->twiceDaily(); + break; + case 'daily': + default: + $builder->daily(); + break; + } + + $builder->withoutOverlapping(); + }); + } +} diff --git a/src/Providers/ResourceProvider.php b/src/Providers/ResourceProvider.php index 10f5398..7068f19 100644 --- a/src/Providers/ResourceProvider.php +++ b/src/Providers/ResourceProvider.php @@ -1,5 +1,15 @@ app->singleton('fof.sitemap.resources', function () { $resources = [ - new Resources\User, - new Resources\Discussion + new Resources\User(), + new Resources\Discussion(), ]; /** @var ExtensionManager $extensions */ $extensions = $this->app->make(ExtensionManager::class); if ($extensions->isEnabled('flarum-tags')) { - $resources[] = new Resources\Tag; + $resources[] = new Resources\Tag(); } if ($extensions->isEnabled('fof-pages')) { - $resources[] = new Resources\Page; + $resources[] = new Resources\Page(); } return $resources; diff --git a/src/Providers/ViewProvider.php b/src/Providers/ViewProvider.php index 77c1eb5..f23c2e4 100644 --- a/src/Providers/ViewProvider.php +++ b/src/Providers/ViewProvider.php @@ -1,5 +1,15 @@ app['view']->addNamespace('fof-sitemap', realpath(__DIR__ . '/../../views')); + $this->app['view']->addNamespace('fof-sitemap', realpath(__DIR__.'/../../views')); } } diff --git a/src/Resources/Discussion.php b/src/Resources/Discussion.php index a5b3822..15f2d66 100644 --- a/src/Resources/Discussion.php +++ b/src/Resources/Discussion.php @@ -1,5 +1,15 @@ generateRouteUrl('discussion', [ - 'id' => $model->id . (trim($model->slug) ? '-' . $model->slug : ''), + 'id' => $model->id.(trim($model->slug) ? '-'.$model->slug : ''), ]); } diff --git a/src/Resources/Page.php b/src/Resources/Page.php index ac8ed80..9c6cab1 100644 --- a/src/Resources/Page.php +++ b/src/Resources/Page.php @@ -1,5 +1,15 @@ generateRouteUrl('pages.page', [ - 'id' => $model->id . (trim($model->slug) ? '-' . $model->slug : ''), + 'id' => $model->id.(trim($model->slug) ? '-'.$model->slug : ''), ]); } diff --git a/src/Resources/Resource.php b/src/Resources/Resource.php index 8941764..2b0e218 100644 --- a/src/Resources/Resource.php +++ b/src/Resources/Resource.php @@ -1,5 +1,15 @@ addUrl($this->url->to('forum')->base() . '/', Carbon::now(), Frequency::DAILY, 0.9); + $urlSet->addUrl($this->url->to('forum')->base().'/', Carbon::now(), Frequency::DAILY, 0.9); // If the homepage is different from /all, also add /all if ($this->settings->get('default_route') !== '/all') { @@ -40,7 +49,7 @@ public function getUrlSet() $resources = $this->app->make('fof.sitemap.resources') ?? []; - /** @var Resource $resource */ + /** @var FoF\Sitemap\Resources\Resource $resource */ foreach ($resources as $resource) { $resource->query()->each(function ($model) use (&$urlSet, $resource) { $urlSet->addUrl(