1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace LeKoala\SsPwa; |
4
|
|
|
|
5
|
|
|
use SilverStripe\Control\Director; |
6
|
|
|
use SilverStripe\Control\Controller; |
7
|
|
|
use SilverStripe\SiteConfig\SiteConfig; |
|
|
|
|
8
|
|
|
use SilverStripe\View\TemplateGlobalProvider; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* @link https://developer.mozilla.org/en-US/docs/Web/Manifest |
12
|
|
|
* @link https://web.dev/learn/pwa/web-app-manifest/ |
13
|
|
|
*/ |
14
|
|
|
class ManifestController extends Controller implements TemplateGlobalProvider, ServiceWorkerCacheProvider |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* @config |
18
|
|
|
* @var string |
19
|
|
|
*/ |
20
|
|
|
private static $gcm_sender_id = null; |
|
|
|
|
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @link https://developer.mozilla.org/en-US/docs/Web/Manifest/background_color |
24
|
|
|
* @config |
25
|
|
|
* @var string |
26
|
|
|
*/ |
27
|
|
|
private static $background_color = '#ffffff'; |
|
|
|
|
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @link https://developer.mozilla.org/en-US/docs/Web/Manifest/orientation |
31
|
|
|
* @config |
32
|
|
|
* @var string |
33
|
|
|
*/ |
34
|
|
|
private static $orientation = 'portrait-primary'; |
|
|
|
|
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @config |
38
|
|
|
* @var string |
39
|
|
|
*/ |
40
|
|
|
private static $custom_icon_path = ''; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var array<string> |
44
|
|
|
*/ |
45
|
|
|
private static $allowed_actions = [ |
|
|
|
|
46
|
|
|
'index', |
47
|
|
|
]; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @return mixed |
51
|
|
|
*/ |
52
|
|
|
public function index() |
53
|
|
|
{ |
54
|
|
|
$config = $this->config(); |
55
|
|
|
$sc = SiteConfig::current_site_config(); |
56
|
|
|
|
57
|
|
|
$title = $sc->Title; |
58
|
|
|
//@phpstan-ignore-next-line |
59
|
|
|
$desc = $sc->Tagline; |
60
|
|
|
$desc = $desc ?: $title; |
61
|
|
|
|
62
|
|
|
$icons = self::listIcons(); |
63
|
|
|
|
64
|
|
|
$theme_color = $background_color = $config->get('background_color'); |
65
|
|
|
//@phpstan-ignore-next-line |
66
|
|
|
if ($sc->ThemeColor) { |
67
|
|
|
$theme_color = $sc->ThemeColor; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
$manifestContent = [ |
71
|
|
|
// Full name of your PWA. It will appear along with the icon in the operating system's home screen, launcher, dock, or menu |
72
|
|
|
'name' => $title, |
73
|
|
|
// Optional, a shorter name of your PWA, used when there is not enough room to display the full value of the name field. Keep it under 12 characters to minimize the possibility of truncation. |
74
|
|
|
'short_name' => $title, |
75
|
|
|
// Array of icon objects with src, type, sizes, and optional purpose fields, describing what images should represent the PWA. |
76
|
|
|
'icons' => $icons, |
77
|
|
|
// The URL the PWA should load when the user starts it from the installed icon. |
78
|
|
|
// An absolute path is recommended, so if your PWA's home page is the root of your site, |
79
|
|
|
// you could set this to ‘/' to open it when your app starts. |
80
|
|
|
// If you don't provide a start URL, the browser can use the URL the PWA was installed from as a start. |
81
|
|
|
// It can be a deep link, such as the details of a product instead of your home screen. |
82
|
|
|
'start_url' => Director::baseURL(), |
83
|
|
|
// One of fullscreen, standalone, minimal-ui, or browser, describing how the OS should draw the PWA window. |
84
|
|
|
// You can read more about the different display modes in the App Design chapter. |
85
|
|
|
// Most use cases implement standalone. |
86
|
|
|
'display' => 'standalone', |
87
|
|
|
// A string that uniquely identifies this PWA against others that may be hosted on the same origin. If it's not set, the start_url will be used as a fallback value. |
88
|
|
|
// Keep in mind that by changing the start_url in the future (such as when changing a query string value) you may be removing the browser's ability to detect that a PWA is already installed. |
89
|
|
|
// @link https://developer.chrome.com/blog/pwa-manifest-id/ |
90
|
|
|
'id' => Director::baseURL(), |
91
|
|
|
// The description member is a string in which developers can explain what the application does. |
92
|
|
|
// description is directionality-capable, which means it can be displayed left to right or right to left based on the values of the dir and lang manifest members. |
93
|
|
|
'description' => $desc, |
94
|
|
|
'lang' => 'en', |
95
|
|
|
'dir' => 'ltr', |
96
|
|
|
// The scope member is a string that defines the navigation scope of this web application's application context. |
97
|
|
|
// It restricts what web pages can be viewed while the manifest is applied. |
98
|
|
|
// If the user navigates outside the scope, it reverts to a normal web page inside a browser tab or window. |
99
|
|
|
'scope' => Director::baseURL(), |
100
|
|
|
// https://developer.chrome.com/docs/extensions/mv3/declare_permissions/ |
101
|
|
|
'permissions' => [ |
102
|
|
|
// https://developer.chrome.com/docs/extensions/reference/gcm/ |
103
|
|
|
'gcm', |
104
|
|
|
], |
105
|
|
|
// The background_color member defines a placeholder background color for the application page to display before its stylesheet is loaded. |
106
|
|
|
// This value is used by the user agent to draw the background color of a shortcut when the manifest is available before the stylesheet has loaded. |
107
|
|
|
'background_color' => $background_color, |
108
|
|
|
// The theme_color member is a string that defines the default theme color for the application. |
109
|
|
|
// This sometimes affects how the OS displays the site (e.g., on Android's task switcher, the theme color surrounds the site). |
110
|
|
|
'theme_color' => $theme_color, |
111
|
|
|
// The orientation member defines the default orientation for all the website's top-level browsing contexts. |
112
|
|
|
'orientation' => $config->get('orientation'), |
113
|
|
|
]; |
114
|
|
|
|
115
|
|
|
$gcm_sender_id = $config->get('gcm_sender_id'); |
116
|
|
|
if ($gcm_sender_id) { |
117
|
|
|
$manifestContent['gcm_sender_id'] = $gcm_sender_id; |
118
|
|
|
$manifestContent['gcm_user_visible_only'] = true; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
$this->getResponse()->addHeader('Content-Type', 'application/manifest+json; charset="utf-8"'); |
122
|
|
|
return json_encode($manifestContent); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @return array<array{'src':string,'sizes':string,'type':string,'purpose':string}> |
127
|
|
|
*/ |
128
|
|
|
public static function listIcons(): array |
129
|
|
|
{ |
130
|
|
|
$iconsPath = self::getIconsPath(); |
131
|
|
|
$sizes = [192, 512]; |
132
|
|
|
$purposes = ['any', 'maskable']; |
133
|
|
|
$version = ServiceWorkerController::config()->get('version'); |
134
|
|
|
|
135
|
|
|
$icons = []; |
136
|
|
|
foreach ($sizes as $size) { |
137
|
|
|
foreach ($purposes as $purpose) { |
138
|
|
|
$icons[] = [ |
139
|
|
|
'src' => self::join_links($iconsPath, 'manifest-icon-' . $size . '.maskable.png?' . $version), |
140
|
|
|
'sizes' => $size . 'x' . $size, |
141
|
|
|
'type' => 'image/png', |
142
|
|
|
'purpose' => $purpose, |
143
|
|
|
]; |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
return $icons; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
public static function getIconsPath(): string |
150
|
|
|
{ |
151
|
|
|
// can be dynamically configured through site config |
152
|
|
|
$sc = SiteConfig::current_site_config(); |
153
|
|
|
if ($sc->hasMethod("PwaIconsPath")) { |
154
|
|
|
//@phpstan-ignore-next-line |
155
|
|
|
return $sc->PwaIconsPath(); |
156
|
|
|
} |
157
|
|
|
// can use custom override |
158
|
|
|
if (self::config()->custom_icon_path) { |
159
|
|
|
return self::config()->custom_icon_path; |
160
|
|
|
} |
161
|
|
|
// defaults to _resources/app/images/icons |
162
|
|
|
$baseURL = '/'; |
163
|
|
|
$iconsPath = self::join_links($baseURL, RESOURCES_DIR, 'app', 'images', 'icons'); |
164
|
|
|
return $iconsPath; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* @return array<string,string> |
169
|
|
|
*/ |
170
|
|
|
public static function get_template_global_variables() |
171
|
|
|
{ |
172
|
|
|
return [ |
173
|
|
|
'PwaIconsPath' => 'getIconsPath', |
174
|
|
|
]; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* @return array<string> |
179
|
|
|
*/ |
180
|
|
|
public static function getServiceWorkerCachedPaths(): array |
181
|
|
|
{ |
182
|
|
|
$icons = self::listIcons(); |
183
|
|
|
return array_map(function ($v) { |
184
|
|
|
return $v['src']; |
185
|
|
|
}, $icons); |
186
|
|
|
} |
187
|
|
|
} |
188
|
|
|
|
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths