1717use phpbb \symfony_request ;
1818use phpbb \template \template ;
1919use phpbb \user ;
20+ use phpbb \webpushnotifications \ext ;
2021
2122class wpn_acp_module
2223{
2324 public $ page_title ;
2425 public $ tpl_name ;
2526 public $ u_action ;
2627
28+ /** @var \phpbb\cache\service */
29+ protected $ cache ;
30+
2731 /** @var config $config */
2832 protected $ config ;
2933
34+ /** @var \phpbb\db\driver\driver_interface */
35+ protected $ db ;
36+
3037 /** @var language $lang */
3138 protected $ lang ;
3239
3340 /** @var log $log */
3441 protected $ log ;
3542
43+ /** @var \FastImageSize\FastImageSize $imagesize */
44+ protected $ imagesize ;
45+
3646 /** @var request $request */
3747 protected $ request ;
3848
@@ -45,6 +55,9 @@ class wpn_acp_module
4555 /** @var user $user */
4656 protected $ user ;
4757
58+ /** @var string */
59+ protected $ root_path ;
60+
4861 /** @var array $errors */
4962 protected $ errors = [];
5063
@@ -62,13 +75,17 @@ public function main($id, $mode)
6275 {
6376 global $ phpbb_container ;
6477
78+ $ this ->cache = $ phpbb_container ->get ('cache ' );
6579 $ this ->config = $ phpbb_container ->get ('config ' );
80+ $ this ->db = $ phpbb_container ->get ('dbal.conn ' );
81+ $ this ->imagesize = $ phpbb_container ->get ('upload_imagesize ' );
6682 $ this ->lang = $ phpbb_container ->get ('language ' );
6783 $ this ->log = $ phpbb_container ->get ('log ' );
6884 $ this ->request = $ phpbb_container ->get ('request ' );
6985 $ this ->symfony_request = $ phpbb_container ->get ('symfony_request ' );
7086 $ this ->template = $ phpbb_container ->get ('template ' );
7187 $ this ->user = $ phpbb_container ->get ('user ' );
88+ $ this ->root_path = $ phpbb_container ->getParameter ('core.root_path ' );
7289
7390 $ form_key = 'phpbb/webpushnotifications ' ;
7491 add_form_key ($ form_key );
@@ -95,6 +112,26 @@ public function main($id, $mode)
95112
96113 $ this ->display_settings ();
97114 }
115+ else if ($ mode === 'pwa ' )
116+ {
117+ $ this ->tpl_name = 'wpn_acp_pwa ' ;
118+
119+ $ this ->lang ->add_lang ('webpushnotifications_module_acp ' , 'phpbb/webpushnotifications ' );
120+
121+ $ this ->page_title = $ this ->lang ->lang ('ACP_WEBPUSH_PWA_SETTINGS ' );
122+
123+ if ($ this ->request ->is_set_post ('submit ' ))
124+ {
125+ if (!check_form_key ($ form_key ))
126+ {
127+ trigger_error ($ this ->lang ->lang ('FORM_INVALID ' ), E_USER_WARNING );
128+ }
129+
130+ $ this ->save_pwa_settings ();
131+ }
132+
133+ $ this ->display_pwa_settings ();
134+ }
98135 }
99136
100137 /**
@@ -164,6 +201,218 @@ public function save_settings()
164201 trigger_error ($ this ->lang ->lang ('CONFIG_UPDATED ' ) . adm_back_link ($ this ->u_action ), E_USER_NOTICE );
165202 }
166203
204+ /**
205+ * Add PWA settings template vars to the form
206+ */
207+ public function display_pwa_settings ()
208+ {
209+ $ this ->template ->assign_vars ([
210+ 'S_PWA_SHOW_INSTALL_BANNER ' => (bool ) $ this ->config ['pwa_show_install_banner ' ],
211+ 'PWA_SHORT_NAME ' => $ this ->config ['pwa_short_name ' ],
212+ 'PWA_ICON_SMALL ' => $ this ->config ['pwa_icon_small ' ],
213+ 'PWA_ICON_LARGE ' => $ this ->config ['pwa_icon_large ' ],
214+ 'STYLES ' => $ this ->get_styles (),
215+ 'U_ACTION ' => $ this ->u_action ,
216+ ]);
217+
218+ $ this ->display_errors ();
219+ }
220+
221+ /**
222+ * Save PWA settings data to the database
223+ *
224+ * @return void
225+ */
226+ public function save_pwa_settings ()
227+ {
228+ $ config_array = $ this ->request ->variable ('config ' , ['' => '' ], true );
229+
230+ $ config_array ['pwa_short_name ' ] = $ config_array ['pwa_short_name ' ] ?? '' ;
231+ $ config_array ['pwa_icon_small ' ] = $ config_array ['pwa_icon_small ' ] ?? '' ;
232+ $ config_array ['pwa_icon_large ' ] = $ config_array ['pwa_icon_large ' ] ?? '' ;
233+
234+ $ this ->validate_pwa_short_name ($ config_array ['pwa_short_name ' ]);
235+ $ this ->validate_pwa_icons ($ config_array ['pwa_icon_small ' ], $ config_array ['pwa_icon_large ' ]);
236+
237+ $ styles = $ this ->get_styles ();
238+ $ updates = [];
239+ foreach ($ styles as $ row )
240+ {
241+ $ style_id = $ row ['style_id ' ];
242+ $ pwa_bg_color = $ this ->request ->variable ('pwa_bg_color_ ' . $ style_id , '' );
243+ $ pwa_theme_color = $ this ->request ->variable ('pwa_theme_color_ ' . $ style_id , '' );
244+
245+ $ updates [$ style_id ] = [
246+ 'pwa_bg_color ' => $ this ->validate_hex_color ($ pwa_bg_color ) ? $ pwa_bg_color : $ row ['pwa_bg_color ' ],
247+ 'pwa_theme_color ' => $ this ->validate_hex_color ($ pwa_theme_color ) ? $ pwa_theme_color : $ row ['pwa_theme_color ' ],
248+ ];
249+ }
250+
251+ if ($ this ->display_errors ())
252+ {
253+ return ;
254+ }
255+
256+ $ this ->log ->add ('admin ' , $ this ->user ->data ['user_id ' ], $ this ->user ->ip , 'LOG_CONFIG_WEBPUSH ' );
257+
258+ // Ensure 4-byte emoji can be stored correctly
259+ $ config_array ['pwa_short_name ' ] = utf8_encode_ucr ($ config_array ['pwa_short_name ' ]);
260+
261+ foreach ([
262+ 'pwa_short_name ' ,
263+ 'pwa_icon_small ' ,
264+ 'pwa_icon_large ' ,
265+ 'pwa_show_install_banner ' ,
266+ ] as $ config_name )
267+ {
268+ $ this ->config ->set ($ config_name , $ config_array [$ config_name ] ?? 0 );
269+ }
270+
271+ $ this ->set_styles ($ updates );
272+
273+ trigger_error ($ this ->lang ->lang ('CONFIG_UPDATED ' ) . adm_back_link ($ this ->u_action ), E_USER_NOTICE );
274+ }
275+
276+ /**
277+ * Validate PWA short site name
278+ */
279+ protected function validate_pwa_short_name (string $ short_name ): void
280+ {
281+ if ($ short_name === '' )
282+ {
283+ return ;
284+ }
285+
286+ $ short_name = ext::decode_entities ($ short_name , ENT_QUOTES );
287+ if (utf8_strlen ($ short_name ) > 12 )
288+ {
289+ $ this ->errors [] = $ this ->lang ->lang ('PWA_SHORT_NAME_INVALID ' );
290+ }
291+ }
292+
293+ /**
294+ * Validate PWA icon filenames and dimensions
295+ */
296+ protected function validate_pwa_icons (string $ small_icon , string $ large_icon ): void
297+ {
298+ if ($ small_icon === '' && $ large_icon === '' )
299+ {
300+ return ;
301+ }
302+
303+ if ($ small_icon === '' )
304+ {
305+ $ this ->errors [] = $ this ->lang ->lang ('PWA_ICON_NOT_PROVIDED ' , $ this ->lang ->lang ('PWA_ICON_SMALL ' ));
306+ return ;
307+ }
308+
309+ if ($ large_icon === '' )
310+ {
311+ $ this ->errors [] = $ this ->lang ->lang ('PWA_ICON_NOT_PROVIDED ' , $ this ->lang ->lang ('PWA_ICON_LARGE ' ));
312+ return ;
313+ }
314+
315+ $ this ->validate_pwa_icon ($ small_icon , 192 );
316+ $ this ->validate_pwa_icon ($ large_icon , 512 );
317+ }
318+
319+ /**
320+ * Validate one PWA icon file
321+ */
322+ protected function validate_pwa_icon (string $ filename , int $ size ): void
323+ {
324+ if (basename ($ filename ) !== $ filename )
325+ {
326+ $ this ->errors [] = $ this ->lang ->lang ('PWA_ICON_INVALID ' , $ filename );
327+ return ;
328+ }
329+
330+ $ image = $ this ->root_path . ext::PWA_ICON_DIR . '/ ' . $ filename ;
331+ $ image_info = $ this ->imagesize ->getImageSize ($ image );
332+ if ($ image_info === false )
333+ {
334+ $ this ->errors [] = $ this ->lang ->lang ('PWA_ICON_INVALID ' , $ filename );
335+ return ;
336+ }
337+
338+ if ($ image_info ['width ' ] !== $ size || $ image_info ['height ' ] !== $ size )
339+ {
340+ $ this ->errors [] = $ this ->lang ->lang ('PWA_ICON_SIZE_INVALID ' , $ filename );
341+ }
342+
343+ if ($ image_info ['type ' ] !== IMAGETYPE_PNG )
344+ {
345+ $ this ->errors [] = $ this ->lang ->lang ('PWA_ICON_MIME_INVALID ' , $ filename );
346+ }
347+ }
348+
349+ /**
350+ * Validate HTML color hex codes
351+ */
352+ protected function validate_hex_color (string $ code ): bool
353+ {
354+ $ code = trim ($ code );
355+
356+ if ($ code === '' )
357+ {
358+ return true ;
359+ }
360+
361+ $ test = (bool ) preg_match ('/^#([0-9A-F]{3}){1,2}$/i ' , $ code );
362+
363+ if ($ test === false )
364+ {
365+ $ this ->errors [] = $ this ->lang ->lang ('PWA_INVALID_COLOUR ' , $ code );
366+ }
367+
368+ return $ test ;
369+ }
370+
371+ /**
372+ * Get style data from the styles table
373+ *
374+ * @return array Style data
375+ */
376+ protected function get_styles (): array
377+ {
378+ $ sql = 'SELECT style_id, style_name, pwa_bg_color, pwa_theme_color
379+ FROM ' . STYLES_TABLE . '
380+ WHERE style_active = 1
381+ ORDER BY style_name ' ;
382+ $ result = $ this ->db ->sql_query ($ sql , 3600 );
383+
384+ $ rows = $ this ->db ->sql_fetchrowset ($ result );
385+ $ this ->db ->sql_freeresult ($ result );
386+
387+ return $ rows ;
388+ }
389+
390+ /**
391+ * Set style data in the styles table
392+ *
393+ * @param array $rows Array of style table data to update; style_id is key
394+ * @return void
395+ */
396+ protected function set_styles (array $ rows ): void
397+ {
398+ if (!empty ($ rows ))
399+ {
400+ $ this ->db ->sql_transaction ('begin ' );
401+
402+ foreach ($ rows as $ style_id => $ row )
403+ {
404+ $ sql = 'UPDATE ' . STYLES_TABLE . '
405+ SET ' . $ this ->db ->sql_build_array ('UPDATE ' , $ row ) . '
406+ WHERE style_id = ' . (int ) $ style_id ;
407+ $ this ->db ->sql_query ($ sql );
408+ }
409+
410+ $ this ->db ->sql_transaction ('commit ' );
411+
412+ $ this ->cache ->destroy ('sql ' , STYLES_TABLE );
413+ }
414+ }
415+
167416 /**
168417 * Display any errors
169418 *
0 commit comments