Browse Source

Ticket #4480 - Update manifest and add service worker.

AntonLV 3 years ago
parent
commit
01b59a2a2d

+ 9 - 9
inc/classes/BxDolTemplate.php

@@ -1528,7 +1528,7 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
     {
         $sUrl = $this->getCssUrl($sName, $sCheckIn);
         if(!empty($sUrl))
-            $sUrl = $this->_addRevision($sUrl);
+            $sUrl = $this->addRevision($sUrl);
 
         return $sUrl;
     }
@@ -1564,7 +1564,7 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
     {
         $sUrl = $this->getJsUrl($sName);
         if(!empty($sUrl))
-            $sUrl = $this->_addRevision($sUrl);
+            $sUrl = $this->addRevision($sUrl);
 
         return $sUrl;
     }
@@ -2165,7 +2165,7 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
 
             list($sUrl) = $mixedFile;
 
-            $aFilesLocated[] = $this->_addRevision($sUrl);
+            $aFilesLocated[] = $this->addRevision($sUrl);
         }
         $sFilesLocated = json_encode($aFilesLocated);
 
@@ -2369,7 +2369,7 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
 
             list($sUrl) = $mixedFile;
 
-            $aFilesLocated[] = $this->_addRevision($sUrl);
+            $aFilesLocated[] = $this->addRevision($sUrl);
         }
         $sFilesLocated = json_encode($aFilesLocated);
 
@@ -2741,7 +2741,7 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
 
             $sFileUrl = $aFile['url'];
             if(!$this->{'_b' . $sUpcaseType . 'Cache'})
-                $sFileUrl = $this->_addRevision($sFileUrl);
+                $sFileUrl = $this->addRevision($sFileUrl);
 
             if($bWrap)
                 $mixedResult .= $this->$sMethodWrap($sFileUrl);
@@ -2791,7 +2791,7 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
                     ]);
 
                     if($bDynamic)
-                        $sResult .= $this->$sMethodWrap($this->_addRevision($sUrl));
+                        $sResult .= $this->$sMethodWrap($this->addRevision($sUrl));
                     else {
                         $bFound = false;
                         $aSearchIn = $bSystem ? $this->aPage[$sArrayKey] : array_merge($this->aPage[$sType . '_system'], $this->aPage[$sArrayKey]);
@@ -3417,7 +3417,7 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
      * 
      * @return integer number
      */
-    function _getRevision()
+    public function getRevision()
     {
         return (int)getParam('sys_revision');
     }
@@ -3427,9 +3427,9 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
      * 
      * @return string with URL 
      */
-    function _addRevision($sUrl)
+    public function addRevision($sUrl)
     {
-        return bx_append_url_params($sUrl, ['rev' => $this->_getRevision()]);
+        return bx_append_url_params($sUrl, ['rev' => $this->getRevision()]);
     }
 
     /**

+ 16 - 7
install/sql/system.sql

@@ -753,18 +753,27 @@ INSERT INTO `sys_options`(`category_id`, `name`, `caption`, `value`, `type`, `ex
 
 
 --
--- CATEGORY (HIDDEN): PWA
+-- CATEGORY (HIDDEN): PWA Manifest
 --
-INSERT INTO `sys_options_categories`(`type_id`, `name`, `caption`, `hidden`, `order`) VALUES (@iTypeId, 'pwa', '_adm_stg_cpt_category_pwa', 1, 2);
+INSERT INTO `sys_options_categories`(`type_id`, `name`, `caption`, `hidden`, `order`) VALUES (@iTypeId, 'pwa_manifest', '_adm_stg_cpt_category_pwa_manifest', 1, 2);
 SET @iCategoryId = LAST_INSERT_ID();
 
 INSERT INTO `sys_options`(`category_id`, `name`, `caption`, `value`, `type`, `extra`, `check`, `check_error`, `order`) VALUES
-(@iCategoryId, 'sys_pwa_name', '_adm_stg_cpt_option_sys_pwa_name', '', 'digit', '', '', '', 1),
-(@iCategoryId, 'sys_pwa_short_name', '_adm_stg_cpt_option_sys_pwa_short_name', '', 'digit', '', '', '', 2),
-(@iCategoryId, 'sys_pwa_description', '_adm_stg_cpt_option_sys_pwa_description', '', 'text', '', '', '', 3), 
-(@iCategoryId, 'sys_pwa_background_color', '_adm_stg_cpt_option_sys_pwa_background_color', '', 'digit', '', '', '', 4),
-(@iCategoryId, 'sys_pwa_theme_color', '_adm_stg_cpt_option_sys_pwa_theme_color', '', 'digit', '', '', '', 5);
+(@iCategoryId, 'sys_pwa_manifest_name', '_adm_stg_cpt_option_sys_pwa_manifest_name', '', 'digit', '', '', '', 1),
+(@iCategoryId, 'sys_pwa_manifest_short_name', '_adm_stg_cpt_option_sys_pwa_manifest_short_name', '', 'digit', '', '', '', 2),
+(@iCategoryId, 'sys_pwa_manifest_description', '_adm_stg_cpt_option_sys_pwa_manifest_description', '', 'text', '', '', '', 3), 
+(@iCategoryId, 'sys_pwa_manifest_background_color', '_adm_stg_cpt_option_sys_pwa_manifest_background_color', '', 'digit', '', '', '', 4),
+(@iCategoryId, 'sys_pwa_manifest_theme_color', '_adm_stg_cpt_option_sys_pwa_manifest_theme_color', '', 'digit', '', '', '', 5);
 
+--
+-- CATEGORY (HIDDEN): PWA Service Worker
+--
+INSERT INTO `sys_options_categories`(`type_id`, `name`, `caption`, `hidden`, `order`) VALUES (@iTypeId, 'pwa_sw', '_adm_stg_cpt_category_pwa_sw', 1, 2);
+SET @iCategoryId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options`(`category_id`, `name`, `caption`, `value`, `type`, `extra`, `check`, `check_error`, `order`) VALUES
+(@iCategoryId, 'sys_pwa_sw_cache', '_adm_stg_cpt_option_sys_pwa_sw_cache', '', 'text', '', '', '', 1), 
+(@iCategoryId, 'sys_pwa_sw_offline', '_adm_stg_cpt_option_sys_pwa_sw_offline', '', 'digit', '', '', '', 2);
 
 --
 -- Table structure for table `sys_options_mixes`

+ 5 - 3
manifest.json.php

@@ -18,13 +18,15 @@ $aContent = [
     'orientation' => 'portrait',
     'start_url' => parse_url(BX_DOL_URL_ROOT, PHP_URL_PATH),
     'display' => 'standalone',
-    'scope' => '/',
-    'background_color' => getParam('sys_pwa_background_color'),
-    'theme_color' => getParam('sys_pwa_theme_color')
+    'scope' => '/'
 ];
 
 $aContent['name'] = ($sName = getParam('sys_pwa_name')) != '' ? $sName : parse_url(BX_DOL_URL_ROOT, PHP_URL_HOST);
 $aContent['short_name'] = ($sShortName = getParam('sys_pwa_short_name')) != '' ? $sShortName : $aContent['name'];
+if(($sBackgroundColor = getParam('sys_pwa_background_color')) != '')
+    $aContent['background_color'] = $sBackgroundColor;
+if(($sThemeColor = getParam('sys_pwa_theme_color')) != '')
+    $aContent['theme_color'] = $sThemeColor;
 
 if(isLogged())
     $aContent['gcm_sender_id'] = '482941778795';

+ 1 - 1
modules/boonex/artificer/classes/BxArtificerTemplate.php

@@ -47,7 +47,7 @@ class BxArtificerTemplate extends BxBaseModGeneralTemplate
             case 'footer':
                 $sResult .= $this->addJs([
                     'sidebar.js'
-                ], true);
+                ]);
                 break;
         }
 

+ 10 - 6
modules/boonex/english/data/langs/system/en.xml

@@ -1736,12 +1736,16 @@
     <string name="_adm_stg_cpt_option_sys_api_extended_units"><![CDATA[Use extended units]]></string>
     <string name="_adm_stg_cpt_option_sys_viewport_meta_tag"><![CDATA[Viewport meta tag]]></string>
 
-    <string name="_adm_stg_cpt_category_pwa"><![CDATA[Progressive Web Apps]]></string>
-    <string name="_adm_stg_cpt_option_sys_pwa_name"><![CDATA[Name]]></string>
-    <string name="_adm_stg_cpt_option_sys_pwa_short_name"><![CDATA[Short name]]></string>
-    <string name="_adm_stg_cpt_option_sys_pwa_description"><![CDATA[Description]]></string>
-    <string name="_adm_stg_cpt_option_sys_pwa_background_color"><![CDATA[Background color]]></string>
-    <string name="_adm_stg_cpt_option_sys_pwa_theme_color"><![CDATA[Theme color]]></string>
+    <string name="_adm_stg_cpt_category_pwa_manifest"><![CDATA[Manifest]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_manifest_name"><![CDATA[Name]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_manifest_short_name"><![CDATA[Short name]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_manifest_description"><![CDATA[Description]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_manifest_background_color"><![CDATA[Background color]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_manifest_theme_color"><![CDATA[Theme color]]></string>
+
+    <string name="_adm_stg_cpt_category_pwa_sw"><![CDATA[Service Worker]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_sw_cache"><![CDATA[URLs to cache on install (each in new line)]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_sw_offline"><![CDATA[Offline page]]></string>
 
     <string name="_adm_stg_cpt_category_sockets"><![CDATA[Sockets server]]></string>
     <string name="_adm_stg_cpt_option_sys_sockets_type"><![CDATA[Type]]></string>

+ 10 - 6
modules/boonex/russian/data/langs/system/ru.xml

@@ -1714,12 +1714,16 @@
     <string name="_adm_stg_cpt_option_sys_api_extended_units"><![CDATA[Использовать расширенные юниты]]></string>
     <string name="_adm_stg_cpt_option_sys_viewport_meta_tag"><![CDATA[Метатег области просмотра]]></string>
 
-    <string name="_adm_stg_cpt_category_pwa"><![CDATA[Прогрессивные веб-приложения]]></string>
-    <string name="_adm_stg_cpt_option_sys_pwa_name"><![CDATA[Имя]]></string>
-    <string name="_adm_stg_cpt_option_sys_pwa_short_name"><![CDATA[Сокращенное имя]]></string>
-    <string name="_adm_stg_cpt_option_sys_pwa_description"><![CDATA[Описание]]></string>
-    <string name="_adm_stg_cpt_option_sys_pwa_background_color"><![CDATA[Цвет фона]]></string>
-    <string name="_adm_stg_cpt_option_sys_pwa_theme_color"><![CDATA[Цвет темы]]></string>
+    <string name="_adm_stg_cpt_category_pwa_manifest"><![CDATA[Manifest]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_manifest_name"><![CDATA[Имя]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_manifest_short_name"><![CDATA[Сокращенное имя]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_manifest_description"><![CDATA[Описание]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_manifest_background_color"><![CDATA[Цвет фона]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_manifest_theme_color"><![CDATA[Цвет темы]]></string>
+
+    <string name="_adm_stg_cpt_category_pwa_sw"><![CDATA[Service Worker]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_sw_cache"><![CDATA[URL-адреса для кэширования при установке (каждый в новой строке)]]></string>
+    <string name="_adm_stg_cpt_option_sys_pwa_sw_offline"><![CDATA[Офлайн-страница]]></string>
 
     <string name="_adm_stg_cpt_category_sockets"><![CDATA[Сервер для сокетов]]></string>
     <string name="_adm_stg_cpt_option_sys_sockets_type"><![CDATA[Тип]]></string>

+ 1 - 1
studio/template/scripts/BxBaseStudioDesigner.php

@@ -651,7 +651,7 @@ class BxBaseStudioDesigner extends BxDolStudioDesigner
 
     protected function getPwa()
     {
-        $oOptions = new BxTemplStudioOptions(BX_DOL_STUDIO_STG_TYPE_SYSTEM, 'pwa');
+        $oOptions = new BxTemplStudioOptions(BX_DOL_STUDIO_STG_TYPE_SYSTEM, ['pwa_manifest', 'pwa_sw']);
 
         $this->aPageCss = array_merge($this->aPageCss, $oOptions->getCss());
         $this->aPageJs = array_merge($this->aPageJs, $oOptions->getJs());

+ 86 - 58
sw.js.php

@@ -12,7 +12,11 @@ define('BX_SERVICE_WORKER', true);
 require_once('./inc/header.inc.php');
 $oTemplate = BxDolTemplate::getInstance();
 
+$sCacheName = 'app' . $oTemplate->getRevision();
+
 $aAssets = [];
+
+//--- CSS/JS files
 foreach(['css' , 'js'] as $sType) {
     $aFiles = $oTemplate->includeFiles($sType, true, false);
     if(!is_array($aFiles))
@@ -21,8 +25,37 @@ foreach(['css' , 'js'] as $sType) {
     $aAssets = array_merge($aAssets, $aFiles);
 }
 
-foreach($aAssets as $iKey => $sAsset)
-    $aAssets[$iKey] = str_replace(BX_DOL_URL_ROOT, '/', $sAsset);
+if(($sCache = getParam('sys_pwa_sw_cache')) != '') {
+    $aItems = preg_split("/\\r\\n|\\r|\\n/", $sCache);
+    if(!empty($aItems) && is_array($aItems))
+        foreach($aItems as $sItem) {
+            if(strpos($sItem, BX_DOL_URL_ROOT) === false)
+                $sItem = BX_DOL_URL_ROOT . $sItem;
+
+            $aAssets[] = $oTemplate->addRevision($sItem);
+        }
+}
+
+//--- Offline page
+if(($sOffline = getParam('sys_pwa_sw_offline')) != '')
+    $aAssets[] = BX_DOL_URL_ROOT . $sOffline;
+
+//--- Favicon or SVG icon
+$sIconUrl = '';
+if(($iId = (int)getParam('sys_site_icon')) != 0)
+    $sIconUrl = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_FILES)->getFileUrlById($iId);          
+if(!$sIconUrl && ($iId = (int)getParam('sys_site_icon_svg')) != 0)
+    $sIconUrl = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES)->getFileUrlById($iId);
+if(!empty($sIconUrl))
+    $aAssets[] = $sIconUrl;
+
+//--- Fonts
+if(getParam('sys_css_icons_default') != '') {
+    $aAssets[] = BX_DOL_URL_BASE . 'fonts/fa-solid-900.woff2';
+    $aAssets[] = BX_DOL_URL_BASE . 'fonts/fa-regular-400.woff2';
+}
+
+//$aAssets[] = 'https://ci.una.io/test/cache_public/bx_templ_css_61777a77ec925fa4b36db9699d01c960.css'; // some basic styles are included here instead of main css file, these styles need to be moved there then tis cache isn't needed
 
 header("Content-Type: text/javascript");
 ?>
@@ -32,13 +65,32 @@ let coreAssets = <?php echo json_encode($aAssets); ?>;
 
 // On install, cache core assets
 self.addEventListener('install', function (event) {
+    console.log("Service Worker: <?php echo $sCacheName; ?>");
     // Cache core assets
-    event.waitUntil(caches.open('app').then(function (cache) {
-        for (let asset of coreAssets) {
-            cache.add(new Request(asset));
-        }
-        return cache;
-    }));
+    event.waitUntil((async () => { 
+        caches.open('<?php echo $sCacheName; ?>').then(function (cache) {
+            for (let asset of coreAssets) {
+                cache.add(new Request(asset));
+            }
+            return cache;
+        });
+    })());
+});
+
+const deleteCache = async (key) => {
+  await caches.delete(key);
+};
+
+const deleteOldCaches = async () => {
+  const cacheKeepList = ["<?php echo $sCacheName; ?>"];
+  const keyList = await caches.keys();
+  const cachesToDelete = keyList.filter((key) => !cacheKeepList.includes(key));
+  await Promise.all(cachesToDelete.map(deleteCache));
+};
+
+// delete old cache on activate
+self.addEventListener("activate", (event) => {
+  event.waitUntil(deleteOldCaches());
 });
 
 // Listen for request events
@@ -51,58 +103,34 @@ self.addEventListener('fetch', function (event) {
     // https://stackoverflow.com/a/49719964
     if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') return;
 
-    // HTML files
-    // Network-first
-    if (request.headers.get('Accept').includes('text/html')) {
-        event.respondWith(
-            fetch(request).then(function (response) {
-                // Create a copy of the response and save it to the cache
-                let copy = response.clone();
-                event.waitUntil(caches.open('app').then(function (cache) {
-                    return cache.put(request, copy);
-                }));
-
-                // Return the response
-                return response;
-            }).catch(function (error) {
-                // If there's no item in cache, respond with a fallback
-                return caches.match(request).then(function (response) {
-                    return response || caches.match('/offline.html');
-                });
-            })
-        );
-    }
-
-    // CSS & JavaScript
-    // Offline-first
-    if (request.headers.get('Accept').includes('text/css') || request.headers.get('Accept').includes('text/javascript')) {
+    // regular pages and static assets
+    if (request.mode === 'navigate' || request.url.match(/\.(css|js|woff2|svg|png|jpg)$/)) {
+        const failedResponse = async (err) => {
+            let reponse = null;
+            if (request.mode === 'navigate') {
+                const cache = await caches.open('<?php echo $sCacheName; ?>');
+                reponse = await cache.match('<?php echo BX_DOL_URL_ROOT . 'offline'; ?>');
+            }
+            return reponse || new Response("Network error happened", {
+                status: 408,
+                headers: { "Content-Type": "text/plain" },
+            });
+        }
         event.respondWith(
-            caches.match(request).then(function (response) {
-                return response || fetch(request).then(function (response) {
-                    // Return the response
-                    return response;
-                });
+            caches.match(request).then((response) => {
+                try {
+                    return response || fetch(request)
+                        .then((response) => {
+                            return response;
+                        })
+                        .catch((err) => {
+                            return failedResponse(err);
+                        });
+                } catch (error) {
+                    return failedResponse(err);
+                }
             })
         );
-        return;
     }
 
-    // Images
-    // Offline-first
-    if (request.headers.get('Accept').includes('image')) {
-        event.respondWith(
-            caches.match(request).then(function (response) {
-                return response || fetch(request).then(function (response) {
-                    // Save a copy of it in cache
-                    let copy = response.clone();
-                    event.waitUntil(caches.open('app').then(function (cache) {
-                        return cache.put(request, copy);
-                    }));
-
-                    // Return the response
-                    return response;
-                });
-            })
-        );
-    }
-});
+});