Plugin Directory

Changeset 3458969


Ignore:
Timestamp:
02/11/2026 01:02:01 PM (7 weeks ago)
Author:
FreelanceDirectZA
Message:

Release version 1.5.0 - Added admin shortcode builder, awareness refactor, developer hook, builder integrations.

Location:
cancer-awareness-ribbon-shortcode
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • cancer-awareness-ribbon-shortcode/trunk/cancer-awareness-ribbon.php

    r3458865 r3458969  
    33/**
    44 * Plugin Name: Cancer Awareness Ribbon Shortcode
    5  * Description: Adds a shortcode to display a cancer awareness ribbon whose colors change based on the current month (or by type override).
    6  * Version: 1.4.0
     5 * Description: Adds a shortcode to display an awareness ribbon whose colors change based on the current month (or by type override).
     6 * Version: 1.5.0
    77 * Author: Parallel Media
    88 * License: GPLv2 or later
     
    1919        plugins_url('assets/css/cancer-awareness-ribbon.css', __FILE__),
    2020        [],
    21         '1.4.0'
     21        '1.5.0'
    2222    );
    2323}, 20);
    2424
    2525/**
    26  * Cancer list: type => [label, month, colors[], optional category]
     26 * Default awareness types list (internal).
     27 * Schema: type => [label, month, colors[], optional category]
    2728 * Categories: Cancer, Medical, Social, Global
    2829 */
    29 function car_get_cancers(): array
     30function car_get_default_awareness_types(): array
    3031{
    3132    return [
     
    6263        'thyroid_cancer' => ['label' => 'Thyroid Cancer', 'month' => 9, 'colors' => ['#800080', '#00B3B3', '#FF69B4']],
    6364
    64         // v1.1 - additional awareness ribbons (non-cancer)
     65        // additional awareness ribbons (non-cancer)
    6566        'hiv_aids_awareness' => ['label' => 'HIV / AIDS Awareness', 'month' => 12, 'colors' => ['#E10600'], 'category' => 'Global'],
    6667        'autism_awareness' => ['label' => 'Autism Awareness', 'month' => 4, 'colors' => ['#1877F2'], 'category' => 'Medical'],
     
    6869        'diabetes_awareness' => ['label' => 'Diabetes Awareness', 'month' => 11, 'colors' => ['#9E9E9E'], 'category' => 'Medical'],
    6970        'heart_disease_awareness' => ['label' => 'Heart Disease Awareness', 'month' => 2, 'colors' => ['#C62828'], 'category' => 'Medical'],
    70 
    71         // v1.2 - additional awareness ribbons (non-cancer)
    7271        'domestic_violence_awareness' => ['label' => 'Domestic Violence Awareness', 'month' => 10, 'colors' => ['#800080'], 'category' => 'Social'],
    7372        'prostate_health_awareness' => ['label' => 'Prostate Health Awareness', 'month' => 9, 'colors' => ['#7EC8E3'], 'category' => 'Medical'],
     
    7574        'lung_health_awareness' => ['label' => 'Lung Health Awareness', 'month' => 11, 'colors' => ['#FFFFFF'], 'category' => 'Medical'],
    7675        'alzheimers_awareness' => ['label' => 'Alzheimer’s Awareness', 'month' => 11, 'colors' => ['#5E2D79'], 'category' => 'Medical'],
    77 
    78         // v1.3 - additional awareness ribbons (non-cancer)
    7976        'pride_awareness' => ['label' => 'Pride Awareness', 'month' => 6, 'colors' => ['#E40303', '#FF8C00', '#FFED00', '#008026', '#004DFF', '#750787'], 'category' => 'Social'],
    8077        'veterans_awareness' => ['label' => 'Veterans Awareness', 'month' => 11, 'colors' => ['#B22234', '#FFFFFF', '#3C3B6E'], 'category' => 'Social'],
     
    8380        'organ_donation_awareness' => ['label' => 'Organ Donation Awareness', 'month' => 4, 'colors' => ['#4CAF50'], 'category' => 'Medical'],
    8481    ];
     82}
     83
     84/**
     85 * Basic hex color validator (#RGB or #RRGGBB).
     86 */
     87function car_is_hex_color($c): bool
     88{
     89    $c = trim((string)$c);
     90    if ($c === '') return false;
     91    return (bool)preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $c);
     92}
     93
     94function car_clamp_int($v, int $min, int $max): int
     95{
     96    $v = (int)$v;
     97    return max($min, min($max, $v));
     98}
     99
     100function car_normalize_category($v): string
     101{
     102    $v = trim((string)$v);
     103    if ($v === '') return '';
     104    $v = strtolower($v);
     105
     106    if ($v === 'cancers') $v = 'cancer';
     107    if ($v === 'med') $v = 'medical';
     108
     109    $map = [
     110        'cancer'  => 'Cancer',
     111        'medical' => 'Medical',
     112        'social'  => 'Social',
     113        'global'  => 'Global',
     114    ];
     115
     116    return $map[$v] ?? '';
     117}
     118
     119function car_sanitize_awareness_types($types): array
     120{
     121    if (!is_array($types)) return [];
     122
     123    $out = [];
     124    foreach ($types as $raw_key => $raw_item) {
     125        $key = sanitize_key((string)$raw_key);
     126        if ($key === '') continue;
     127        if (!is_array($raw_item)) continue;
     128
     129        $label = isset($raw_item['label']) ? trim((string)$raw_item['label']) : '';
     130        if ($label === '') continue;
     131
     132        $month = isset($raw_item['month']) ? (int)$raw_item['month'] : 0;
     133        if ($month !== 0) $month = car_clamp_int($month, 1, 12);
     134
     135        $colors_in = $raw_item['colors'] ?? [];
     136        if (!is_array($colors_in)) $colors_in = [$colors_in];
     137
     138        $colors = [];
     139        foreach ($colors_in as $c) {
     140            $c = trim((string)$c);
     141            if ($c === '') continue;
     142            if (!car_is_hex_color($c)) continue;
     143            $colors[] = $c;
     144        }
     145        if (!$colors) $colors = ['#999999'];
     146
     147        $category = '';
     148        if (isset($raw_item['category'])) {
     149            $category = car_normalize_category($raw_item['category']);
     150        }
     151
     152        $item = [
     153            'label'  => $label,
     154            'month'  => $month,
     155            'colors' => $colors,
     156        ];
     157        if ($category !== '') $item['category'] = $category;
     158
     159        $out[$key] = $item;
     160    }
     161
     162    return $out;
     163}
     164
     165/**
     166 * Awareness Types Registry (v1.5)
     167 *
     168 * Developers can register/modify awareness ribbons via:
     169 *   add_filter('car_awareness_types', function(array $types) { ...; return $types; });
     170 */
     171function car_get_awareness_types(): array
     172{
     173    $types = car_get_default_awareness_types();
     174    $types = apply_filters('car_awareness_types', $types);
     175    return car_sanitize_awareness_types($types);
     176}
     177
     178/**
     179 * Backwards compatibility wrapper (deprecated).
     180 *
     181 * @deprecated 1.5.0 Use car_get_awareness_types()
     182 */
     183function car_get_cancers(): array
     184{
     185    return car_get_awareness_types();
     186}
     187
     188function car_item_category(array $item): string
     189{
     190    $cat = isset($item['category']) ? car_normalize_category($item['category']) : '';
     191    return $cat !== '' ? $cat : 'Cancer';
     192}
     193
     194function car_group_items_by_category(array $items): array
     195{
     196    $order = ['Cancer', 'Medical', 'Social', 'Global'];
     197    $grouped = [
     198        'Cancer' => [],
     199        'Medical' => [],
     200        'Social' => [],
     201        'Global' => [],
     202    ];
     203
     204    foreach ($items as $key => $item) {
     205        $cat = car_item_category($item);
     206        if (!isset($grouped[$cat])) $grouped[$cat] = [];
     207        $grouped[$cat][$key] = $item;
     208    }
     209
     210    $out = [];
     211    foreach ($order as $cat) {
     212        if (!empty($grouped[$cat])) $out[$cat] = $grouped[$cat];
     213        unset($grouped[$cat]);
     214    }
     215    foreach ($grouped as $cat => $bucket) {
     216        if (!empty($bucket)) $out[$cat] = $bucket;
     217    }
     218
     219    return $out;
     220}
     221
     222/**
     223 * Helper for builder select options (flat).
     224 * Elementor SELECT does not reliably support optgroups (nested arrays).
     225 */
     226function car_get_awareness_types_select_options_flat(): array
     227{
     228    $types = car_get_awareness_types();
     229    $out = [];
     230
     231    foreach ($types as $key => $item) {
     232        $label = (string)($item['label'] ?? $key);
     233        $cat = car_item_category($item);
     234        $out[$key] = $cat . ' | ' . $label;
     235    }
     236
     237    // Optional: sort by label (keeps it tidy)
     238    asort($out, SORT_NATURAL | SORT_FLAG_CASE);
     239
     240    return $out;
    85241}
    86242
     
    103259}
    104260
    105 function car_clamp_int($v, int $min, int $max): int
    106 {
    107     $v = (int)$v;
    108     return max($min, min($max, $v));
    109 }
    110 
    111 function car_normalize_category($v): string
    112 {
    113     $v = trim((string)$v);
    114     if ($v === '') return '';
    115     $v = strtolower($v);
    116 
    117     // allow a few friendly aliases
    118     if ($v === 'cancers') $v = 'cancer';
    119     if ($v === 'med') $v = 'medical';
    120 
    121     $map = [
    122         'cancer' => 'Cancer',
    123         'medical' => 'Medical',
    124         'social' => 'Social',
    125         'global' => 'Global',
    126     ];
    127 
    128     return $map[$v] ?? '';
    129 }
    130 
    131 function car_item_category(array $item): string
    132 {
    133     // Default: anything without explicit category is Cancer
    134     $cat = isset($item['category']) ? car_normalize_category($item['category']) : '';
    135     return $cat !== '' ? $cat : 'Cancer';
    136 }
    137 
    138 function car_group_items_by_category(array $items): array
    139 {
    140     $order = ['Cancer', 'Medical', 'Social', 'Global'];
    141     $grouped = [
    142         'Cancer' => [],
    143         'Medical' => [],
    144         'Social' => [],
    145         'Global' => [],
    146     ];
    147 
    148     foreach ($items as $key => $item) {
    149         $cat = car_item_category($item);
    150         if (!isset($grouped[$cat])) $grouped[$cat] = [];
    151         $grouped[$cat][$key] = $item;
    152     }
    153 
    154     // return ordered + any extra categories (future-proof)
    155     $out = [];
    156     foreach ($order as $cat) {
    157         if (!empty($grouped[$cat])) $out[$cat] = $grouped[$cat];
    158         unset($grouped[$cat]);
    159     }
    160     foreach ($grouped as $cat => $bucket) {
    161         if (!empty($bucket)) $out[$cat] = $bucket;
    162     }
    163 
    164     return $out;
    165 }
    166 
    167261function car_render_ribbon_svg(array $colors, int $size, string $label): string
    168262{
     
    193287c-33.208-21.358-88.518-46.819-163.426-46.819s-130.217,25.46-163.426,46.819c-25.382,16.325-41.878,32.54-49.703,41.068
    194288c-16.459-29.648-25.327-63.358-25.327-97.408C861.545,436.251,868.418,406.194,881.421,379.246z M826.783,1759.396
    195 l-157.379-336.675l255.586-332.444l168.38,259.026L826.783,1759.396z M1373.217,1759.396l-268.557-413.123l-0.042-0.064
     289l-157.379-336.675l255.586-332.444l168.38,259.026L826.783,1759.396z M1373.217,1759.396
     290l-268.557-413.123l-0.042-0.064
    196291c-0.007-0.01-0.013-0.02-0.02-0.03L770.167,831.709c-18.718-28.784-29.762-62.112-31.94-96.38s4.559-68.728,19.482-99.653
    197292l94.354-195.578c-1.086,8.654-1.637,17.37-1.637,26.073c0,38.495,10.741,76.575,30.562,109.42c0.135,0.271,0.29,0.533,0.47,0.782
     
    213308}
    214309
     310/**
     311 * Shared renderer so shortcode + builders can call the same logic.
     312 */
     313function car_render_awareness_ribbon(array $atts = []): string
     314{
     315    return car_shortcode_cancer_ribbon($atts);
     316}
     317
    215318function car_shortcode_cancer_ribbon($atts): string
    216319{
     
    221324        'label'    => '0',
    222325        'class'    => '',
    223         'category' => '',   // v1.4: filter by category (Cancer, Medical, Social, Global)
    224         'list'     => '0',  // v1.4: list all ribbons for a selected month
     326        'category' => '',
     327        'list'     => '0',
    225328    ], $atts, 'cancer_ribbon');
    226329
    227330    wp_enqueue_style('car-ribbon-style');
    228331
    229     $cancers = car_get_cancers();
     332    $types = car_get_awareness_types();
    230333
    231334    $size = car_clamp_int($atts['size'], 16, 512);
     
    244347    $extra_class = sanitize_html_class((string)$atts['class']);
    245348
    246     // v1.4: list all awareness ribbons for the selected month (optionally filtered by category)
    247349    if ($list_mode) {
    248350        $matches = [];
    249351
    250         foreach ($cancers as $key => $item) {
     352        foreach ($types as $key => $item) {
    251353            if ((int)($item['month'] ?? 0) !== (int)$month) continue;
    252354
     
    262364        }
    263365
    264         // Group by category (Cancer, Medical, Social, Global)
    265366        $grouped = car_group_items_by_category($matches);
    266367
     
    293394    }
    294395
    295     // Single ribbon mode (existing behavior) + optional category filter when type is not forced
    296396    $type = sanitize_key((string)$atts['type']);
    297397    $selected = null;
    298398
    299     // Type override always wins
    300     if ($type && isset($cancers[$type])) {
    301         $selected = $cancers[$type];
     399    if ($type && isset($types[$type])) {
     400        $selected = $types[$type];
    302401    } else {
    303         // If a category is provided and it's not Cancer, skip featured_by_month and pick first matching in that category for the month
    304402        if ($category !== '' && $category !== 'Cancer') {
    305             foreach ($cancers as $item) {
     403            foreach ($types as $item) {
    306404                if ((int)($item['month'] ?? 0) === (int)$month && strcasecmp(car_item_category($item), $category) === 0) {
    307405                    $selected = $item;
     
    310408            }
    311409        } else {
    312             // Original featured-by-month logic
    313410            $featured = car_featured_by_month();
    314             if (isset($featured[$month]) && isset($cancers[$featured[$month]])) {
    315                 $candidate = $cancers[$featured[$month]];
    316                 // If category is set (Cancer) ensure it matches
     411            if (isset($featured[$month]) && isset($types[$featured[$month]])) {
     412                $candidate = $types[$featured[$month]];
    317413                if ($category === '' || strcasecmp(car_item_category($candidate), $category) === 0) {
    318414                    $selected = $candidate;
     
    321417        }
    322418
    323         // Fallback: first item matching month (+ optional category)
    324419        if (!$selected) {
    325             foreach ($cancers as $item) {
     420            foreach ($types as $item) {
    326421                if ((int)($item['month'] ?? 0) !== (int)$month) continue;
    327422                if ($category !== '' && strcasecmp(car_item_category($item), $category) !== 0) continue;
     
    338433
    339434    $wrap_class = 'car-ribbon-wrap' . ($extra_class ? ' ' . $extra_class : '');
    340 
    341435    $svg = car_render_ribbon_svg((array)$selected['colors'], $size, (string)$selected['label']);
    342436
     
    355449    add_shortcode('cancer_ribbon', 'car_shortcode_cancer_ribbon');
    356450});
     451
     452/**
     453 * Integrations loader
     454 */
     455function car_load_integrations(): void
     456{
     457    // Admin page
     458    if (is_admin()) {
     459        $admin_loader = __DIR__ . '/includes/admin/admin.php';
     460        if (file_exists($admin_loader)) {
     461            require_once $admin_loader;
     462        }
     463    }
     464
     465    // Elementor integration
     466    $elementor_loader = __DIR__ . '/includes/integrations/elementor.php';
     467    if (file_exists($elementor_loader)) {
     468        require_once $elementor_loader;
     469    }
     470
     471    // WPBakery / Visual Composer integration
     472    $wpbakery_loader = __DIR__ . '/includes/integrations/wpbakery.php';
     473    if (file_exists($wpbakery_loader)) {
     474        require_once $wpbakery_loader;
     475    }
     476
     477    // Divi integration
     478    $divi_loader = __DIR__ . '/includes/integrations/divi.php';
     479    if (file_exists($divi_loader)) {
     480        require_once $divi_loader;
     481    }
     482
     483    // Beaver Builder integration
     484    $beaver_loader = __DIR__ . '/includes/integrations/beaver.php';
     485    if (file_exists($beaver_loader)) {
     486        require_once $beaver_loader;
     487    }
     488}
     489add_action('plugins_loaded', 'car_load_integrations', 30);
  • cancer-awareness-ribbon-shortcode/trunk/readme.txt

    r3458889 r3458969  
    11=== Cancer Awareness Ribbon Shortcode ===
    22Contributors: freelancedirectza
    3 Tags: cancer, awareness, shortcode, accessibility, charity
     3Tags: cancer, nonprofit, ribbon, shortcode, accessibility
    44Requires at least: 5.8
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 1.4.0
    8 Version: 1.4.0
     7Stable tag: 1.5.0
     8Version: 1.5.0
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1616**Cancer Awareness Ribbon Shortcode** is a lightweight WordPress plugin that adds a shortcode for displaying a professionally designed awareness ribbon anywhere on your website.
    1717
    18 [Live Demo](https://www.parallelmedia.co.za/cancer-awareness-ribbon-demo)
     18[Live Demo](https://www.parallelmedia.co.za/cancer-awareness-ribbon-demo/)
    1919
    2020The ribbon automatically adapts to the current awareness month and supports a wide range of awareness types including Cancer, Medical, Social, and Global causes.
     
    2424- Supports single-color and multi-color (gradient) ribbons
    2525- Uses a clean, scalable SVG with no background
    26 - Works seamlessly with Elementor, Gutenberg, headers, footers, and widgets
     26- Works seamlessly with Elementor, Divi, WPBakery, Beaver Builder, Gutenberg, headers, footers, and widgets
     27- Includes a built-in admin shortcode builder with live preview
    2728- Scales crisply at any size
    2829- Does not rely on external libraries or image files
     
    3738- Site-wide awareness displays
    3839
    39 Version 1.4.0 introduces:
    40 - Category grouping (Cancer, Medical, Social, Global)
    41 - Category filtering via shortcode
    42 - List mode to display all awareness ribbons for a selected month
    43 - Structured support for expanding awareness types
     40Version 1.5.0 introduces:
     41- Admin settings page with live shortcode preview
     42- Default ribbon settings (size, type, month, category, label, list mode)
     43- Internal data structure refactor from cancer-only to awareness types
     44- Developer filter hook for registering custom awareness ribbons
     45- Improved builder integrations (Elementor, Divi, WPBakery, Beaver Builder)
     46- Foundation for future Pro features
    4447
    4548== Features ==
    4649
    4750* Automatic ribbon selection based on the current month
    48 * Supports many awareness types (Cancer, Medical, Social, Global)
     51* Supports Cancer, Medical, Social, and Global awareness types
    4952* Multi-color gradient support for applicable ribbons
    5053* Fully responsive SVG (no images required)
    51 * Elementor Shortcode widget compatible
     54* Admin shortcode builder with live preview
     55* Save default ribbon display settings
     56* Compatible with major page builders
    5257* Lightweight and fast (no external libraries)
    5358* Accessible SVG with ARIA labels
    5459* Customizable size, label, type, month, category filtering, and month listing via shortcode attributes
     60* Developer hook to extend awareness ribbon library
    5561
    5662== Usage ==
     
    6672[cancer_ribbon type="breast_cancer"]
    6773[cancer_ribbon month="10"]
    68 
    69 New in 1.4.0:
    7074
    7175[cancer_ribbon category="Medical"]
     
    8084* `month` – Force a specific month (1–12)
    8185* `category` – Filter by category: Cancer, Medical, Social, Global (optional)
    82 * `list` – If set to 1/true, lists all ribbons for the selected month (optional, works great with `category`)
     86* `list` – If set to 1/true, lists all ribbons for the selected month (optional)
     87
     88== Admin Shortcode Builder ==
     89
     90Version 1.5.0 adds a top-level **Awareness Ribbon** admin page where you can:
     91
     92- Select ribbon type
     93- Choose month or auto-detect
     94- Adjust ribbon size
     95- Enable label display
     96- Filter by category
     97- Enable list mode
     98- Add custom CSS class
     99- View live preview instantly
     100- Copy generated shortcode
     101- Save global defaults
     102
     103== Developer Extensibility ==
     104
     105Developers can extend the ribbon library using a filter hook:
     106
     107
     108
     109    add_filter( 'car_awareness_types', 'my_custom_awareness_ribbon' );
     110    function my_custom_awareness_ribbon( $types ) {
     111        $types['my_custom_cause'] = array(
     112            'label'    => 'My Custom Cause',
     113            'month'    => 6,
     114            'colors'   => array( '#123456', '#abcdef' ),
     115            'category' => 'Global',
     116        );
     117        return $types;
     118    }
     119
     120
     121This allows plugins and themes to register custom awareness ribbons without modifying core plugin files.
    83122
    84123== Supported Awareness Types ==
     
    97136- Thyroid Cancer (September) – Purple, Teal & Pink
    98137
    99 Additional awareness ribbons added in later versions include (among others):
     138Additional awareness ribbons include (among others):
     139
    100140- HIV / AIDS Awareness
    101141- Autism Awareness
     
    121161== Frequently Asked Questions ==
    122162
    123 = Does this work with Elementor? =
    124 Yes. Use Elementor’s **Shortcode** widget and insert `[cancer_ribbon]`.
     163= Does this work with page builders? =
     164Yes. It works with Elementor, Divi, WPBakery, Beaver Builder, Gutenberg, and any builder that supports shortcodes.
    125165
    126166= Does it add any images or external files? =
     
    1471872. Breast cancer awareness ribbon (October)
    1481883. Multi-color gradient ribbon example
    149 4. Elementor Shortcode widget usage
    150 5. Month listing view (list mode) showing multiple ribbons
     1894. Elementor module integration
     1905. Admin shortcode builder with live preview
     1916. Month listing view (list mode) showing multiple ribbons
    151192
    152193== Changelog ==
     194
     195= 1.5.0 =
     196* Added admin shortcode builder with live preview
     197* Added global default ribbon settings
     198* Refactored internal data structure from cancer-only to awareness types
     199* Added developer filter hook for registering custom awareness ribbons
     200* Added full builder integrations (Elementor, Divi, WPBakery, Beaver Builder)
     201* Improved internal structure for future Pro version expansion
    153202
    154203= 1.4.0 =
     
    157206* Added `list` shortcode attribute to display all ribbons for a selected month
    158207* Added grouped output by category when using list mode
    159 * Added categories to non-cancer awareness entries while keeping existing data structure intact
    160208
    161209= 1.3.0 =
    162 * Added Pride awareness ribbon (multi-color gradient)
     210* Added Pride awareness ribbon
    163211* Added Veterans awareness ribbon
    164212* Added Child Protection awareness ribbon
     
    180228* Added Heart Disease awareness ribbon
    181229
    182 = 1.0.2 =
    183 * Fixed stylesheet loading to use proper WordPress enqueue functions
    184 * Updated contributor metadata for WordPress.org compliance
    185 * Minor internal cleanup and review-related improvements
    186 
    187 = 1.0.1 =
    188 * Added support for custom SVG ribbon
    189 * Improved Elementor compatibility
    190 * Fixed inline CSS loading issues
    191 * Removed background elements from SVG
    192 
    193230= 1.0.0 =
    194231* Initial release
     
    196233== Upgrade Notice ==
    197234
    198 = 1.4.0 =
    199 Adds category support, category filtering, and list mode to display all ribbons for a selected month.
    200 
    201 = 1.0.2 =
    202 Fixed stylesheet loading, updated contributor metadata, and minor internal cleanup.
     235= 1.5.0 =
     236Adds admin shortcode builder, default settings support, internal awareness refactor, developer extensibility hook, and builder integrations.
    203237
    204238== License ==
Note: See TracChangeset for help on using the changeset viewer.