Plugin Directory

Changeset 3424024


Ignore:
Timestamp:
12/20/2025 01:30:07 AM (3 months ago)
Author:
lbell
Message:

bugfix and improvements

Location:
pretty-google-calendar/trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • pretty-google-calendar/trunk/admin/admin.php

    r3015663 r3424024  
    6262      ?>
    6363    </form>
    64     </div>
    6564<?php
    6665  }
     
    7978      'pgcal-main-settings',
    8079      esc_attr__('Usage', 'pretty-google-calendar'),
    81       array($this, 'pgcal_pring_main_info'), // Callback
     80      array($this, 'pgcal_print_main_info'), // Callback
    8281      'pgcal-setting-admin' // Page
    8382    );
     
    9089      'pgcal-main-settings' // Section
    9190    );
    92 
    93     // add_settings_field(
    94     //   'use_tooltip',
    95     //   esc_attr__('Use Tooltip (Migrating to shortcode attribute use_tooltip)', 'pretty-google-calendar'),
    96     //   array($this, 'pgcal_tooltip_callback'),
    97     //   'pgcal-setting-admin',
    98     //   'pgcal-main-settings'
    99     // );
    100 
    101     // add_settings_field(
    102     //   'no_link',
    103     //   esc_attr__('Disable Event Link (Migrating to shortcode attribute no_link)', 'pretty-google-calendar'),
    104     //   array($this, 'pgcal_no_link_callback'),
    105     //   'pgcal-setting-admin',
    106     //   'pgcal-main-settings'
    107     // );
    10891  }
    10992
     
    117100    if (isset($input['google_api']))
    118101      // TODO test api?
    119       $sanitized_input['google_api'] = $input['google_api'];
    120 
    121     // if (isset($input['use_tooltip']))
    122     //   $sanitized_input['use_tooltip'] = sanitize_text_field($input['use_tooltip']);
    123 
    124     // if (isset($input['no_link']))
    125     //   $sanitized_input['no_link'] = sanitize_text_field($input['no_link']);
    126 
     102      $sanitized_input['google_api'] = sanitize_text_field($input['google_api']);
    127103    return $sanitized_input;
    128104  }
     
    131107   * Print the Section text
    132108   */
    133   public function pgcal_pring_main_info() {
     109  public function pgcal_print_main_info() {
    134110    printf(
    135111      '<p>%s [pretty_google_calendar gcal="address@group.calendar.google.com"] </p>
     
    153129    );
    154130  }
    155 
    156   // public function pgcal_tooltip_callback() {
    157   //   printf(
    158   //     '<input title="%s" type="checkbox" id="use_tooltip" name="pgcal_settings[use_tooltip]" value="yes" %s />',
    159   //     esc_html__("Use the popper/tooltip plugin to display event information.", "pretty-google-calendar"),
    160   //     isset($this->options['use_tooltip']) ? 'checked' : ''
    161   //   );
    162   // }
    163 
    164   // public function pgcal_no_link_callback() {
    165   //   printf(
    166   //     '<input title="%s" type="checkbox" id="no_link" name="pgcal_settings[no_link]" value="yes" %s />',
    167   //     esc_html__("Disable the link to the calendar.google.com event.", "pretty-google-calendar"),
    168   //     isset($this->options['no_link']) ? 'checked' : ''
    169   //   );
    170   // }
    171131}
  • pretty-google-calendar/trunk/init/init.php

    r3422453 r3424024  
    6868 */
    6969function pgcal_register_admin_css() {
    70   wp_register_style('pgcal-admin-css', PGCAL_URL . 'public/css/pgcal-admin.css');
     70  wp_register_style('pgcal-admin-css', PGCAL_URL . 'public/css/pgcal-admin.css', array(), PGCAL_VER);
    7171  wp_enqueue_style('pgcal-admin-css');
    7272}
  • pretty-google-calendar/trunk/init/shortcode.php

    r3422486 r3424024  
    1818      'show_today_button'          => "true",
    1919      'show_title'                 => "true",
    20       'id_hash'                    => bin2hex(random_bytes(5)),
     20      'id_hash'                    => pgc_generate_unique_id_hash(),
    2121      'use_tooltip'                => isset($globalSettings['use_tooltip']) ? "true" : "false",
    2222      'no_link'                    => isset($globalSettings['no_link']) ? "true" : "false",
     
    3030  $pgcalSettings["id_hash"] = preg_replace('/[\W]/', '', $pgcalSettings["id_hash"]);
    3131
     32  // Auto-resolve views based on user-provided attributes
     33  $pgcalSettings['views'] = pgc_resolve_views($atts, $args);
     34
     35  // Auto-resolve initial_view based on views (validate it's in the list, or pick smartly)
     36  $pgcalSettings['initial_view'] = pgc_resolve_initial_view($pgcalSettings['views'], $pgcalSettings['initial_view']);
     37
    3238  // Include public-facing global settings needed by the frontend.
    3339  // The Google API key is intended for client-side use to render public
    3440  // calendars; embed it directly in the inline settings so anonymous
    3541  // visitors don't rely on an AJAX endpoint to retrieve it.
    36   if ( isset($globalSettings['google_api']) ) {
     42  if (isset($globalSettings['google_api'])) {
    3743    $pgcalSettings['google_api'] = $globalSettings['google_api'];
    3844  }
     
    7985
    8086  $shortcode_output = "
    81   <div id='pgcalendar-" . $pgcalSettings["id_hash"] . "' class='pgcal-container'>" . esc_html__("loading...", "pretty-google-calendar") . "</div>
     87  <div id='pgcalendar-" . esc_attr($pgcalSettings["id_hash"]) . "' class='pgcal-container'>" . esc_html__("loading...", "pretty-google-calendar") . "</div>
    8288  <div class='pgcal-branding'>" . esc_html__("Powered by", "pretty-google-calendar") . " <a href='https://wordpress.org/plugins/pretty-google-calendar/'>Pretty Google Calendar</a></div>
    8389  ";
  • pretty-google-calendar/trunk/pretty-google-calendar.php

    r3422486 r3424024  
    44Plugin URI: https://github.com/lbell/pretty-google-calendar
    55Description: Google Calendars that aren't ugly.
    6 Version: 2.0.2
     6Version: 2.1.0
    77Author: LBell
    88Author URI: http://lorenbell.com
     
    2727
    2828
    29 define('PGCAL_VER', "2.0.2");
     29define('PGCAL_VER', "2.1.0");
    3030define('PGCAL_DIR', plugin_dir_path(__FILE__)); // Trailing slash
    31 define('PGCAL_TEMPLATE_DIR', PGCAL_DIR . 'templates/');
     31// define('PGCAL_TEMPLATE_DIR', PGCAL_DIR . 'templates/');
    3232define('PGCAL_URL', plugin_dir_url(__FILE__));
    3333
    3434load_plugin_textdomain('pretty-google-calendar', false, PGCAL_DIR . 'languages');
    3535
    36 require(PGCAL_DIR . 'util/utils.php');
    37 require(PGCAL_DIR . 'admin/admin.php');
    38 require(PGCAL_DIR . 'init/shortcode.php');
    39 require(PGCAL_DIR . 'init/init.php');
     36
     37require_once PGCAL_DIR . 'util/utils.php';
     38require_once PGCAL_DIR . 'admin/admin.php';
     39require_once PGCAL_DIR . 'init/shortcode.php';
     40require_once PGCAL_DIR . 'init/init.php';
    4041
    4142// require(PGCAL_DIR . 'dev/console-log.php'); // DEBUG
  • pretty-google-calendar/trunk/public/js/pgcal.js

    r3422486 r3424024  
    3535  // attempt to fetch globals via AJAX (admin-only usage).
    3636  let globalSettings = {};
    37   if (pgcalSettings && pgcalSettings['google_api']) {
    38     globalSettings = { 'google_api': pgcalSettings['google_api'] };
     37  if (pgcalSettings && pgcalSettings["google_api"]) {
     38    globalSettings = { google_api: pgcalSettings["google_api"] };
    3939  } else {
    4040    globalSettings = await pgcalFetchGlobals(ajaxurl, ajaxNonce);
     
    7979          meridiem: "short",
    8080        },
     81      },
     82      // Standard List Views
     83      listDay: {
     84        type: "list",
     85        duration: { days: 1 },
     86        buttonText: pgcalSettings["custom_list_button"],
     87      },
     88      listWeek: {
     89        type: "list",
     90        duration: { days: 7 },
     91        buttonText: pgcalSettings["custom_list_button"],
     92      },
     93      listMonth: {
     94        type: "list",
     95        duration: { months: 1 },
     96        buttonText: pgcalSettings["custom_list_button"],
     97      },
     98      listYear: {
     99        type: "list",
     100        duration: { years: 1 },
     101        buttonText: pgcalSettings["custom_list_button"],
    81102      },
    82103      // Custom List View
  • pretty-google-calendar/trunk/readme.txt

    r3422486 r3424024  
    66Requires at least: 3.0
    77Tested up to: 6.9
    8 Stable tag: 2.0.2
     8Stable tag: 2.1.0
    99License: GPLv2 or later
    1010License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    6767
    6868`initial_view="dayGridMonth"`
    69 Sets the default view to be displayed when opening the page. Defaults to "dayGridMonth".
     69Sets the default view to be displayed when opening the page. Defaults to "dayGridMonth". Note: If only one view is specified in "views", "initial_view" will automatically be set to that view and does not need to be specified.
    7070
    7171`enforce_listview_on_mobile="true"`
     
    151151
    152152== Changelog ==
     153= 2.1.0 =
     154
     155- Fixed: Better list args parsing (Fixes #57)
     156- Fixed: Removed hardcoded timezone argument for correct local time display. Override with `fc_args='{"timeZone":"###"}'` if needed.
     157- Fixed: Admin CSS versioning
     158- Fixed: Sanitization of Google API key in admin
     159- Improved: id_hash generation
     160- Improved: Automatic initial_view resolution when only one view is specified (Fixes #51)
     161
    153162= 2.0.2 =
    154163
    155 - Fixed: Prevent unauthorized disclosure of the Google API (CVE-2025-12898)
     164- Fixed: Security fix (no known exploit)
    156165
    157166= 2.0.1 =
  • pretty-google-calendar/trunk/util/utils.php

    r2613868 r3424024  
    11<?php
    2 // Shhhh
     2
     3/**
     4 * Generate a guaranteed unique ID hash for calendar instances on a page.
     5 *
     6 * Uses a static counter combined with a timestamp to guarantee uniqueness
     7 * across multiple calendar shortcodes on the same page load. This ensures
     8 * that even 200+ calendars on one page will never have ID collisions.
     9 *
     10 * @return string Unique hex ID hash
     11 */
     12function pgc_generate_unique_id_hash() {
     13  static $instance_counter = 0;
     14  $instance_counter++;
     15
     16  // Combine timestamp (microseconds) + instance counter + random for good measure
     17  // timestamp (8 bytes) + counter (4 bytes) = 12 bytes, highly unlikely to collide
     18  $unique_data = microtime(true) . '-' . $instance_counter . '-' . wp_rand(1000, 9999);
     19
     20  return bin2hex(hash('sha256', $unique_data, true));
     21}
     22
     23/**
     24 * Automatically resolve the calendar views based on user-provided attributes.
     25 *
     26 * Intelligently handles view configuration with the following logic:
     27 * - If user provides list_type but not views: auto-adjust views to include it
     28 * - If user provides fc_args but not views/list_type: use only dayGridMonth
     29 * - If user provides views explicitly: use as-is, no auto-adjustment
     30 * - Otherwise: use defaults (dayGridMonth, listCustom)
     31 *
     32 * @param array $shortcode_atts User-provided shortcode attributes
     33 * @param array $parsed_args    Parsed arguments with defaults applied
     34 * @return string Resolved views string (comma-separated)
     35 */
     36function pgc_resolve_views($shortcode_atts, $parsed_args) {
     37  $list_type = trim($parsed_args['list_type']);
     38  $current_views = $parsed_args['views'];
     39
     40  // Determine what the user explicitly provided
     41  $user_provided_views = isset($shortcode_atts['views']);
     42  $user_provided_list_type = isset($shortcode_atts['list_type']);
     43  $user_provided_fc_args = isset($shortcode_atts['fc_args']) && $shortcode_atts['fc_args'] !== '{}';
     44
     45  // If user explicitly provided views, use them as-is
     46  if ($user_provided_views) {
     47    return $current_views;
     48  }
     49
     50  // If user provided fc_args without views/list_type, use only dayGridMonth
     51  if ($user_provided_fc_args && !$user_provided_list_type) {
     52    return 'dayGridMonth';
     53  }
     54
     55  // If user provided list_type but not views, auto-adjust
     56  if ($user_provided_list_type) {
     57    if ($list_type === 'listCustom') {
     58      // Ensure listCustom is in the views
     59      $views = array_map('trim', explode(',', $current_views));
     60      $list_type_found = false;
     61
     62      foreach ($views as $view) {
     63        if (stripos($view, $list_type) !== false) {
     64          $list_type_found = true;
     65          break;
     66        }
     67      }
     68
     69      if (!$list_type_found) {
     70        $views[] = $list_type;
     71        return implode(', ', $views);
     72      }
     73
     74      return $current_views;
     75    } else {
     76      // Replace listCustom with the specified list_type
     77      return 'dayGridMonth, ' . $list_type;
     78    }
     79  }
     80
     81  // Default: use existing views
     82  return $current_views;
     83}
     84
     85/**
     86 * Automatically resolve the initial_view based on the provided views.
     87 *
     88 * Intelligently handles initial_view configuration:
     89 * - If initial_view is in the views list: use it (valid choice, either user-specified or default)
     90 * - If only one view is specified: use that view
     91 * - If multiple views and default view (dayGridMonth) is in the list: use the default
     92 * - If multiple views but default is NOT in the list: use the first view they provided
     93 *
     94 * @param string $views        The resolved views string (comma-separated)
     95 * @param string $initial_view The currently set initial_view value (default or user-provided)
     96 * @return string The resolved initial_view
     97 */
     98function pgc_resolve_initial_view($views, $initial_view) {
     99  // Parse views into individual view names
     100  $view_list = array_map('trim', explode(',', $views));
     101
     102  // If there's only one view, use it as the initial view
     103  if (count($view_list) === 1) {
     104    return $view_list[0];
     105  }
     106
     107  // For multiple views, check if the current initial_view is in the list
     108  $initial_view_trimmed = trim($initial_view);
     109  if (in_array($initial_view_trimmed, $view_list, true)) {
     110    // It's valid, use it
     111    return $initial_view_trimmed;
     112  }
     113
     114  // initial_view is not in the views list, pick one intelligently
     115  $default_view = 'dayGridMonth';
     116  $has_default_in_views = in_array($default_view, $view_list, true);
     117
     118  // If default view is in their list, use it
     119  if ($has_default_in_views) {
     120    return $default_view;
     121  }
     122
     123  // If default view is NOT in their list, use their first view
     124  return $view_list[0];
     125}
Note: See TracChangeset for help on using the changeset viewer.