Plugin Directory

Changeset 3446504


Ignore:
Timestamp:
01/25/2026 11:30:49 AM (6 weeks ago)
Author:
griffinforms
Message:

Release 2.1.5.0

Location:
griffinforms-form-builder/trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • griffinforms-form-builder/trunk/admin/css/griffinforms-settings.css

    r3357957 r3446504  
    114114}
    115115
     116.griffinforms-settings-option-history .gf-settings-history-toggle {
     117    margin-left: 6px;
     118    font-size: 12px;
     119}
     120
     121.griffinforms-settings-option-history .gf-settings-history-list {
     122    margin-top: 6px;
     123}
     124
     125.griffinforms-settings-option-history .gf-settings-history-items {
     126    margin: 0;
     127    padding-left: 18px;
     128    font-size: 12px;
     129}
     130
     131.griffinforms-settings-option-history .gf-settings-history-item {
     132    margin: 2px 0;
     133}
     134
    116135.form-table td fieldset label, .form-table td fieldset li, .form-table td fieldset p {
    117136    font-size: 13px;
  • griffinforms-form-builder/trunk/admin/html/pages/settings/format.php

    r3425584 r3446504  
    185185        }
    186186   
    187         $last_value = $this->formatOptionHistoryValue($this->option['last_value']);
    188         $last_editor_id = $this->option['last_editor'];
    189         $last_edited_on = $this->option['last_edited_on'];
     187        $history = $this->option['history'] ?? [];
     188        $history_count = is_array($history) ? count($history) : 0;
    190189        $max_len = 160;
    191         $last_value_full = (string) $last_value;
    192         $last_value_trimmed = $last_value_full;
    193         if (strlen($last_value_full) > $max_len) {
    194             $last_value_trimmed = substr($last_value_full, 0, $max_len) . '...';
    195         }
     190
     191        $latest_entry = $history_count > 0 ? $history[0] : null;
     192        $latest_value = $latest_entry['value'] ?? $this->option['last_value'];
     193        $last_editor_id = $latest_entry['editor'] ?? $this->option['last_editor'];
     194        $last_edited_on = $latest_entry['edited_on'] ?? $this->option['last_edited_on'];
     195
     196        [$last_value_trimmed, $last_value_full, $last_value_truncated] = $this->getHistoryValueDisplay($latest_value, $max_len);
    196197   
    197198        // Fetch editor details based on user ID
     
    210211        echo '<span>' . esc_html(__('on', 'griffinforms-form-builder')) . ' ' . esc_html($formatted_time) . '</span>. ';
    211212        echo '<span>' . esc_html(__('Its previous value was', 'griffinforms-form-builder')) . ' <code>' . esc_html($last_value_trimmed) . '</code>';
    212         if (strlen($last_value_full) > $max_len) {
     213        if ($last_value_truncated) {
    213214            echo ' <a href="#" class="gf-settings-history-view-full" data-gf-full-value="' . esc_attr($last_value_full) . '">' . esc_html__('View full', 'griffinforms-form-builder') . '</a>';
    214215        }
    215216        echo '</span>';
     217
     218        if ($history_count > 1) {
     219            $toggle_text = sprintf(__('View history (%d)', 'griffinforms-form-builder'), $history_count - 1);
     220            echo ' <a href="#" class="gf-settings-history-toggle" data-gf-collapsed-text="' . esc_attr($toggle_text) . '" data-gf-expanded-text="' . esc_attr(__('Hide history', 'griffinforms-form-builder')) . '" aria-expanded="false">' . esc_html($toggle_text) . '</a>';
     221
     222            echo '<div class="gf-settings-history-list" style="display: none;">';
     223            echo '<ul class="gf-settings-history-items">';
     224            for ($i = 1; $i < $history_count; $i++) {
     225                $entry = $history[$i];
     226                $entry_editor_id = $entry['editor'] ?? null;
     227                $entry_editor = $entry_editor_id ? get_user_by('id', $entry_editor_id) : null;
     228                $entry_editor_name = $entry_editor ? $entry_editor->display_name : __('Unknown User', 'griffinforms-form-builder');
     229                $entry_time = $entry['edited_on'] ?? '';
     230                $entry_time_formatted = $entry_time ? wp_date(get_option('date_format') . ' ' . get_option('time_format'), strtotime($entry_time)) : '';
     231
     232                $previous_value = $entry['value'] ?? '';
     233                $new_value = $history[$i - 1]['value'] ?? '';
     234
     235                [$prev_trimmed, $prev_full, $prev_truncated] = $this->getHistoryValueDisplay($previous_value, $max_len);
     236                [$new_trimmed, $new_full, $new_truncated] = $this->getHistoryValueDisplay($new_value, $max_len);
     237
     238                echo '<li class="gf-settings-history-item">';
     239                echo esc_html($entry_editor_name) . ' ' . esc_html(__('changed value from', 'griffinforms-form-builder')) . ' <code>' . esc_html($prev_trimmed) . '</code>';
     240                if ($prev_truncated) {
     241                    echo ' <a href="#" class="gf-settings-history-view-full" data-gf-full-value="' . esc_attr($prev_full) . '">' . esc_html__('View full', 'griffinforms-form-builder') . '</a>';
     242                }
     243                echo ' ' . esc_html(__('to', 'griffinforms-form-builder')) . ' <code>' . esc_html($new_trimmed) . '</code>';
     244                if ($new_truncated) {
     245                    echo ' <a href="#" class="gf-settings-history-view-full" data-gf-full-value="' . esc_attr($new_full) . '">' . esc_html__('View full', 'griffinforms-form-builder') . '</a>';
     246                }
     247                if (!empty($entry_time_formatted)) {
     248                    echo ' ' . esc_html(__('on', 'griffinforms-form-builder')) . ' ' . esc_html($entry_time_formatted) . '.';
     249                }
     250                echo '</li>';
     251            }
     252            echo '</ul>';
     253            echo '</div>';
     254        }
    216255        echo '</div>';
    217256    }
     
    236275        return (string) $value;
    237276    }
     277
     278    protected function getHistoryValueDisplay($value, $max_len): array
     279    {
     280        $formatted = $this->formatOptionHistoryValue($value);
     281        $full = (string) $formatted;
     282        $trimmed = $full;
     283        $truncated = false;
     284        if (strlen($full) > $max_len) {
     285            $trimmed = substr($full, 0, $max_len) . '...';
     286            $truncated = true;
     287        }
     288        return [$trimmed, $full, $truncated];
     289    }
    238290}
  • griffinforms-form-builder/trunk/admin/js/griffinforms-settings.js

    r3425584 r3446504  
    1111        }
    1212    });
     13
     14    $(document).on('click', '.gf-settings-history-toggle', function(e) {
     15        e.preventDefault();
     16        var toggle = $(this);
     17        var list = toggle.closest('.griffinforms-settings-option-history').find('.gf-settings-history-list');
     18        if (!list.length) {
     19            return;
     20        }
     21        list.slideToggle(150, function() {
     22            var isOpen = list.is(':visible');
     23            toggle.attr('aria-expanded', isOpen ? 'true' : 'false');
     24            var text = isOpen ? toggle.attr('data-gf-expanded-text') : toggle.attr('data-gf-collapsed-text');
     25            if (text) {
     26                toggle.text(text);
     27            }
     28        });
     29    });
    1330});
  • griffinforms-form-builder/trunk/config.php

    r3439965 r3446504  
    55class Config
    66{
    7     public const VERSION = '2.1.4.0';
     7    public const VERSION = '2.1.5.0';
    88    public const DB_VER = '1.0';
    99    public const PHP_REQUIRED = '8.2';
  • griffinforms-form-builder/trunk/frontend/html/forms/form.php

    r3421663 r3446504  
    106106        $html = '<div class="griffinforms-form' . absint($this->id) . '-container row' . $theme_class . $theme_slug_class . (!$is_theme_set && !empty($this->bs_form_main_container_classes) ? ' ' . esc_attr($this->bs_form_main_container_classes) : '') . '" data-gf-form-id="' . absint($this->id) . '"' . $theme_identifier_attr . '>';
    107107        $html .= $theme_assets_markup . $theme_css;
     108        $html .= '<noscript><div class="alert alert-warning small" role="alert">';
     109        $html .= '<strong class="fw-medium">' . esc_html__('JavaScript required', 'griffinforms-form-builder') . '</strong>. ';
     110        $html .= esc_html__('This form needs JavaScript to function correctly. Please enable JavaScript in your browser and reload the page.', 'griffinforms-form-builder');
     111        $html .= '</div></noscript>';
    108112
    109113        if ($this->resume_attempted && !empty($this->resume_payment_error)) {
  • griffinforms-form-builder/trunk/griffinforms.php

    r3439965 r3446504  
    44 * Plugin URI:        https://griffinforms.com/
    55 * Description:       A powerful and flexible form builder for WordPress. Create multi-page forms with drag-and-drop ease, custom validations, and full submission management.
    6  * Version:           2.1.4.0
     6 * Version:           2.1.5.0
    77 * Requires at least: 6.6
    88 * Requires PHP:      8.2
  • griffinforms-form-builder/trunk/readme.txt

    r3441920 r3446504  
    172172== Changelog ==
    173173
     174= 2.1.5.0 – 2026-01-25 =
     175* Improvement: Settings history now retains the last 5 changes with an expandable history list.
     176* Improvement: Frontend forms now show a “JavaScript required” notice when scripts are disabled.
     177
    174178= 2.1.4.0 – 2026-01-15 =
    175179* Feature: New Gutenberg block for embedding GriffinForms with a lightweight editor preview.
     
    466470== Upgrade Notice ==
    467471
     472= 2.1.5.0 =
     473Adds a settings history trail with expandable entries and shows a JavaScript-required notice when scripts are disabled on the frontend. Recommended update.
     474
    468475= 2.1.4.0 =
    469476Adds a native Gutenberg block with a lightweight, theme-aware editor preview and quick links to edit forms. Recommended update for block editor users.
  • griffinforms-form-builder/trunk/settings.php

    r3421663 r3446504  
    163163            $current_value = $this->maybeDecode($this->getOption($option_name));
    164164            if ($current_value != $option_value) {
     165                $now = current_time('mysql', 1);
     166                $current_editor = get_current_user_id();
     167                $last_option_value = null;
     168                $last_editor = null;
     169                $last_edited_on = null;
     170
     171                $history_row = $wpdb->get_row(
     172                    $wpdb->prepare(
     173                        "SELECT last_option_value, editor, edited_on FROM %i WHERE option_name = %s",
     174                        $this->table,
     175                        $option_name
     176                    )
     177                );
     178                if ($history_row) {
     179                    $last_option_value = $history_row->last_option_value ?? null;
     180                    $last_editor = $history_row->editor ?? null;
     181                    $last_edited_on = $history_row->edited_on ?? null;
     182                }
     183
    165184                // Determine whether the option should be encrypted
    166185                if (in_array($option_name, $this->encrypted_options)) {
     
    175194                        $encrypted_value = openssl_encrypt(maybe_serialize($option_value), $method, $secure_key, 0, $iv);
    176195   
    177                         // Encrypt the past (current) value using the same key and IV
    178                         $encrypted_last_value = openssl_encrypt(maybe_serialize($current_value), $method, $secure_key, 0, $iv);
     196                        // Build and encrypt history array using the same key and IV
     197                        $history_entries = $this->normalizeHistoryEntries(
     198                            $option_name,
     199                            $this->maybeDecode($this->decryptValue($option_name, $last_option_value)),
     200                            $last_editor,
     201                            $last_edited_on
     202                        );
     203                        array_unshift($history_entries, [
     204                            'value' => $current_value,
     205                            'editor' => $current_editor,
     206                            'edited_on' => $now,
     207                        ]);
     208                        $history_entries = array_slice($history_entries, 0, 5);
     209                        $encrypted_last_value = openssl_encrypt(maybe_serialize($history_entries), $method, $secure_key, 0, $iv);
    179210                    } else {
    180211                    // Handle missing keys error
     
    186217                    // If the option is not in encrypted options, keep values as plain text
    187218                    $encrypted_value = maybe_serialize($option_value);
    188                     $encrypted_last_value = maybe_serialize($current_value);
     219                    $history_entries = $this->normalizeHistoryEntries(
     220                        $option_name,
     221                        $this->maybeDecode($last_option_value),
     222                        $last_editor,
     223                        $last_edited_on
     224                    );
     225                    array_unshift($history_entries, [
     226                        'value' => $current_value,
     227                        'editor' => $current_editor,
     228                        'edited_on' => $now,
     229                    ]);
     230                    $history_entries = array_slice($history_entries, 0, 5);
     231                    $encrypted_last_value = maybe_serialize($history_entries);
    189232                }
    190233   
     
    195238                        'last_option_value' => $encrypted_last_value,  // Encrypted or plain text last value
    196239                        'option_group' => sanitize_text_field($option_group),
    197                         'editor' => get_current_user_id(),
    198                         'edited_on' => current_time('mysql', 1),
     240                        'editor' => $current_editor,
     241                        'edited_on' => $now,
    199242                    ),
    200243                    array('option_name' => $option_name),  // Where clause
     
    330373            $current_value = $this->maybeDecode($this->decryptValue($option_name, $result->option_value));
    331374            $last_value = $this->maybeDecode($this->decryptValue($option_name, $result->last_option_value));
     375            $history = $this->normalizeHistoryEntries($option_name, $last_value, $result->editor, $result->edited_on);
     376            $history = array_slice($history, 0, 5);
     377            $first_entry = !empty($history) ? $history[0] : null;
    332378   
    333379            return [
    334380                'current_value' => $current_value,
    335                 'last_value' => $last_value,
    336                 'last_editor' => $result->editor,
    337                 'last_edited_on' => $result->edited_on,
     381                'last_value' => $first_entry['value'] ?? $last_value,
     382                'last_editor' => $first_entry['editor'] ?? $result->editor,
     383                'last_edited_on' => $first_entry['edited_on'] ?? $result->edited_on,
     384                'history' => $history,
    338385            ];
    339386        }
     
    345392            'last_editor' => null,
    346393            'last_edited_on' => null,
     394            'history' => [],
    347395        ];
     396    }
     397
     398    protected function normalizeHistoryEntries($option_name, $last_value, $last_editor, $last_edited_on): array
     399    {
     400        if ($last_value === 'none' || $last_value === null || $last_value === '') {
     401            return [];
     402        }
     403
     404        if (is_array($last_value)) {
     405            $is_history = true;
     406            foreach ($last_value as $entry) {
     407                if (!is_array($entry) || !array_key_exists('value', $entry)) {
     408                    $is_history = false;
     409                    break;
     410                }
     411            }
     412            if ($is_history) {
     413                return $last_value;
     414            }
     415        }
     416
     417        return [[
     418            'value' => $last_value,
     419            'editor' => $last_editor,
     420            'edited_on' => $last_edited_on,
     421        ]];
    348422    }
    349423
Note: See TracChangeset for help on using the changeset viewer.