You are here

pwa.admin.inc in Progressive Web App 7.2

Same filename and directory in other branches
  1. 7 pwa.admin.inc

PWA administration forms.

File

pwa.admin.inc
View source
<?php

/**
 * @file
 * PWA administration forms.
 */

/**
 * Main configuration page for PWA.
 */
function pwa_admin_configuration() {

  // Reuse some fields from the manifest config form.
  $manifest_form = pwa_admin_configuration_manifest();
  $form['pwa_short_name'] = [
    '#description' => t('Name of the shortcut created on the device. Should be like an app name (one short word or an acronym). You can configure the rest of the <a href="@manifest_url">manifest file values</a>.', [
      '@manifest_url' => url('/admin/config/pwa/settings/manifest'),
    ]),
  ] + $manifest_form['pwa_short_name'];
  $form['pwa_description'] = [
    '#description' => t('A short description of the Progressive Web App. Required for inclusion in the <a href="@windows_pwa_url">Microsoft Store</a>.', [
      '@windows_pwa_url' => 'https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-edgehtml/microsoft-store#criteria-for-automatic-submission',
    ]),
  ] + $manifest_form['pwa_description'];
  $form['pwa_sw_cache_version'] = [
    '#type' => 'textfield',
    '#title' => t('Version'),
    '#description' => t('Changing this number will invalidate caches for the manifest and serviceworker cache. Use it when assets have significantly changed or if you want to force a cache refresh for all clients.'),
    '#size' => 5,
    '#default_value' => variable_get('pwa_sw_cache_version', 1),
  ];
  $form['pwa_sw_debug'] = [
    '#type' => 'checkbox',
    '#title' => t('Show debug messages'),
    '#description' => t('Show all log messages from the serviceworker libraries.'),
    '#default_value' => variable_get('pwa_sw_debug', FALSE),
  ];

  // Apple specific configuration.
  $form['pwa_apple_meta_enable'] = [
    '#type' => 'checkbox',
    '#title' => t('Improve Apple devices integration'),
    '#description' => t('Let the PWA module add several non-standard metatags that help improve the PWA experience on Apple devices.'),
    '#default_value' => variable_get('pwa_apple_meta_enable', TRUE),
  ];
  $form['apple'] = [
    '#type' => 'fieldset',
    '#title' => t('Apple devices integration settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#states' => [
      'invisible' => [
        '[name="pwa_apple_meta_enable"]' => [
          'checked' => FALSE,
        ],
      ],
    ],
  ];
  $form['apple']['pwa_apple_status-bar-style'] = [
    "#type" => 'select',
    "#title" => t('Select color for status-bar-style'),
    '#options' => [
      'default' => t('Default'),
      'black' => t('Black'),
      'black_translucent' => t('Black translucent'),
    ],
    '#default_value' => variable_get('pwa_apple_status-bar-style', 'black_translucent'),
  ];
  $form['apple']['pwa_apple_startup-image'] = [
    '#type' => 'checkbox',
    '#title' => t('Enable startup splash screen'),
    '#description' => t('The splash screen is generated automatically from the 512x512 image from the manifest. This will add 2 meta tags for each device selected.'),
    '#default_value' => variable_get('pwa_apple_startup-image', TRUE),
  ];
  $form['apple']['pwa_apple_startup-image_devices'] = [
    '#type' => 'checkboxes',
    '#title' => t('Splash screen for the following devices'),
    '#description' => t('Two link tags in the html head will be generated for each device selected.'),
    '#options' => array_column(_pwa_apple_devices(), 'device', 'device'),
    '#states' => [
      'visible' => [
        '[name="pwa_apple_startup-image"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
    '#default_value' => variable_get('pwa_apple_startup-image_devices', []),
  ];
  $form = system_settings_form($form);

  // Wait for all the values to be saved before refreshing cache.
  $form['#submit'][] = 'pwa_admin_clear_cache';
  $form['#submit'][] = 'pwa_admin_generate_drusplash';
  return $form;
}
function pwa_admin_generate_drusplash($form, &$form_state) {

  // The option is disabled, remove all the associated images.
  if (!$form_state['values']['pwa_apple_startup-image']) {
    file_unmanaged_delete_recursive('public://pwa/apple');
  }
  else {

    // Trigger the generation of the different splash screens.
    _pwa_apple_drusplash_list();
  }
}

/**
 * Configure PWA settings for manifest.json
 */
function pwa_admin_configuration_manifest() {
  $form = [];
  $form['pwa_short_name'] = [
    '#type' => 'textfield',
    '#title' => t('Short name'),
    '#description' => t('Name of the shortcut created on the device. Should be like an app name (one short word or an acronym).'),
    '#size' => 10,
    '#default_value' => variable_get('pwa_short_name', variable_get('site_name')),
    '#required' => TRUE,
  ];
  $form['pwa_name'] = [
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#description' => t('Usually appears as the name on the splash screen during launch.'),
    '#size' => 30,
    '#default_value' => variable_get('pwa_name', variable_get('site_name')),
    '#required' => TRUE,
  ];
  $form['pwa_description'] = [
    '#type' => 'textfield',
    '#title' => t('Description'),
    '#description' => t('A short description of the Progressive Web App. Answer the question "Why do I need this app?"'),
    '#default_value' => variable_get('pwa_description', ''),
  ];
  $form['pwa_background_color'] = [
    '#type' => 'textfield',
    '#title' => t('Background color'),
    '#description' => t('Color of the browser UI when launching from home screen.'),
    '#size' => 8,
    '#default_value' => variable_get('pwa_background_color', '#F6F6F2'),
  ];
  $form['pwa_theme_color'] = [
    '#type' => 'textfield',
    '#title' => t('Theme color'),
    '#description' => t('Color of the background splash page when launching from home screen.'),
    '#size' => 8,
    '#default_value' => variable_get('pwa_theme_color', '#53B0EB'),
  ];
  if (module_exists('color')) {
    $form['pwa_background_color']['#value_callback'] = 'color_palette_color_value';
    $form['pwa_theme_color']['#value_callback'] = 'color_palette_color_value';
  }
  $form['pwa_start_url'] = [
    '#type' => 'textfield',
    '#title' => t('Start URL'),
    '#description' => t('Home page when launched from home screen. You can append a query string for analytics. For example <code>/home?startfrom=manifest</code>.'),
    '#default_value' => variable_get('pwa_start_url', '/?source=pwa'),
  ];
  $form['pwa_orientation'] = [
    '#type' => 'select',
    '#title' => t('Orientation'),
    '#options' => [
      'portrait' => t('Portrait'),
      'landscape' => t('Landscape'),
    ],
    '#default_value' => variable_get('pwa_orientation', 'portrait'),
  ];
  $form['pwa_display'] = [
    '#type' => 'select',
    '#title' => t('Display'),
    '#options' => [
      'fullscreen' => t('Full screen'),
      'standalone' => t('Standalone'),
      'minimal-ui' => t('Minimal UI'),
      'browser' => t('Browser'),
    ],
    '#description' => t('Determines whether the PWA will behave like a web page or a native app. <a href="@mdn-display" target="_blank" rel="noopener">Read more at MDN</a>.', [
      '@mdn-display' => 'https://developer.mozilla.org/en-US/docs/Web/Manifest#display',
    ]),
    '#default_value' => variable_get('pwa_display', 'standalone'),
  ];
  $form['pwa_icons'] = [
    '#type' => 'fieldset',
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#title' => t('Branding icons'),
    '#description' => t('Use <code>hook_pwa_manifest_alter()</code> to configure custom icons. <a href="@manifest-example" target="_blank" rel="noopener">See official example</a>.', [
      '@manifest-example' => 'https://git.drupalcode.org/project/pwa/-/blob/7.x-2.x/pwa.api.php#L33',
    ]),
  ];
  $form['#attached']['css'][] = drupal_get_path('module', 'pwa') . '/css/icons.css';

  // Define the SVG mask showing the safe zone.
  $svg = '<svg viewBox="0 0 100 100" style="width:0px;height:0px;" xmlns="http://www.w3.org/2000/svg"><defs><mask id="pwa-maskcircle"><rect fill="white" width="100%" height="100%"/><circle cx="50" cy="50" r="40" fill="black"/></mask><rect id="pwa-mask" fill="red" opacity="0.5" width="100%" height="100%" mask="url(#pwa-maskcircle)"/></defs></svg>';
  $svg_mask = '<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><use xlink:href="#pwa-mask"/></svg>';
  $maskable = FALSE;
  $rows = [];
  foreach (_pwa_manifest_file()['icons'] as $i => $icon) {
    $mask = strpos($icon['purpose'], 'maskable') !== FALSE;
    if ($mask) {
      $maskable = TRUE;
    }
    $rows[] = [
      '<pre>' . check_plain(str_replace('\\/', '/', json_encode($icon, JSON_PRETTY_PRINT))) . '</pre>',
      '<span class="pwa-maskable"><img src="' . $icon['src'] . '">' . ($mask ? $svg_mask : '') . '</span>',
    ];
  }
  $form['pwa_icons']['details'] = [
    '#type' => 'item',
    '#markup' => $svg . theme('table', [
      'header' => [
        t('Properties'),
        t('Image'),
      ],
      'rows' => $rows,
      'attributes' => [
        'id' => 'mytable-order',
      ],
    ]),
    '#description' => !$maskable ? '' : t('For maskable icons the circle inside the red area is the <a href="@maskable-spec" target="_blank" rel="noopener">safe zone</a>.', [
      '@maskable-spec' => 'https://w3c.github.io/manifest/#icon-masks',
    ]),
  ];
  $form = system_settings_form($form);

  // Wait for all the values to be saved before refreshing cache.
  $form['#submit'][] = 'pwa_admin_clear_cache';
  return $form;
}

/**
 * Configure PWA settings for Service Worker.
 */
function pwa_admin_configuration_sw() {
  $form = [];
  $form['intro'] = [
    '#markup' => '<p>' . t('Additional settings for the ServiceWorker.') . '</p>',
  ];
  $form['pwa_sw_everywhere'] = [
    '#type' => 'checkbox',
    '#title' => t('Load Serviceworker registration script on all pages'),
    '#description' => t('The call to register the serviceworker will be done on all pages.'),
    '#default_value' => variable_get('pwa_sw_everywhere', FALSE),
  ];
  $form['pwa_sw_phonehome'] = [
    '#type' => 'checkbox',
    '#title' => t('Phone home'),
    '#description' => t('When enabled the serviceworker will check periodically if it is still valid. By default the service worker will stay <a href="@staleurl">active for 24h maximum</a> after the serviceworker file is removed. Enable this feature is you need to invalidate the serviceworker before then.', [
      '@staleurl' => 'https://w3c.github.io/ServiceWorker/#service-worker-registration-stale',
    ]),
    '#default_value' => variable_get('pwa_sw_phonehome', TRUE),
  ];
  $form['pwa_workbox_url'] = [
    '#type' => 'textfield',
    '#title' => t('Path to workbox library'),
    '#description' => t('Provide a path or URL where the main workbox script is located. Leave empty to use the default URL %default.', [
      '%default' => PWA_WORKBOX_URL,
    ]),
    '#default_value' => variable_get('pwa_workbox_url', ''),
  ];
  $form = system_settings_form($form);

  // Wait for all the values to be saved before refreshing cache.
  $form['#submit'][] = 'pwa_admin_clear_cache';
  return $form;
}
function pwa_admin_configuration_sw_patterns() {
  $form = [];
  $form['intro'] = [
    '#markup' => '<p>' . t('Configure caching strategies for pages and assets, this configuration will be used by the workbox library.') . '</p>',
  ];
  $form['pwa_sw_cache_exclude'] = [
    '#type' => 'textarea',
    '#title' => t('Do not cache'),
    '#description' => t('URLs paths matching one of these patterns will never be cached by the Service Worker. One JavaScript regex per line.'),
    '#default_value' => variable_get('pwa_sw_cache_exclude', PWA_SW_CACHE_EXCLUDE),
  ];
  $form['asset'] = [
    '#type' => 'fieldset',
    '#title' => t('Assets configuration'),
    '#tree' => TRUE,
    '#description' => t('Define the strategy to use while serving the asset type, a maximum size above which requests will not be cached on the client side, and if responses from third party domains will be cached (useful if there is a CDN)'),
  ];
  $merged = array_replace_recursive(PWA_SW_ASSETS, variable_get('pwa_sw_asset_config', []));
  foreach ($merged as $type => $default) {
    $form['asset'][$type] = [
      '#type' => 'fieldset',
      '#title' => $type,
      '#attributes' => [
        'class' => [
          'container-inline',
        ],
      ],
    ];
    $form['asset'][$type]['strategy'] = [
      '#type' => 'select',
      '#title' => t('Strategy'),
      '#options' => [
        'DoNotCache' => t('Do not cache'),
        'CacheFirst' => t('Cache first'),
        'NetworkFirst' => t('Network first'),
        'StaleWhileRevalidate' => t('Stale while revalidate'),
        'NetworkOnly' => t('Network only'),
        'CacheOnly' => t('Cache only'),
      ],
      '#default_value' => $default['strategy'],
    ];
    $form['asset'][$type]['external'] = [
      '#type' => 'checkbox',
      '#title' => t('Cache third party'),
      '#default_value' => $default['external'],
      '#prefix' => '&nbsp;&nbsp;&nbsp;',
      '#states' => [
        'invisible' => [
          'select[name="asset[' . $type . '][strategy]"]' => [
            'value' => 'DoNotCache',
          ],
        ],
      ],
    ];
    $form['asset'][$type]['limitMaxSize'] = [
      '#type' => 'checkbox',
      '#title' => t('Limit maximum size'),
      '#default_value' => $default['limitMaxSize'],
      '#prefix' => '&nbsp;&nbsp;&nbsp;',
      '#states' => [
        'invisible' => [
          'select[name="asset[' . $type . '][strategy]"]' => [
            'value' => 'DoNotCache',
          ],
        ],
      ],
    ];

    // Maximum size in KB of the responses to put in the cache.
    // The default value is taken from the httparchive: https://httparchive.org/reports/state-of-images#bytesImg
    $form['asset'][$type]['maxSize'] = [
      '#type' => 'textfield',
      '#title' => t('Maxium size'),
      '#default_value' => $default['maxSize'],
      '#size' => 4,
      '#field_suffix' => 'kB',
      '#attributes' => [
        'placeholder' => PWA_SW_ASSETS[$type]['maxSize'],
      ],
      '#prefix' => '&nbsp;&nbsp;&nbsp;',
      '#states' => [
        'invisible' => [
          [
            'input[name="asset[' . $type . '][limitMaxSize]"]' => [
              'checked' => FALSE,
            ],
          ],
          [
            'select[name="asset[' . $type . '][strategy]"]' => [
              'value' => 'DoNotCache',
            ],
          ],
        ],
      ],
    ];
  }
  $form['pwa_sw_patterns_page'] = [
    '#type' => 'textarea',
    '#title' => t('Caching strategy for pages'),
    '#description' => t('The format is Strategy|Pattern, for example <code>NetworkFirst|/node/.*</code>. <br>Available strategies: CacheFirst, CacheOnly, NetworkFirst, NetworkOnly, StaleWhileRevalidate.'),
    '#default_value' => variable_get('pwa_sw_patterns_page', ''),
  ];
  $form = system_settings_form($form);

  // Transform the values for pwa_sw_assets variable.
  array_unshift($form['#submit'], 'pwa_admin_configuration_sw_patterns_submit');

  // Wait for all the values to be saved before refreshing cache.
  $form['#submit'][] = 'pwa_admin_clear_cache';
  return $form;
}
function pwa_admin_configuration_sw_patterns_submit($form, &$form_state) {
  $values =& $form_state['values'];
  variable_set('pwa_sw_asset_config', $values['asset']);
  unset($values['asset']);
}
function pwa_admin_configuration_sw_precache() {
  $form = [];
  $form['intro'] = [
    '#markup' => '<p>' . t('Use cautiously, precached files and pages will always be served from the cache, even if they are updated on the website.') . '</p>',
  ];
  $form['pwa_sw_precache_page'] = [
    '#type' => 'textarea',
    '#title' => t('Precache pages'),
    '#description' => t('Relative URLs. Pages acessible offline even if the user did not visit them. The offline page is added by default. List of pages that will be loaded on serviceworker installation. Only set pages that do not change frequently, page assets are not automatically saved.'),
    '#default_value' => variable_get('pwa_sw_precache_page', '/offline'),
  ];
  $form['pwa_sw_precache_asset'] = [
    '#type' => 'textarea',
    '#title' => t('Precache assets'),
    '#description' => t('Relative URLs. List of files that will be loaded on serviceworker installation. The files should not change much, like logos, fonts, icons, etc.'),
    '#default_value' => variable_get('pwa_sw_precache_asset', ''),
  ];
  $form = system_settings_form($form);

  // Wait for all the values to be saved before refreshing cache.
  $form['#submit'][] = 'pwa_admin_clear_cache';
  return $form;
}

/**
 * Helper function to refresh Service Worker once admin settings are saved.
 */
function pwa_admin_clear_cache($form, &$form_state) {
  cache_clear_all([
    'pwa:serviceworker',
    'pwa:manifest',
    'pwa:apple',
  ], 'cache');
}

Functions

Namesort descending Description
pwa_admin_clear_cache Helper function to refresh Service Worker once admin settings are saved.
pwa_admin_configuration Main configuration page for PWA.
pwa_admin_configuration_manifest Configure PWA settings for manifest.json
pwa_admin_configuration_sw Configure PWA settings for Service Worker.
pwa_admin_configuration_sw_patterns
pwa_admin_configuration_sw_patterns_submit
pwa_admin_configuration_sw_precache
pwa_admin_generate_drusplash