├── pages ├── index.php ├── system.cache_warmup.start.php ├── generator.php └── warmup.php ├── assets ├── css │ └── cache-warmup.css └── js │ ├── timer.jquery.min.js │ ├── cache-warmup.js │ └── handlebars.min.js ├── .github └── workflows │ └── publish-to-redaxo.yml ├── lib ├── generator_images.php ├── writer.php ├── generator.php ├── generator_pages.php └── selector.php ├── package.yml ├── LICENSE ├── lang ├── en_gb.lang ├── sv_se.lang ├── de_de.lang ├── es_es.lang └── pt_br.lang ├── boot.php ├── CHANGELOG.md └── README.md /pages/index.php: -------------------------------------------------------------------------------- 1 | i18n('title')); 6 | rex_be_controller::includeCurrentPageSubPath(); 7 | -------------------------------------------------------------------------------- /assets/css/cache-warmup.css: -------------------------------------------------------------------------------- 1 | .panel-cache-warmup .panel-body { 2 | height: 250px; 3 | overflow: hidden; 4 | overflow-y: auto; 5 | -webkit-overflow-scrolling: touch; 6 | } 7 | 8 | /* hide be_style customizer title here as our popup is too small! */ 9 | #rex-page-cache-warmup-warmup .be-style-customizer-title { 10 | display: none; 11 | } -------------------------------------------------------------------------------- /.github/workflows/publish-to-redaxo.yml: -------------------------------------------------------------------------------- 1 | name: Publish release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | redaxo_publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: FriendsOfREDAXO/installer-action@v1 14 | with: 15 | myredaxo-username: ${{ secrets.MYREDAXO_USERNAME }} 16 | myredaxo-api-key: ${{ secrets.MYREDAXO_API_KEY }} 17 | description: ${{ github.event.release.body }} 18 | -------------------------------------------------------------------------------- /pages/system.cache_warmup.start.php: -------------------------------------------------------------------------------- 1 | ' . rex_i18n::rawMsg('cache_warmup_description') . '

'; 6 | $content .= '

' . rex_i18n::rawMsg('cache_warmup_button_start') . '

'; 7 | 8 | $fragment = new rex_fragment(); 9 | $fragment->setVar('title', rex_i18n::rawMsg('cache_warmup_title')); 10 | $fragment->setVar('body', $content, false); 11 | echo $fragment->parse('core/page/section.php'); 12 | -------------------------------------------------------------------------------- /lib/generator_images.php: -------------------------------------------------------------------------------- 1 | isAvailable()) { 17 | foreach ($items as $item) { 18 | [$image, $type] = $item; 19 | rex_media_manager::create($type, $image); 20 | } 21 | } 22 | return $items; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/writer.php: -------------------------------------------------------------------------------- 1 | setSubject(false); 15 | }); 16 | } 17 | 18 | /** 19 | * Build JSON object from array. 20 | * 21 | * @param array $items 22 | * @return string 23 | */ 24 | public static function buildJSON(array $items): string 25 | { 26 | return (string) json_encode($items); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/generator.php: -------------------------------------------------------------------------------- 1 | isAvailable()) { 17 | foreach ($items as $item) { 18 | [$article_id, $clang] = $item; 19 | 20 | // generate content 21 | $article = new rex_article_content($article_id, $clang); 22 | $article->getArticleTemplate(); 23 | 24 | // generate meta 25 | rex_article_cache::generateMeta($article_id, $clang); 26 | 27 | // generate lists 28 | rex_article_cache::generateLists($article_id); 29 | } 30 | } 31 | return $items; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /package.yml: -------------------------------------------------------------------------------- 1 | package: cache_warmup 2 | version: '4.0.0' 3 | author: Friends Of REDAXO 4 | supportpage: https://github.com/FriendsOfREDAXO/cache_warmup 5 | 6 | page: 7 | title: translate:title 8 | perm: admin 9 | popup: true 10 | hidden: true 11 | subpages: 12 | warmup: 13 | title: translate:title 14 | popup: true 15 | hidden: true 16 | generator: 17 | title: generator 18 | hidden: true 19 | hasLayout: false 20 | 21 | pages: 22 | system/cache_warmup: 23 | title: translate:title 24 | perm: admin 25 | subpages: 26 | start: { title: translate:page_start } 27 | readme: { title: translate:page_readme, subPath: README.md } 28 | 29 | requires: 30 | redaxo: '^5.4' 31 | php: '>=7.4' 32 | 33 | # define conflicts: prevents packages from update to avoid breaking changes 34 | conflicts: 35 | packages: 36 | media_manager: '>=3' 37 | metainfo: '>=3' 38 | structure: '>=3' 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Friends Of REDAXO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pages/generator.php: -------------------------------------------------------------------------------- 1 | isValid()) { 20 | $proceed = false; // token not valid, do not proceed! 21 | } 22 | rex::setProperty('redaxo', $mode); // reset to previous mode 23 | } 24 | 25 | if (!$proceed) { 26 | // do not proceed but send a 403 forbidden 27 | rex_response::cleanOutputBuffers(); 28 | rex_response::setStatus(rex_response::HTTP_FORBIDDEN); 29 | } else { 30 | // generate page cache 31 | $pages = rex_get('pages', 'string'); 32 | if (strlen($pages) > 0) { 33 | $generator = new cache_warmup_generator_pages(); 34 | $items = cache_warmup_generator::prepareItems($pages); 35 | $generator->generateCache($items); 36 | } 37 | 38 | // generate image cache 39 | $images = rex_get('images', 'string'); 40 | if (strlen($images) > 0) { 41 | $generator = new cache_warmup_generator_images(); 42 | $items = cache_warmup_generator::prepareItems($images); 43 | $items = cache_warmup_selector::getImageNames($items); 44 | $generator->generateCache($items); 45 | } 46 | 47 | // clear output 48 | cache_warmup_writer::clearOutput(); 49 | rex_response::setStatus(rex_response::HTTP_OK); 50 | } 51 | -------------------------------------------------------------------------------- /lang/en_gb.lang: -------------------------------------------------------------------------------- 1 | cache_warmup_title = Cache Warmup 2 | cache_warmup_description = Generates cache files for all pages and used images in advance to improve the initial website performance. 3 | cache_warmup_page_start = Start 4 | cache_warmup_page_readme = Help and documentation 5 | 6 | cache_warmup_button_start = Generate cache 7 | cache_warmup_button_success = Cool, thanks! 8 | cache_warmup_button_cancel = Cancel 9 | cache_warmup_button_again = Try again 10 | 11 | cache_warmup_pages_title = Generating pages… 12 | cache_warmup_pages_progress = Page 1 of 13 | 14 | cache_warmup_images_title = Generating images… 15 | cache_warmup_images_progress = Image 1 of 16 | 17 | cache_warmup_time_elapsed = Time elapsed 18 | 19 | cache_warmup_finished_title = Finished: cache prepared. 20 | cache_warmup_finished_text =

The cache has been prepared for pages and images. It took some and will save users from waiting too long when calling up the website for the first time.

21 | 22 | cache_warmup_error_title = Damn, something went wrong! 23 | cache_warmup_error_text =

An error occured while generating the cache. Find details on the error page. We appreciate any support in improving the addon quality if you report issues at Github. Thank you!

24 | cache_warmup_error_link = Link to error page 25 | 26 | cache_warmup_nothing_title = No content? 27 | cache_warmup_nothing_text =

I’m sorry but there’s nothing to cache. No images or text. Either REDAXO is empty or the required addons (media_manager for images and structure for content) are not available.

28 | -------------------------------------------------------------------------------- /lang/sv_se.lang: -------------------------------------------------------------------------------- 1 | cache_warmup_title = Cache Warmup 2 | cache_warmup_description = Genererar cachefiler för alla sidor och använda bilder i förväg för att förbättra prestandan vid första besöket på webbsidan. 3 | cache_warmup_page_start = Start 4 | cache_warmup_page_readme = Hjälp och dokumentation 5 | 6 | cache_warmup_button_start = Skapa cachen 7 | cache_warmup_button_success = Cool, tack! 8 | cache_warmup_button_cancel = Avbryta 9 | cache_warmup_button_again = Försök igen 10 | 11 | cache_warmup_pages_title = Skapa sidor ... 12 | cache_warmup_pages_progress = Sida 1 från 13 | 14 | cache_warmup_images_title = Skapa bilder ... 15 | cache_warmup_images_progress = Bild 1 från 16 | 17 | cache_warmup_time_elapsed = Tiden som gått 18 | 19 | cache_warmup_finished_title = Färdig: Cachen skapades 20 | cache_warmup_finished_text =

Cacheminnet har förberedds för sidor och bilder . Det tog lite och kommer att spara tid för användare från att vänta för länge när de öppnar webbplatsen för första gången.

21 | 22 | cache_warmup_error_title = Något har gått fel! 23 | cache_warmup_error_text =

Ett fel uppstod när cacheminnet genererades. Se detaljer på felsidan. Vi uppskattar allt stöd för att förbättra kvalitén av tillägget om du rapporterar problem på Github . Tack!

24 | cache_warmup_error_link = Länk till felsidan 25 | 26 | cache_warmup_nothing_title = Inget innehåll? 27 | cache_warmup_nothing_text =

Jag är ledsen men det finns inget att cachar. Inga bilder eller text. Antingen REDAXO är tom eller de nödvändiga tilläggen ( media_manager för bilder och struktur för innehåll) är inte tillgängliga.

28 | -------------------------------------------------------------------------------- /lang/de_de.lang: -------------------------------------------------------------------------------- 1 | cache_warmup_title = Cache-Warmup 2 | cache_warmup_description = Generiert den Cache für alle Seiten und verwendeten Bilder vorab, so dass die Website bereits beim ersten Aufruf performant läuft. 3 | cache_warmup_page_start = Start 4 | cache_warmup_page_readme = Hilfe und Dokumentation 5 | 6 | cache_warmup_button_start = Cache jetzt generieren 7 | cache_warmup_button_success = Prima, danke! 8 | cache_warmup_button_cancel = Abbrechen 9 | cache_warmup_button_again = Nochmal versuchen 10 | 11 | cache_warmup_pages_title = Generiere Seiten… 12 | cache_warmup_pages_progress = Seite 1 von 13 | 14 | cache_warmup_images_title = Generiere Bilder… 15 | cache_warmup_images_progress = Bild 1 von 16 | 17 | cache_warmup_time_elapsed = Verstrichene Zeit 18 | 19 | cache_warmup_finished_title = Fertig: Cache generiert 20 | cache_warmup_finished_text =

Der Cache wurde für Seiten und Bilder vorbereitet. Das hat etwa gedauert und sorgt dafür, dass nun niemand mehr bei den ersten Seitenaufrufen unnötig lange warten muss.

21 | 22 | cache_warmup_error_title = Verdammt, etwas ist schief gelaufen! 23 | cache_warmup_error_text =

Beim Erzeugen des Caches ist ein Fehler aufgetreten. Details befinden sich auf der Fehlerseite. Wir freuen uns über jede Mithilfe, die Qualität des Addons zu verbessern, indem Fehler bei Github gemeldet werden. Vielen Dank!

24 | cache_warmup_error_link = Link zur Fehlerseite 25 | 26 | cache_warmup_nothing_title = Keine Inhalte? 27 | cache_warmup_nothing_text =

Tut mir leid, aber es gibt nichts zum Cachen. Keine Bilder und keine Texte. Entweder ist REDAXO leer, oder die notwendigen Addons (media_manager für Bilder und structure für Inhalte) sind nicht installiert.

28 | -------------------------------------------------------------------------------- /lang/es_es.lang: -------------------------------------------------------------------------------- 1 | cache_warmup_title = Calentamiento de caché 2 | cache_warmup_description = Genera la memoria caché para todas las páginas y las imágenes usadas por adelantado, de modo que el sitio web se ejecuta rápidamente en la primera llamada. 3 | cache_warmup_page_start = Comienzo 4 | cache_warmup_page_readme = Ayuda y documentación 5 | 6 | cache_warmup_button_start = Generar caché 7 | cache_warmup_button_success = Genial, gracias 8 | cache_warmup_button_cancel = Cancelar 9 | cache_warmup_button_again = Intenta de nuevo 10 | 11 | cache_warmup_pages_title = Generar páginas 12 | cache_warmup_pages_progress = Página 1 de ... 13 | 14 | cache_warmup_images_title = Generando imágenes... 15 | cache_warmup_images_progress = Imagen 1 de 16 | 17 | cache_warmup_time_elapsed = Tiempo transcurrido 18 | 19 | cache_warmup_finished_title = Terminado: caché generado 20 | cache_warmup_finished_text =

La memoria caché se ha preparado para páginas y imágenes . Tomó un poco de y quieren ahorrar a los usuarios de esperar demasiado tiempo Al llamar el sitio web por primera vez.

21 | 22 | cache_warmup_error_title = Maldición, algo salió mal! 23 | cache_warmup_error_text =

Se produjo un error al generar el caché. Encuentra detalles en la página de error. Github. Agradecemos cualquier ayuda para mejorar la calidad del complemento. ¡Gracias!

24 | cache_warmup_error_link = Enlace a la página de error 25 | 26 | cache_warmup_nothing_title = ¿Sin contenido? 27 | cache_warmup_nothing_text =

Lo siento, pero no hay nada que almacenar en caché. No hay fotos ni textos O bien REDAXO está vacío, o los complementos necesarios (media_manager para imágenes y estructura para el contenido) no están instalados.

28 | -------------------------------------------------------------------------------- /lang/pt_br.lang: -------------------------------------------------------------------------------- 1 | cache_warmup_title = Cache Warmup 2 | cache_warmup_description = Gera arquivos de cache com antecedência para todas as páginas e imagens usadas para melhorar o desempenho inicial do site. 3 | cache_warmup_page_start = Começar 4 | cache_warmup_page_readme = Ajuda e documentação 5 | 6 | cache_warmup_button_start = Gera chachês 7 | cache_warmup_button_success = Legal, obrigada! 8 | cache_warmup_button_cancel = Cancelar 9 | cache_warmup_button_again = Tende de novo 10 | 11 | cache_warmup_pages_title = Gerando páginas.. 12 | cache_warmup_pages_progress = Página 1 of 13 | 14 | cache_warmup_images_title = Gerando imagens... 15 | cache_warmup_images_progress = Imagem 1 of 16 | 17 | cache_warmup_time_elapsed = Tempo decorrido 18 | 19 | cache_warmup_finished_title = Concluído: cachê preparado. 20 | cache_warmup_finished_text =

O cachê foi preparado para pages e imagens. Demorou um tempo

The cache has been prepared for pages and images. It took some E irá salvar os usuários de esperar muito tempo ao chamar o site pela primeira vez. 21 | 22 | cache_warmup_error_title = Merda, algo deu errado! 23 | cache_warmup_error_text =

Um erro ocorreu ao gerar o cachê. Mais detalhes na página. Nós apreciamos qualquer ajuda para melhorar a qualidade do addon, se você reportar os erros aqui: Github. Obrigado!

24 | cache_warmup_error_link = Lindo para a página de erros 25 | 26 | cache_warmup_nothing_title = Sem conteúdo? 27 | cache_warmup_nothing_text =

Desculpe-nos, mas não há nada no cachê. O REDAXO ou os addons requeridos estão vazios (media_manager para imagens structure e conteúdo) não estão disponíveis.

28 | -------------------------------------------------------------------------------- /boot.php: -------------------------------------------------------------------------------- 1 | ['min' => 10, 'max' => 50, 'ratio' => 0.4], 13 | 'chunkSizePages' => ['min' => 100, 'max' => 1000, 'ratio' => 6], 14 | ]; 15 | 16 | // get `max_execution_time` 17 | // if it’s 0 (false), set to a low value 18 | $executionTime = (int) ini_get('max_execution_time'); 19 | if (0 === $executionTime) { 20 | $executionTime = 30; 21 | } 22 | 23 | // define number of items to generate per single request based on `max_execution_time` 24 | // higher values reduce number of requests but extend script time 25 | // (hint: enable debug mode in REDAXO to report execution times) 26 | foreach ($chunksConfig as $k => $v) { 27 | $numOfItems = round($executionTime * $v['ratio']); 28 | 29 | if ($numOfItems > $v['max']) { 30 | // limit to max value 31 | // hint: executionTime === 0 equates to infinite! 32 | $this->setConfig($k, $v['max']); 33 | } elseif ($numOfItems < $v['min']) { 34 | // limit to min value 35 | $this->setConfig($k, $v['min']); 36 | } else { 37 | // set to calculated number of items 38 | $this->setConfig($k, $numOfItems); 39 | } 40 | } 41 | } 42 | 43 | // inject addon ressources 44 | if (rex::isBackend() && !is_null(rex::getUser()) && false !== strpos(rex_be_controller::getCurrentPage(), 'cache_warmup')) { 45 | if ('warmup' == rex_be_controller::getCurrentPagePart(2)) { 46 | rex_view::addJsFile($this->getAssetsUrl('js/handlebars.min.js')); 47 | rex_view::addJsFile($this->getAssetsUrl('js/timer.jquery.min.js')); 48 | } 49 | 50 | rex_view::addCssFile($this->getAssetsUrl('css/cache-warmup.css')); 51 | rex_view::addJsFile($this->getAssetsUrl('js/cache-warmup.js')); 52 | } 53 | 54 | // switch REDAXO to frontend mode before generating cache files 55 | // this is essential to include content modification by addons, e.g. slice status on/off 56 | rex_extension::register('PACKAGES_INCLUDED', static function () { 57 | if ('cache_warmup/generator' === rex_be_controller::getCurrentPage()) { 58 | rex::setProperty('redaxo', false); 59 | } 60 | }, rex_extension::EARLY); 61 | -------------------------------------------------------------------------------- /assets/js/timer.jquery.min.js: -------------------------------------------------------------------------------- 1 | /*! timer.jquery 0.7.1 2017-09-27*/ 2 | !function(n){var a={PLUGIN_NAME:"timer",TIMER_RUNNING:"running",TIMER_PAUSED:"paused",TIMER_REMOVED:"removed",DAYINSECONDS:86400};function s(t){var e;return t=t||0,e=Math.floor(t/60),{days:t>=a.DAYINSECONDS?Math.floor(t/a.DAYINSECONDS):0,hours:3600<=t?Math.floor(t%a.DAYINSECONDS/3600):0,totalMinutes:e,minutes:60<=t?Math.floor(t%3600/60):e,seconds:t%60,totalSeconds:t}}function r(t){return((t=parseInt(t,10))<10&&"0")+t}function e(){return Math.round((Date.now?Date.now():(new Date).getTime())/1e3)}function i(t){var e,n;return 0t.config.duration)&&(t.config.callback&&t.config.callback(),t.config.repeat||(clearInterval(t.intervalId),o(t,a.TIMER_STOPPED),t.config.duration=null))}};function c(t,e){if(this.element=t,this.originalConfig=n.extend({},e),this.totalSeconds=0,this.intervalId=null,this.html="html","INPUT"!==t.tagName&&"TEXTAREA"!==t.tagName||(this.html="val"),this.config=d.getDefaultConfig(),e.duration&&(e.duration=d.durationTimeToSeconds(e.duration)),"string"!=typeof e&&(this.config=n.extend(this.config,e)),this.config.seconds&&(this.totalSeconds=this.config.seconds),this.config.editable&&d.makeEditable(this),this.startTime=d.unixSeconds()-this.totalSeconds,this.config.duration&&this.config.repeat&&this.config.updateFrequency<1e3&&(this.config.updateFrequency=1e3),this.config.countdown){if(!this.config.duration)throw new Error("Countdown option set without duration!");if(this.config.editable)throw new Error("Cannot set editable on a countdown timer!");this.config.startTime=d.unixSeconds()-this.config.duration,this.totalSeconds=this.config.duration}}c.prototype.start=function(){this.state!==a.TIMER_RUNNING&&(d.setState(this,a.TIMER_RUNNING),this.render(),this.intervalId=setInterval(d.intervalHandler.bind(null,this),this.config.updateFrequency))},c.prototype.pause=function(){this.state===a.TIMER_RUNNING&&(d.setState(this,a.TIMER_PAUSED),clearInterval(this.intervalId))},c.prototype.resume=function(){this.state===a.TIMER_PAUSED&&(d.setState(this,a.TIMER_RUNNING),this.config.countdown?this.startTime=d.unixSeconds()-this.config.duration+this.totalSeconds:this.startTime=d.unixSeconds()-this.totalSeconds,this.intervalId=setInterval(d.intervalHandler.bind(null,this),this.config.updateFrequency))},c.prototype.remove=function(){clearInterval(this.intervalId),d.setState(this,a.TIMER_REMOVED),n(this.element).data(a.PLUGIN_NAME,null),n(this.element).data("seconds",null)},c.prototype.reset=function(){var t=this.originalConfig;this.remove(),n(this.element).timer(t)},c.prototype.render=function(){this.config.format?n(this.element)[this.html](d.secondsToFormattedTime(this.totalSeconds,this.config.format)):n(this.element)[this.html](d.secondsToPrettyTime(this.totalSeconds)),n(this.element).data("seconds",this.totalSeconds)},n.fn.timer=function(e){return e=e||"start",this.each(function(){n.data(this,a.PLUGIN_NAME)instanceof c||n.data(this,a.PLUGIN_NAME,new c(this,e));var t=n.data(this,a.PLUGIN_NAME);"string"==typeof e?"function"==typeof t[e]&&t[e]():t.start()})}}(jQuery); 3 | -------------------------------------------------------------------------------- /pages/warmup.php: -------------------------------------------------------------------------------- 1 | 7 |
8 |
9 |
'; 10 | 11 | $footer = ' 12 |
13 | 14 |
'; 15 | 16 | $fragment = new rex_fragment(); 17 | $fragment->setVar('class', 'cache-warmup'); 18 | $fragment->setVar('body', $body, false); 19 | $fragment->setVar('footer', $footer, false); 20 | echo $fragment->parse('core/page/section.php'); 21 | 22 | /* cache warmup items JSON */ 23 | echo ''; 24 | 25 | /* CSRF token (REX 5.5+) */ 26 | if (class_exists('rex_csrf_token')) { 27 | echo ''; 28 | } 29 | 30 | /* disable minibar (REX 5.7+) */ 31 | if (class_exists('rex_minibar') && null === rex_minibar::getInstance()->isActive()) { 32 | rex_minibar::getInstance()->setActive(false); 33 | } 34 | ?> 35 | 36 | 37 | 38 | 39 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 61 | 62 | 63 | 68 | 69 | 70 | 71 | 72 | 75 | 76 | 77 | 80 | 81 | 82 | 85 | 86 | 87 | 90 | 91 | 92 | 95 | 96 | 97 | 98 | 99 | 102 | 103 | 104 | 107 | 108 | 109 | 110 | 111 | 114 | 115 | 116 | 119 | 120 | 121 | 124 | 125 | 126 | 127 | 128 | 131 | 132 | 133 | 136 | 137 | 138 | 141 | 142 | 143 | 144 | 145 | 148 | 149 | 150 | 151 | 152 | 155 | 156 | 157 | 163 | 164 | 165 | 170 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog: Cache Warmup 2 | 3 | 4 | ## [4.0.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/4.0.0) – 02.07.2022 5 | 6 | ### Breaking changes 7 | 8 | * Erfordert REDAXO 5.4 und PHP 7.4 als Mindestversionen 9 | Alter Code wurde entfernt, um die Komplexität zu verringern! 🦊 10 | 11 | 12 | ## [3.7.1](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.7.1) – 12.06.2022 13 | 14 | ### Bugfixes 15 | 16 | * Externe Pakete aktualisiert 17 | * Code aufgeräumt 18 | 19 | 20 | ## [3.7.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.7.0) – 20.11.2021 21 | 22 | ### Features 23 | 24 | * Dokumentation für den Dark Mode angepasst (REDAXO 5.13) 25 | * Konflikte mit YForm 4 entfernt 26 | * PHP-Mindestversion auf 7 erhöht 27 | 28 | 29 | ## [3.6.1](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.6.1) – 10.10.2019 30 | 31 | ### Bugfixes 32 | 33 | * Generator responds with HTTP 200 to provide YRewrite 2.6+ compat (#106) 34 | 35 | 36 | ## [3.6.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.6.0) – 10.02.2019 37 | 38 | ### Features 39 | 40 | * Funktioniert mit YForm 3 (#103) 41 | 42 | 43 | ## [3.5.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.5.0) – 24.12.2018 44 | 45 | ### Features 46 | 47 | * Debug-Modus wird nicht mehr innerhalb des JS aktiviert und deaktiviert, sondern hängt nun fest an REDAXOs Debug-Modus. (#92) 48 | * Dokumentation: Umgang mit Fehlern beim Warmup-Prozess 49 | 50 | 51 | ## [3.4.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.4.0) – 28.09.2018 52 | 53 | ### Features 54 | 55 | * Use `includeCurrentPageSubPath` (#94 @christophboecker) 56 | Requires at least REDAXO 5.1 57 | 58 | ### Bugfixes 59 | 60 | * fix wrong `rex_media::clearInstance` values (#97 @staabm) 61 | 62 | 63 | ## [3.3.1](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.3.1) – 14.07.2018 64 | 65 | ### Features 66 | 67 | * Performance: JS/CSS nur auf Warmup-Seiten laden (#83 @staabm) 68 | * Performance: Generierung von Medien optimiert (#84 @staabm) 69 | * Extension Points (EPs) zum Filtern der zu generierenden Objekte (#90 @schuer) 70 | * Spanische Übersetzung, Traducción en castellano. ¡Gracias! (#91 @nandes2062) 71 | 72 | Hilfe zur Benutzung der neuen Extension Points findet ihr in der README! 🚀 73 | 74 | 75 | ## [3.3.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.3.0) – 14.07.2018 76 | 77 | -> [3.3.1](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.3.1) 78 | 79 | 80 | ## [3.2.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.2.0) – 13.10.2017 81 | 82 | ### Features 83 | 84 | * Schwedisch. Tack så mycket @interweave-media! (#78) 85 | 86 | ### Bugfixes 87 | 88 | * Cache-Buster mittels AddOn-Version entfernt, weil REDAXO seit 5.3 einen eigenen mitbringt (#79) 89 | 90 | 91 | ## [3.1.1](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.1.1) – 29.09.2017 92 | 93 | ### Bugfixes 94 | 95 | * Konfiguration nur noch bei Aufruf der Warmup-Seite bearbeiten (#76 @IngoWinter) 96 | Verbessert die Performance und vermeidet Session-Lock-Probleme. 97 | 98 | 99 | ## [3.1.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.1.0) – 20.08.2017 100 | 101 | ### Features 102 | 103 | * YForm-Kompatibilität: Der Feldtyp `mediafile` (Uploads in den Medienpool) wird nun ebenfalls beachtet. (#74) 104 | 105 | 106 | ## [3.0.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/3.0.0) – 14.06.2017 107 | 108 | ### Features 109 | 110 | * __Template beim Generieren der Artikelcaches mit einbeziehen__ (#71) 111 | Bisher wurden nur die reinen Artikelinhalte generiert. Das war völlig okay, sorgte aber für Fehler, wenn innerhalb der Templates wichtige Dinge definiert wurden, so wie z. B. die Tabs der beliebten [Basisdemo](https://github.com/FriendsOfREDAXO/demo_base). Mit diesem Update lädt Cache-Warmup nicht mehr nur die Artikel, sondern auch die Templates drumrum. 112 | * __Generierung der Bildercaches über neue Funktionen des Media-Managers__ (#72) 113 | Der Media-Manager 2.3.0 enthält eine separate Methode (`rex_media_manager::create()`), um Cachefiles von Bildern mit allen Bildeffekten zu generieren. Diese benutzen wir nun auch für Cache-Warmup und haben unseren alten Code, der dafür notwendig war, als _deprecated_ markiert. 114 | 115 | ### Breaking changes 116 | 117 | * Artikelinhalte werden nun anders generiert als vorher, nämlich inklusive ihrer Templates drumrum. Weil das potentiell zu anderen Ergebnissen führen kann, als mit der vorherigen Version von Cache-Warmup, ist diese aktuelle Version ein _Major Release_. 118 | 119 | 120 | ## [2.3.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/2.3.0) – 10.06.2017 121 | 122 | ### Features 123 | 124 | * Extension Point (EP) `CACHE_WARMUP_IMAGES` hinzugefügt, um Entwickler\_innen die Möglichkeit zu geben, Bilder zu ergänzen, für die Cachefiles generiert werden. (@IngoWinter: #69, #70) 125 | 126 | 127 | ## [2.2.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/2.2.0) – 07.06.2017 128 | 129 | ### Features 130 | 131 | * Wechsel in den Frontend-Modus vorm Generieren der Cache-Files (#68) 132 | Dadurch werden auch Inhaltsanpassungen fremder AddOns beachtet, die mittels Extension Points die Ausgabe beeinflussen, z. B. der Slice-Status on/off durch [blÖcks](https://github.com/FriendsOfREDAXO/bloecks). 133 | * Portugiesisch, vielen Dank an Taina Soares! (#67) 134 | 135 | 136 | ## [2.1.1](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/2.1.1) – 29.03.2017 137 | 138 | Workaround für Nutzer:innen des [blÖcks](https://github.com/FriendsOfREDAXO/bloecks)-Addons: Cache-Warmup generiert vorerst keine Artikelinhalte mehr, solange der Slice-Status (Online/Offline) nicht beachtet wird. Mit diesem Update wird verhindert, dass Offline-Inhalte publiziert werden. 139 | 140 | Siehe Diskussion: https://github.com/FriendsOfREDAXO/cache_warmup/issues/65 141 | 142 | 143 | ## [2.1.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/2.1.0) – 13.03.2017 144 | 145 | ### Features 146 | 147 | * 💥 Cachefiles pro Request deutlich erhöht (#58) — Danke an alle fürs Abstimmen und Testen! 148 | * Konflikte mit anderen Addons/Plugins definiert (#51) 149 | 150 | ### Bugfixes 151 | 152 | * Unnötigen Parameter entfernt (#57, @staabm) 153 | * Darstellungsfehler mit dem be_style-Customizer behoben (#56) 154 | * Fehler beim Speicherüberlauf abfangen (#62) 155 | 156 | ### Security 157 | 158 | * ⚠️ Bilder-IDs prüfen und absichern (#63) — Danke @gharlan! 159 | 160 | 161 | ## [2.0.1](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/2.0.1) – 25.01.2017 162 | 163 | ### Bugfixes 164 | 165 | * Kompatibilität für REX <5.1 wiederhergestellt (#48) 166 | 167 | 168 | ## [2.0.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/2.0.0) – 20.01.2017 169 | 170 | Für dieses Release wurden einige Fehler behoben und Funktionen verbessert, so dass Cache-Warmup nun merklich weniger Speicher benötigt und deutlich schneller läuft als vorher. Hooray! 171 | 172 | ### Bugfixes & Improvements: 173 | 174 | * 💥 Speichernutzung beim Filtern von Bildern korrigiert (#36, @isospin @staabm @gharlan) 175 | * 💥 Speichernutzung beim Prüfen von Bildern massiv reduziert (#44, @gharlan) 176 | * 💥 Filterung der Artikel korrigiert (#43, @tbaddade @staabm) 177 | * Code vereinfacht und verbessert (#30 #34 #35, jeweils @staabm) 178 | * Addon-Beschreibung verbessert (#38) 179 | * PHP-Mindestversion definiert (#32) 180 | * `help.php` entfernt, so dass die Hilfefunktion nun den Inhalt der README anzeigt (#47) 181 | 182 | ### Breaking changes: 183 | 184 | * Sichtbarkeit einiger Methoden verringert (#33, @staabm) 185 | * `cache_warmup_writer::replaceOutputWith()` entfernt (#39) 186 | * `cache_warmup_selector::getLanguages()` entfernt (#43) 187 | 188 | 189 | ## [1.0.3](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/1.0.3) – 10.10.2016 190 | 191 | ### Bugfixes 192 | 193 | * Warnings im Systemlog unterbunden (#24) 194 | 195 | 196 | ## [1.0.2](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/1.0.2) – 18.08.2016 197 | 198 | ### Bugfixes 199 | 200 | * Popup nun mit Hinweis, falls keine Artikel oder Bilder zum Generieren vorhanden sind. #23 201 | * Bilder konnten nicht in Sprachen (clang) oder Medien (media) hinterlegt werden. #25 202 | 203 | 204 | ## [1.0.1](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/1.0.1) – 29.07.2016 205 | 206 | ### Bugfixes 207 | 208 | * Kleine PHP-Korrekturen und Aufräumarbeiten #19 209 | * Kleine JavaScript-Korrekturen und Aufräumarbeiten #20 210 | 211 | 212 | ## [1.0.0](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/1.0.0) – 02.06.2016 213 | 214 | ### Breaking changes 215 | 216 | * Unterstriche statt Bindestrichen (REDAXO-Standard, erforderlich für myREDAXO) #17 217 | 218 | 219 | ## [1.0.0-RC1](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/1.0.0-RC1) – 25.05.2016 220 | 221 | ### Bugfixes 222 | 223 | * Ajax Request mit Timestamp, um Caching zu vermeiden #15 224 | 225 | ### Breaking changes 226 | 227 | * Unterstriche statt Bindestrichen (REDAXO-Standard, erforderlich für myREDAXO) #16 228 | 229 | 230 | ## [1.0.0-beta4](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/1.0.0-beta4) – 20.05.2016 231 | 232 | ### Bugfixes 233 | 234 | * Assets-Caching vermeiden + kleine JS-Fixes #12 235 | * Fehler vermeiden, wenn kein Metainfo-Feld existiert #13 236 | * package.yml Verbesserungen #14 237 | 238 | 239 | ## [1.0.0-beta3](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/1.0.0-beta3) – 18.05.2016 240 | 241 | ### Bugfixes 242 | 243 | * JS-Fehler & Popup-Größe #10 244 | 245 | 246 | ## [1.0.0-beta2](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/1.0.0-beta2) – 18.05.2016 247 | 248 | ### Bugfixes 249 | 250 | * Kontext für Subpage system.cache-warmup (REX-5.0-Kompatibilität) #6 251 | * Bilder werden in manchen Umgebungen zu groß generiert #7 252 | 253 | 254 | ## [1.0.0-beta1](https://github.com/FriendsOfREDAXO/cache_warmup/releases/tag/1.0.0-beta1) – 18.05.2016 255 | 256 | Erstes Beta-Release 257 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cache-Warmup 2 | 3 | Generiert den Cache vorab, so dass die Website bereits beim Erstaufruf performant läuft. 4 | 5 | ![Screenshot](https://raw.githubusercontent.com/FriendsOfREDAXO/cache-warmup/assets/cache-warmup.png) 6 | 7 | ## Wofür wird das Addon benötigt? 8 | 9 | Manchmal hinterlegt man eine Website zur Ansicht auf einem Testserver. Häufig wird davor oder danach der REDAXO-Cache gelöscht, um veraltete Inhalte zu entfernen, die vielleicht noch aus der Entwicklungszeit enthalten sind. Danach allerdings müssen alle Inhalte neu generiert werden. REDAXO übernimmt dies eigenständig beim Aufruf jeder Seite. 10 | 11 | Diese initialen Seitenaufrufe können leider recht langsam sein, vor allem, wenn der Cache für viele Bilder generiert werden muss. Nutzer*innen, denen die technischen Hintergründe nicht bekannt sind, und die erstmalig die Website anschauen, könnten nun (fälschlicherweise) annehmen, REDAXO sei nicht sonderlich schnell. Verständlich, denn sie erhalten im ersten Moment keine performante Website. 12 | 13 | Das Cache-Warmup-Addon kann alle verwendeten Inhalte der Website vorab generieren, so dass danach niemand mehr unnötig lange warten muss. 14 | 15 | 🐿 __Protip:__ Weil Cache-Warmup alle Bilder und Seiten einmal durchläuft, ist es nebenher sehr nützlich, um zu prüfen, ob die Website fehlerfrei ausgeliefert wird. Ob also keine Bilder zu groß sind, um vom Webserver verarbeitet zu werden, und ob alle Templates und Module richtig funktionieren. 16 | 17 | --- 18 | 19 | ## 🤕 Fehler beim Warmup-Prozess 20 | 21 | Es kommt immer wieder vor, dass Cache-Warmup nicht vollständig durchläuft, sondern vorher mit einem Fehler abbricht. An dieser Stelle ein paar Infos dazu, welche Fehler vorkommen können, und wie du damit umgehen kannst: 22 | 23 | ### `RAM exceeded (internal)` 24 | 25 | __Ursache:__ 26 | Der Arbeitsspeicher des Webservers reicht nicht aus, um alle Bilder zu verarbeiten. Dies ist ein Problem für deine Website, denn es bedeutet, dass die betroffenen Bilder nicht von REDAXOs Media Manager ausgegeben werden können, sondern stattdessen ein Fehlerbild angezeigt wird. 27 | 28 | __Maßnahmen:__ 29 | Du solltest nun unbedingt deinen Medienpool prüfen und alle übergroßen Bilder — das betrifft nicht die Dateigröße, sondern die Pixel! — manuell verkleinern und neu hochladen. Wenn beispielsweise Bilder in deinem Medienpool liegen, die mit einer Digitalkamera aufgenommen worden sind und unbearbeitet hochgeladen wurden, dann sind sie womöglich ~6000 Pixel breit, und REDAXO würde mehr RAM benötigen, um davon Thumbnails zu erstellen, als auf typischen Webspaces verfügbar ist. Wenn du sie auf ~3000 Pixel verkleinerst und neu in den Medienpool lädst, sollte es hoffentlich klappen! 30 | Wenn möglich, kannst du alternativ auch einfach den Arbeitsspeicher des Webservers vergrößern. 256 MB sollten gut funktionieren, wenn du mit großen Bildern arbeitest. 31 | 32 | ### `Request Timeout (408)` 33 | 34 | __Ursache:__ 35 | Das Generieren des Caches dauert zu lange, so dass die maximale Skriptlaufzeit (max\_execution\_time) des Webservers überschritten wird. Cache-Warmup versucht zwar, abhängig von der Skriptlaufzeit den Cache in kleinen Schritten zu generieren, aber manchmal — etwa bei sehr großen Bildern im Medienpool oder aufwendigen Templates/Modulen — sind selbst kleine Schritte noch zu groß. 36 | 37 | __Maßnahmen:__ 38 | Du kannst Cache-Warmup verlangsamen, indem du diesen Code-Schnipsel in die `boot.php` des project-AddOns steckst. Der Wert für `$throttle` entspricht der Geschwindigkeit (1–100 in Prozent) und kann von dir angepasst werden. Im Code-Schnipsel wird beispielhaft mit 50% gearbeitet, so dass Cache-Warmup nur noch halb so schnell arbeitet wie üblich: 39 | 40 | ```php 41 | // decelerate Cache Warmup addOn 42 | rex_extension::register('PACKAGES_INCLUDED', function () { 43 | $addon = rex_addon::get('cache_warmup'); 44 | $throttle = 50; // percent % 45 | 46 | $addon->setConfig('chunkSizeImages', ceil($addon->getConfig('chunkSizeImages') * $throttle / 100)); 47 | $addon->setConfig('chunkSizePages', ceil($addon->getConfig('chunkSizePages') * $throttle / 100)); 48 | }); 49 | ``` 50 | 51 | ### `Internal Server Error (500)` 52 | 53 | __Ursachen:__ 54 | Dieser Fehler kann verschiedene Ursachen haben. Manchmal entsteht er aufgrund von Fehlern im PHP-Code, z. B. innerhalb von Templates oder Modulen. In solchen Fällen müsstest du das Problem eigentlich auch auf der Website sehen, wenn du die betroffenen Seiten aufrufst. 55 | 56 | __Maßnahmen:__ 57 | Cache-Warmup bearbeitet normalerweise mehrere Seiten in einem Rutsch (etwa 100 bis 1000) und kann leider nicht unterscheiden, auf welchen davon Fehler auftreten. Um das herauszufinden, kannst du den Warmup für jede Seite einzeln durchlaufen lassen, indem du diesen Code-Schnipsel in die `boot.php` des project-AddOns mit aufnimmst: 58 | 59 | ```php 60 | // run Cache Warmup addOn in single page mode 61 | rex_extension::register('PACKAGES_INCLUDED', function () { 62 | rex_addon::get('cache_warmup')->setConfig('chunkSizePages', 1); 63 | }); 64 | ``` 65 | 66 | Womöglich dauert der Warmup-Vorgang nun sehr lange, das ist leider nicht zu ändern. Aber die URL der Fehlerseite liefert dir nun zumindest die ID samt ctype der Seite, auf der das Problem aufgetreten ist. 67 | 68 | --- 69 | 70 | ## Extension Points (EP) 71 | 72 | Das AddOn stellt verschiedene Extension Points bereit, um in die Auswahl der Artikel und Bilder, deren Cachefiles generiert werden sollen, manuell einzugreifen. Dies kann nützlich sein, um etwa Bilder zu ergänzen, die aus verschiedenen Gründen nicht vom AddOn erfasst worden sind, oder um bestimmte Kategorien oder Medientypen vom Generieren des Caches auszuschließen. 73 | 74 | | Extension Point | Beschreibung | 75 | | --------------------------------------- | ------------ | 76 | | `CACHE_WARMUP_GENERATE_PAGE` | Enthält den zu generierenden Artikel und die Sprache. Kann verwendet werden, um Artikel anhand verschiedener Kriterien auszulassen, wenn der Cache generiert wird. | 77 | | `CACHE_WARMUP_GENERATE_IMAGE` | Enthält das zu generierende Bild und den Medientyp. Kann verwendet werden, um Bilder anhand verschiedener Kriterien auszulassen, wenn der Cache generiert wird. | 78 | | `CACHE_WARMUP_IMAGES` | Ermöglicht, die Liste der vom AddOn ausgewählten Bilder zu bearbeiten. | 79 | | `CACHE_WARMUP_MEDIATYPES` | Ermöglicht, die Liste der vom AddOn ausgewählten Medientypen zu bearbeiten. | 80 | | `CACHE_WARMUP_PAGES_WITH_CLANGS` | Liefert alle zu generierenden Artikel in ihren Sprachen. Kann verwendet werden, um die Artikelliste zu bearbeiten, vor allem, um weitere Artikel mit Angabe der Sprache zu ergänzen. | 81 | | `CACHE_WARMUP_IMAGES_WITH_MEDIATYPES` | Liefert alle zu generierenden Bilder mit ihren Medientypen. Kann verwendet werden, um die Bilderliste zu bearbeiten, vor allem, um weitere Bilder mit Angabe des Medientyps zu ergänzen. | 82 | 83 | ## Anwendungsbeispiele für die Nutzung von EPs 84 | 85 | Die Beispiele zeigen verschiedene Anwendungsfälle und können beispielsweise __in der `boot.php` des project-AddOns__ hinterlegt werden. 86 | 87 | ### `CACHE_WARMUP_GENERATE_PAGE` 88 | 89 | Dieser EP wird unmittelbar vorm Generieren der Cachefiles jedes einzelnen Artikels angesprochen und ermöglicht, anhand verschiedener Kriterien den Artikel zu überspringen. Das Codebeispiel zeigt verschiedene Anwendungsfälle: 90 | 91 | ```php 92 | rex_extension::register('CACHE_WARMUP_GENERATE_PAGE', function (rex_extension_point $ep) { 93 | list($article_id, $clang) = $ep->getParams(); 94 | 95 | $article = rex_article::get($article_id); 96 | 97 | // Artikel mit ID 42 auslassen 98 | if ($article_id == 42) { 99 | return false; 100 | } 101 | 102 | // Artikel der Kategorie 23 und deren Kindkategorien auslassen 103 | if (in_array(23, $article->getPathAsArray())) { 104 | return false; 105 | } 106 | 107 | // Sprache mit clang 2 komplett auslassen 108 | if ($clang == 2) { 109 | return false; 110 | } 111 | 112 | return true; 113 | }); 114 | ``` 115 | 116 | ### `CACHE_WARMUP_GENERATE_IMAGE ` 117 | 118 | Dieser EP wird unmittelbar vorm Generieren der Cachefiles jedes einzelnen Bilders angesprochen und ermöglicht, anhand verschiedener Kriterien das Bild zu überspringen. Das Codebeispiel zeigt verschiedene Anwendungsfälle: 119 | 120 | ```php 121 | rex_extension::register('CACHE_WARMUP_GENERATE_IMAGE', function (rex_extension_point $ep) { 122 | list($image, $mediaType) = $ep->getParams(); 123 | 124 | $media = rex_media::get($image); 125 | if ($media) { 126 | if ($media->isImage()) { 127 | 128 | // Bilder vom Typ SVG auslassen 129 | if ($media->getExtension() == 'svg') { 130 | return false; 131 | } 132 | 133 | // Bilder der Kategorie 2 auslassen 134 | if ($media->getCategoryId() == 2) { 135 | return false; 136 | } 137 | 138 | // MediaType 'photos' ausschließlich für Bilder der Kategorie 3 verwenden 139 | if ($mediaType == 'photos' && $media->getCategoryId() != 3) { 140 | return false; 141 | } 142 | 143 | // MediaType 'fullscreen' auslassen 144 | if ($mediaType == 'fullscreen') { 145 | return false; 146 | } 147 | 148 | // Interne REDAXO-MediaTypes (beginnen mit 'rex_') auslassen 149 | if (strpos($mediaType, 'rex_') !== false) { 150 | return false; 151 | } 152 | } 153 | rex_media::clearInstance($image); 154 | } 155 | return true; 156 | }); 157 | ``` 158 | 159 | ### `CACHE_WARMUP_IMAGES ` 160 | 161 | Über diesen EP kann die Liste der vom AddOn erfassten Bilder modifiziert werden, um z. B. Bilder aus der Liste zu entfernen, deren Cachefiles nicht generiert werden sollen, oder um Bilder zu ergänzen, die aus verschiedenen Gründen nicht vom AddOn erfasst worden sind. 162 | 163 | ```php 164 | rex_extension::register('CACHE_WARMUP_IMAGES', function (rex_extension_point $ep) { 165 | $images = $ep->getSubject(); 166 | 167 | // Bilder hinzufügen 168 | $images[] = 'dave-grohl.jpg'; 169 | $images[] = 'pat-smear.jpg'; 170 | $images[] = 'nate-mendel.jpg'; 171 | $images[] = 'taylor-hawkins.jpg'; 172 | $images[] = 'chris-shiflett.jpg'; 173 | 174 | return $images; 175 | }); 176 | ``` 177 | 178 | ### `CACHE_WARMUP_MEDIATYPES ` 179 | 180 | Über diesen EP können die im System hinterlegten Mediatypen modifiziert werden, um z. B. Mediatypen aus der Liste zu entfernen, die nicht zum Generieren von Cachefiles verwendet werden sollen, oder um eigene Mediatypen zu ergänzen. 181 | 182 | ```php 183 | rex_extension::register('CACHE_WARMUP_MEDIATYPES', function (rex_extension_point $ep) { 184 | $mediaTypes = $ep->getSubject(); 185 | foreach ($mediaTypes as $k => $mediaType) { 186 | 187 | // MediaType 'content' auslassen 188 | if ($mediaType === 'content') { 189 | unset($mediaTypes[$k]); 190 | } 191 | 192 | // REDAXO-MediaTypes auslassen 193 | if (strpos($mediaType, 'rex_') !== false) { 194 | unset($mediaTypes[$k]); 195 | } 196 | } 197 | return $mediaTypes; 198 | }); 199 | ``` 200 | 201 | ### `CACHE_WARMUP_PAGES_WITH_CLANGS` 202 | 203 | Liefert alle zu generierenden Artikel in ihren Sprachen. Kann verwendet werden, um die Artikelliste zu bearbeiten, vor allem, um weitere Artikel mit Angabe der Sprache zu ergänzen, z. B. solche Artikel, die aufgrund ihres Offline-Status’ nicht vom AddOn erfasst worden sind. 204 | 205 | ```php 206 | rex_extension::register('CACHE_WARMUP_PAGES_WITH_CLANGS', function (rex_extension_point $ep) { 207 | $pages = $ep->getSubject(); 208 | 209 | // Seite hinzufügen (article_id, clang) 210 | $pages[] = array(12, 1); 211 | $pages[] = array(12, 2); 212 | 213 | return $pages; 214 | }); 215 | ``` 216 | 217 | ### `CACHE_WARMUP_IMAGES_WITH_MEDIATYPES ` 218 | 219 | Liefert alle zu generierenden Bilder mit ihren Medientypen. Kann verwendet werden, um die Bilderliste zu bearbeiten, vor allem, um weitere Bilder mit Angabe des Medientyps zu ergänzen. 220 | 221 | 🐿 __Protip:__ Dieser EP ist sehr nützlich im Umgang mit responsive Images und virtuellen Medientypen! 222 | 223 | ```php 224 | rex_extension::register('CACHE_WARMUP_IMAGES_WITH_MEDIATYPES', function (rex_extension_point $ep) { 225 | $images = $ep->getSubject(); 226 | 227 | // Bild mit MediaType hinzufügen 228 | $images[] = array('dave-grohl.jpg', 'portrait'); 229 | 230 | // Liste von Bildern mit Liste von MediaTypes hinzufügen 231 | $imagesToAdd = array( 232 | 'pat-smear.jpg', 233 | 'nate-mendel.jpg', 234 | 'taylor-hawkins.jpg', 235 | 'chris-shiflett.jpg' 236 | ); 237 | $mediaTypesToAdd = array( 238 | 'type1', 239 | 'type2', 240 | 'type3' 241 | ); 242 | foreach ($imagesToAdd as $image) { 243 | 244 | // Prüfen, Bilder vorhanden ist 245 | $media = rex_media::get($image); 246 | if ($media) { 247 | if ($media->isImage()) { 248 | 249 | // Bild mit Medientyp hinfügen 250 | foreach ($mediaTypesToAdd as $mediaType) { 251 | $images[] = array($image, $mediaType); 252 | } 253 | } 254 | rex_media::clearInstance($image); 255 | } 256 | } 257 | 258 | return $images; 259 | }); 260 | ``` 261 | 262 | --- 263 | 264 | ## Ich bin Entwickler*in. Was genau macht das Addon? 265 | 266 | 1. Es werden alle __Bilder__ erfasst, die in __Modulen, Metainfos und yforms__ verwendet werden, sowie alle definierten __MediaTypes__ des Media Managers. Verschiedene Extension Points (EPs) ermöglichen, die Liste der ausgewählten Bilder und MediaTypes zu bearbeiten, um zu kontrollieren, welche Cachefiles erstellt werden. 267 | 2. Es werden alle __Seiten__ erfasst, die online sind, sowie alle __Sprachen__. Verschiedene Extension Points (EPs) ermöglichen, die Liste zu bearbeiten. 268 | 3. Aus den erfassten Daten wird __ein großes Array erstellt__ mit Einträgen für jedes Bild mit jedem MediaType und jeder Seite in jeder Sprache. Beispiel: 10 Bilder mit 5 MediaTypes = 50 Bilder. 100 Seiten in 3 Sprachen = 300 Seiten. 269 | 4. Das große Array wird danach in viele Häppchen zerhackt, deren Größe von der __Skriptlaufzeit des Servers__ abhängt. Damit kann später gesteuert werden, wie viele Cachefiles bei jedem Request erstellt werden. Bilder benötigen dabei natürlich massiv mehr Serverressourcen als Seiten. 270 | 5. Das Array wird __als JSON im HTML des Popups__ ausgegeben, das das Generieren des Caches triggert, den Fortschritt zeigt und Infos ausgibt. Das Popup __parst das JSON__ und sendet __häppchenweise AJAX-Requests an einen Generator__. 271 | 6. Der Generator erstellt die Cachefiles für Bilder und Seiten. Die Angaben dazu, welche Bilder mit welchen Mediatypen und welche Seiten in welchen Sprachen erstellt werden sollen, befinden sich im __Query string__ der URL jedes AJAX-Request. 272 | -------------------------------------------------------------------------------- /lib/selector.php: -------------------------------------------------------------------------------- 1 | getConfig('chunkSizePages')); 30 | $images['items'] = self::chunk( 31 | $images['items'], 32 | rex_addon::get('cache_warmup')->getConfig('chunkSizeImages') 33 | ); 34 | } 35 | 36 | return [ 37 | 'pages' => $pages, 38 | 'images' => $images, 39 | ]; 40 | } 41 | 42 | /** 43 | * Get all images being used in REDAXO (pages, meta, yforms) 44 | * »X never, ever marks the spot.« (-- Indiana Jones). 45 | * 46 | * @throws rex_sql_exception 47 | * 48 | * @return array 49 | */ 50 | private static function getImages(): array 51 | { 52 | if (rex_addon::get('media_manager')->isAvailable() && rex_addon::get('structure')->isAvailable()) { 53 | $images = []; 54 | $sql = rex_sql::factory(); 55 | 56 | /* find images in pages (media1-10, medialist1-10) */ 57 | 58 | $select = 'media1,media2,media3,media4,media5,media6,media7,media8,media9,media10,medialist1,medialist2,medialist3,medialist4,medialist5,medialist6,medialist7,medialist8,medialist9,medialist10'; 59 | $sql->setQuery('SELECT '.$select.' FROM '.rex::getTablePrefix().'article_slice'); 60 | foreach ($sql as $row) { 61 | foreach (range(1, 10) as $num) { 62 | if (is_string($row->getValue('media'.$num))) { 63 | $images[] = $row->getValue('media'.$num); 64 | } 65 | if (is_string($row->getValue('medialist'.$num))) { 66 | $files = explode(',', $row->getValue('medialist'.$num)); 67 | foreach ($files as $file) { 68 | $images[] = $file; 69 | } 70 | } 71 | } 72 | } 73 | 74 | /* find images in yforms (be_media, be_medialist, mediafile) */ 75 | 76 | if (rex_addon::get('yform')->isAvailable()) { 77 | $yforms = []; 78 | 79 | // get tables and fields where 'be_media' and 'be_medialist' are used 80 | $sql->setQuery( 81 | 'SELECT table_name,name FROM '.rex::getTablePrefix( 82 | ).'yform_field WHERE type_name LIKE "be_media%" OR type_name LIKE "mediafile"' 83 | ); 84 | foreach ($sql as $row) { 85 | $yforms[$row->getValue('table_name')][] = $row->getValue('name'); 86 | } 87 | 88 | // walk through tables and find images 89 | foreach ($yforms as $table => $fields) { 90 | $sql->setQuery('SELECT '.implode(',', array_values($fields)).' FROM '.$table); 91 | foreach ($sql as $row) { 92 | foreach ($fields as $field) { 93 | $files = $row->getValue($field); 94 | if (strpos($files, ',') > 0) { 95 | // is medialist 96 | foreach (explode(',', $files) as $file) { 97 | $images[] = $file; 98 | } 99 | } else { 100 | // is media 101 | $images[] = $files; 102 | } 103 | } 104 | } 105 | } 106 | } 107 | 108 | /* find images in metainfos (REX_MEDIA_WIDGET, REX_MEDIALIST_WIDGET) */ 109 | 110 | if (rex_addon::get('metainfo')->isAvailable()) { 111 | $metainfos = []; 112 | 113 | // get 'REX_MEDIA_WIDGET' and 'REX_MEDIALIST_WIDGET' ids 114 | $sql->setQuery('SELECT id FROM '.rex::getTablePrefix().'metainfo_type WHERE label LIKE "REX_MEDIA%"'); 115 | foreach ($sql as $row) { 116 | $metainfos['ids'][] = $row->getValue('id'); 117 | } 118 | 119 | // get field names where 'REX_MEDIA_WIDGET' and 'REX_MEDIALIST_WIDGET' are used 120 | $sql->setQuery( 121 | 'SELECT name FROM '.rex::getTablePrefix().'metainfo_field WHERE type_id IN ('.implode( 122 | ',', 123 | $metainfos['ids'] 124 | ).')' 125 | ); 126 | foreach ($sql as $row) { 127 | $metainfos['names'][] = $row->getValue('name'); 128 | } 129 | 130 | // find images in metas (article, clang, media) 131 | if (isset($metainfos['names'])) { 132 | $tablesFrom = [ 133 | rex::getTablePrefix().'article', 134 | rex::getTablePrefix().'clang', 135 | rex::getTablePrefix().'media', 136 | ]; 137 | foreach ($tablesFrom as $table) { 138 | $sql->setQuery('SELECT * FROM '.$table); 139 | if ($sql->getRows() > 0) { 140 | foreach ($sql as $row) { 141 | foreach ($metainfos['names'] as $field) { 142 | if ($row->hasValue($field)) { 143 | $files = $row->getValue($field); 144 | if (strpos($files, ',') > 0) { 145 | // is medialist 146 | foreach (explode(',', $files) as $file) { 147 | $images[] = $file; 148 | } 149 | } else { 150 | // is media 151 | $images[] = $files; 152 | } 153 | } 154 | } 155 | } 156 | } 157 | } 158 | } 159 | } 160 | 161 | /* prepare and return ------------------------------------------------- */ 162 | 163 | // filter images 164 | return self::filterImages($images); 165 | } 166 | return []; 167 | } 168 | 169 | /** 170 | * Filter images: remove duplicate images and non-image items. 171 | * 172 | * @param array $items 173 | * @return array 174 | */ 175 | private static function filterImages(array $items): array 176 | { 177 | $filteredImages = []; 178 | 179 | $items = array_unique($items); // remove duplicate values 180 | 181 | foreach ($items as $item) { 182 | $media = rex_media::get($item); 183 | if (!is_null($media)) { 184 | if ($media->isImage()) { 185 | $filteredImages[] = $item; 186 | } 187 | rex_media::clearInstance($item); 188 | } 189 | } 190 | 191 | return $filteredImages; 192 | } 193 | 194 | /** 195 | * Get image IDs 196 | * returns sth like `array(17, 'content')` from `array('image.jpg', 'content')`. 197 | * 198 | * @param array $items 199 | * @return array 200 | */ 201 | public static function getImageIds(array $items): array 202 | { 203 | $filteredImages = []; 204 | 205 | foreach ($items as $item) { 206 | $media = rex_media::get($item[0]); 207 | if (!is_null($media)) { 208 | if ($media->isImage()) { 209 | $filteredImages[] = [(int) $media->getId(), $item[1]]; 210 | } 211 | rex_media::clearInstance($item); 212 | } 213 | } 214 | 215 | return $filteredImages; 216 | } 217 | 218 | /** 219 | * Get image names 220 | * returns sth like `array('image.jpg', 'portrait')` from `array(23, 'portrait')`. 221 | * 222 | * @param array $items 223 | * @throws rex_sql_exception 224 | * @return array 225 | */ 226 | public static function getImageNames(array $items): array 227 | { 228 | $filteredImages = []; 229 | 230 | // filter image ids 231 | $imageIds = array_column($items, 0); 232 | $imageIds = array_filter($imageIds, static function ($v) { 233 | return preg_match('/^\d+$/', $v) && (int) $v > 0; // sanitize 234 | }); 235 | $imageIds = array_unique($imageIds); 236 | 237 | // fetch images names for selected ids 238 | $images = []; 239 | $sql = rex_sql::factory(); 240 | $sql->setQuery( 241 | 'SELECT id, filename FROM '.rex::getTablePrefix().'media WHERE id IN ('.implode(',', $imageIds).')' 242 | ); 243 | foreach ($sql as $row) { 244 | $images[$row->getValue('id')] = $row->getValue('filename'); 245 | } 246 | 247 | // loop through items and replace ids with names 248 | foreach ($items as $item) { 249 | $filteredImages[] = [$images[$item[0]], $item[1]]; 250 | } 251 | 252 | return $filteredImages; 253 | } 254 | 255 | /** 256 | * Get all images and mediatypes as array including 'count' and 'items'. 257 | * 258 | * @return array 259 | */ 260 | private static function getImagesArray(): array 261 | { 262 | $images = self::getImages(); 263 | $mediaTypes = self::getMediaTypes(); 264 | 265 | // EPs to modify images and mediatypes 266 | $images = rex_extension::registerPoint(new rex_extension_point('CACHE_WARMUP_IMAGES', $images)); 267 | $mediaTypes = rex_extension::registerPoint(new rex_extension_point('CACHE_WARMUP_MEDIATYPES', $mediaTypes)); 268 | 269 | $items = []; 270 | if (count($images) > 0 && count($mediaTypes) > 0) { 271 | foreach ($images as $image) { 272 | $media = rex_media::get($image); 273 | if (!is_null($media)) { 274 | if ($media->isImage()) { 275 | foreach ($mediaTypes as $type) { 276 | // EP to control cache generation 277 | $generateImage = rex_extension::registerPoint( 278 | new rex_extension_point( 279 | 'CACHE_WARMUP_GENERATE_IMAGE', 280 | true, [$image, $type] 281 | ) 282 | ); 283 | 284 | if ($generateImage) { 285 | $items[] = [$image, $type]; 286 | } 287 | } 288 | } 289 | rex_media::clearInstance($media); 290 | } 291 | } 292 | } 293 | 294 | // EP to modify images with mediatypes 295 | $items = rex_extension::registerPoint(new rex_extension_point('CACHE_WARMUP_IMAGES_WITH_MEDIATYPES', $items)); 296 | 297 | return ['count' => count($items), 'items' => $items]; 298 | } 299 | 300 | /** 301 | * Get all media types as defined in media manager addon. 302 | * 303 | * @throws rex_sql_exception 304 | * 305 | * @return array 306 | */ 307 | private static function getMediaTypes(): array 308 | { 309 | if (rex_addon::get('media_manager')->isAvailable()) { 310 | $mediaTypes = []; 311 | 312 | $sql = rex_sql::factory(); 313 | $sql->setQuery('SELECT name FROM '.rex::getTablePrefix().'media_manager_type'); 314 | 315 | foreach ($sql as $row) { 316 | $mediaTypes[] = $row->getValue('name'); 317 | } 318 | 319 | return $mediaTypes; 320 | } 321 | return []; 322 | } 323 | 324 | /** 325 | * Get all pages being online. 326 | * 327 | * @throws rex_sql_exception 328 | * 329 | * @return array 330 | */ 331 | private static function getPages(): array 332 | { 333 | if (rex_addon::get('structure')->isAvailable()) { 334 | $query = 'SELECT a.id, a.clang_id FROM '.rex::getTable('article').' AS a INNER JOIN '.rex::getTable( 335 | 'clang' 336 | ).' AS c ON a.clang_id = c.id WHERE a.status = ? AND c.status = ?'; 337 | $params = [1, 1]; 338 | 339 | $sql = rex_sql::factory(); 340 | return $sql->getArray($query, $params, PDO::FETCH_NUM); 341 | } 342 | return []; 343 | } 344 | 345 | /** 346 | * Get all pages and languages as array including 'count' and 'items'. 347 | * 348 | * @return array 349 | */ 350 | private static function getPagesArray(): array 351 | { 352 | $pages = self::getPages(); 353 | 354 | $items = []; 355 | if (count($pages) > 0) { 356 | foreach ($pages as $page) { 357 | // EP to control cache generation 358 | $generatePage = rex_extension::registerPoint( 359 | new rex_extension_point( 360 | 'CACHE_WARMUP_GENERATE_PAGE', 361 | true, $page 362 | ) 363 | ); 364 | 365 | if ($generatePage) { 366 | $items[] = [(int) $page[0], (int) $page[1]]; 367 | } 368 | } 369 | } 370 | 371 | // EP to modify pages with clangs 372 | $items = rex_extension::registerPoint(new rex_extension_point('CACHE_WARMUP_PAGES_WITH_CLANGS', $items)); 373 | 374 | return ['count' => count($items), 'items' => $items]; 375 | } 376 | 377 | /** 378 | * Split an array into chunks. 379 | * 380 | * @param array $items 381 | * @param int $chunkSize 382 | * 383 | * @return array 384 | */ 385 | private static function chunk(array $items, int $chunkSize): array 386 | { 387 | return array_chunk($items, $chunkSize); 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /assets/js/cache-warmup.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | $(document).ready(function () { 3 | 4 | /* set debug mode */ 5 | 6 | var DEBUGMODE = false; 7 | if ($('.rex-is-debugmode').length) { 8 | DEBUGMODE = true; 9 | } 10 | 11 | 12 | /* debug log helper */ 13 | 14 | var debug = (function () { 15 | return { 16 | log: function () { 17 | var args = Array.prototype.slice.call(arguments); 18 | (DEBUGMODE) ? console.log.apply(console, args) : false; 19 | }, 20 | info: function () { 21 | var args = Array.prototype.slice.call(arguments); 22 | (DEBUGMODE) ? console.info.apply(console, args) : false; 23 | }, 24 | error: function () { 25 | var args = Array.prototype.slice.call(arguments); 26 | (DEBUGMODE) ? console.error.apply(console, args) : false; 27 | } 28 | } 29 | })(); 30 | 31 | 32 | /* warmup popup */ 33 | 34 | var popup = null; 35 | var popupButton = $('.cache-warmup__button-start'); 36 | var warmupButton = $('#rex-page-cache-warmup-warmup'); 37 | 38 | popupButton.on('click', function (e) { 39 | e.preventDefault(); 40 | 41 | var url = $(this).attr('href'); 42 | var title = 'Cache Warmup'; 43 | var parameters = 'left=' + (screen.width - 650) + ', top=50, height=471, width=600, menubar=no, location=no, resizable=no, status=no, scrollbars=yes'; 44 | 45 | if (popup === null || popup.closed) { 46 | popup = window.open(url, title, parameters); 47 | popup.resizeTo(600, 470); 48 | debug.log('open new popup: ', [title, url, parameters]); 49 | } 50 | else { 51 | popup.focus(); 52 | debug.log('focus popup: ', title); 53 | } 54 | }); 55 | 56 | warmupButton.on('click', '.cache-warmup__button--success, .cache-warmup__button--cancel', function (e) { 57 | e.preventDefault(); 58 | debug.log('close popup.'); 59 | window.close(); 60 | }); 61 | 62 | warmupButton.on('click', '.cache-warmup__button--again', function (e) { 63 | e.preventDefault(); 64 | document.location.reload(true); 65 | }); 66 | 67 | 68 | /** 69 | * Content 70 | * precompiles handlebar templates and injects content to page 71 | * 72 | * @param config 73 | * @constructor 74 | */ 75 | var Content = function (config) { 76 | debug.info('new Content'); 77 | 78 | // register templates 79 | if (config.templates && config.templates.length) { 80 | this._templates = {}; 81 | this._registerTemplates(config.templates); 82 | } 83 | }; 84 | 85 | Content.prototype = { 86 | 87 | _registerTemplates: function (templateSlugs) { 88 | if (templateSlugs.length) { 89 | templateSlugs.forEach(function (slug) { 90 | this._templates[slug] = Handlebars.compile($('#cache_warmup_tpl_' + slug).html()); 91 | }, this); 92 | debug.log('content: registered ' + templateSlugs.length + ' templates: ' + templateSlugs); 93 | return this; 94 | } 95 | }, 96 | 97 | _selectTarget: function (target) { 98 | var _target = $('.cache-warmup__target__' + target); 99 | return (_target.length) ? _target : undefined; 100 | }, 101 | 102 | injectTemplate: function (target, template) { 103 | if (this._templates[template]) { 104 | this._selectTarget(target).html(this._templates[template]()); 105 | debug.log('content: inject ' + template + ' to ' + target); 106 | } 107 | }, 108 | 109 | injectContent: function (target, content) { 110 | var _target = this._selectTarget(target); 111 | if (_target) { 112 | _target.html(content); 113 | debug.log('content: inject content to ' + target); 114 | } 115 | }, 116 | 117 | removeElement: function (target) { 118 | this._selectTarget(target).remove(); 119 | }, 120 | 121 | setFromValue: function (value) { 122 | $('.cache-warmup__target__progress-from').html(value); 123 | }, 124 | 125 | setToValue: function (value) { 126 | $('.cache-warmup__target__progress-to').html(value); 127 | } 128 | }; 129 | 130 | 131 | /** 132 | * Stopwatch 133 | * binds timer (external package) to given element selector 134 | * 135 | * @param selector 136 | * @constructor 137 | */ 138 | var Stopwatch = function (selector) { 139 | debug.info('new Stopwatch at "' + selector + '"'); 140 | 141 | this._selector = selector; 142 | this._el = null; 143 | }; 144 | 145 | Stopwatch.prototype = { 146 | 147 | start: function (value) { 148 | debug.log('stopwatch: started at ' + value); 149 | this._el = $(this._selector); 150 | this._el.timer({ 151 | 'seconds': value, 152 | 'format': '%H:%M:%S' 153 | }); 154 | }, 155 | 156 | pause: function () { 157 | this._el.timer('pause'); 158 | debug.log('stopwatch: stopped at ' + this._el.data('seconds')); 159 | }, 160 | 161 | getTime: function () { 162 | return this._el.data('seconds'); 163 | }, 164 | 165 | reset: function () { 166 | this.start(); 167 | debug.info('stopwatch: reset.'); 168 | } 169 | }; 170 | 171 | 172 | /** 173 | * Progressbar 174 | * controls the progress bar (bootstrap), sets to given value 175 | * 176 | * @param selector 177 | * @param value 178 | * @constructor 179 | */ 180 | var Progressbar = function (selector, value) { 181 | debug.info('new Progressbar at "' + selector + '" starting at ' + value); 182 | 183 | this._selector = selector; 184 | this._el = $(selector); 185 | this._value = value; 186 | this._min = 0; 187 | this._max = 100; 188 | }; 189 | 190 | Progressbar.prototype = { 191 | 192 | setProgress: function (value) { 193 | if (value > this._max) { 194 | this._value = this._max; 195 | } 196 | else if (value < this._min) { 197 | this._value = this._min; 198 | } 199 | else { 200 | this._value = value; 201 | } 202 | this._update(); 203 | debug.log('progressbar: set to ' + this._value); 204 | return this; 205 | }, 206 | 207 | getProgress: function () { 208 | return this._value; 209 | }, 210 | 211 | reset: function () { 212 | this._value = this._min; 213 | this._update(); 214 | debug.info('progressbar: reset.'); 215 | return this; 216 | }, 217 | 218 | _update: function () { 219 | this._el = $(this._selector); 220 | this._el.find('.progress-bar').css('width', this._value + '%'); 221 | return this; 222 | } 223 | }; 224 | 225 | 226 | /** 227 | * Calculator 228 | * stores number of chunks and calculates progress 229 | * does not care about items but chunks only 230 | * 231 | * @param config 232 | * @constructor 233 | */ 234 | var Calculator = function (config) { 235 | this._initialConfig = config; 236 | 237 | this._init(); 238 | debug.info('new Calculator :', JSON.stringify(this.config)); 239 | }; 240 | 241 | Calculator.prototype = { 242 | 243 | _init: function () { 244 | this.config = {}; 245 | for (var item in this._initialConfig) { 246 | if (this._initialConfig.hasOwnProperty(item)) { 247 | this.config[item] = this._initialConfig[item]; 248 | this.config[item].current = 0; 249 | } 250 | } 251 | }, 252 | 253 | reset: function () { 254 | this._init(); 255 | debug.info('calculator: reset.'); 256 | }, 257 | 258 | registerNextChunk: function (type) { 259 | this.config[type].current += 1; 260 | debug.log('calculator: set ' + type + ' to ' + this.config[type].current + '/' + this.config[type].total + ', overall progress at ' + this.getProgress() + '% now'); 261 | return this; 262 | }, 263 | 264 | getCurrent: function (type) { 265 | return this.config[type].current; 266 | }, 267 | 268 | getTotal: function (type) { 269 | return this.config[type].total; 270 | }, 271 | 272 | getProgress: function () { 273 | return this._calculateProgress(); 274 | }, 275 | 276 | _calculateProgress: function () { 277 | var finished = 0; 278 | var total = 0; 279 | for (var type in this.config) { 280 | if (this.config.hasOwnProperty(type)) { 281 | finished += this.getCurrent(type); 282 | total += this.getTotal(type); 283 | } 284 | } 285 | return Math.round(finished / total * 100); 286 | } 287 | }; 288 | 289 | 290 | /** 291 | * Config 292 | * prepares config JSON, returns URIs for generator requests 293 | * 294 | * @param itemsJSON 295 | * @param generatorUrl 296 | * @constructor 297 | */ 298 | var Config = function (itemsJSON, generatorUrl, token) { 299 | this._items = itemsJSON; 300 | this._generatorUrl = generatorUrl; 301 | this._token = token; 302 | 303 | if (!$.isEmptyObject(this._items) && this._generatorUrl.length) { 304 | debug.info("new Config for " + this._getDebugInfo() + "generator at " + generatorUrl + " with token " + token); 305 | } 306 | else { 307 | debug.error('new Config: no content.'); 308 | } 309 | }; 310 | 311 | Config.prototype = { 312 | 313 | _getDebugInfo: function () { 314 | var info = ''; 315 | var types = this.getItemTypes(); 316 | if (types.length) { 317 | types.forEach(function (entry) { 318 | info += this.getNumOfItems(entry) + ' ' + entry + ' (' + this.getNumOfChunks(entry) + ' chunks), '; 319 | }, this); 320 | return info; 321 | } 322 | }, 323 | 324 | hasItems: function () { 325 | return Object.keys(this._items).some(function(type) { 326 | return this._items[type].count > 0; 327 | }, this); 328 | }, 329 | 330 | getNumOfItems: function (type) { 331 | return this._items[type] ? this._items[type].count : 0; 332 | }, 333 | 334 | getNumOfChunks: function (type) { 335 | return this._items[type].items ? this._items[type].items.length : 0; 336 | }, 337 | 338 | getItemTypes: function () { 339 | return (Object.keys(this._items)); 340 | }, 341 | 342 | generateTokenParameter: function () { 343 | return this._token ? "&_csrf_token=" + this._token : null; 344 | }, 345 | 346 | getUrlsForType: function (type) { 347 | var urls = []; 348 | var chunk = []; 349 | if (this._items[type] && this._items[type].items.length) { 350 | for (var i = 0, imax = this._items[type].items.length; i < imax; i++) { 351 | chunk = []; 352 | for (var j = 0, jmax = this._items[type].items[i].length; j < jmax; j++) { 353 | chunk.push(this._items[type].items[i][j].join('.')); 354 | } 355 | urls.push({ 356 | 'absolute': this._generatorUrl + '&' + type + '=' + chunk.join() + this.generateTokenParameter(), 357 | 'slug': type + '=' + chunk.join(), 358 | 'itemsNum': jmax 359 | }); 360 | } 361 | } 362 | return urls; 363 | } 364 | }; 365 | 366 | 367 | /** 368 | * Cache 369 | * sends ajax request to generator file 370 | * 371 | * @param cacheWarmup 372 | * @constructor 373 | */ 374 | var Cache = function (cacheWarmup) { 375 | this.cacheWarmup = cacheWarmup; 376 | }; 377 | 378 | Cache.prototype = { 379 | 380 | generate: function (type, callback) { 381 | var timerStart; 382 | var timerEnd; 383 | var executionTimes = []; 384 | var that = this; 385 | var urls = this.cacheWarmup.config.getUrlsForType(type); 386 | var cachedItemsCount = 0; 387 | 388 | if (urls.length) { 389 | 390 | // loop through urls and send serial requests (not parallel!) 391 | urls.reduce(function (p, url, index) { 392 | return p.then(function () { 393 | timerStart = new Date().getTime(); 394 | // send request 395 | return $.ajax({ 396 | url: url.absolute, 397 | cache: false, 398 | beforeSend: function () { 399 | // update components 400 | // why not after request? because from UX view it feels better beforehand. 401 | debug.log('---'); 402 | cachedItemsCount += url.itemsNum; 403 | that.cacheWarmup.content.setFromValue(cachedItemsCount); 404 | that.cacheWarmup.calculator.registerNextChunk(type); 405 | that.cacheWarmup.progressbar.setProgress(that.cacheWarmup.calculator.getProgress()); 406 | } 407 | }) 408 | .done(function (data) { 409 | // special: error on success (http status 200) 410 | // media manager returns 200 even if an image cannot be generated (too big, RAM exceeded) 411 | // we assume an error if response starts with rex-page-header 412 | // otherwise page will return blank if stuff works out as expected 413 | if (data.substr(0, 30) === '