[ name, description, premium, category, tags, url, icon, color ] ] */ public static function catalog() { return (array) apply_filters( 'remaily_addons_catalog', [ 'calculator' => [ 'name' => __( 'Calculator', 'remaily' ), 'description' => __( 'Work out prices and totals in the form as visitors fill it in, with formulas, option prices and dates. Show or hide parts of the form on a total, and take the payment from it.', 'remaily' ), 'premium' => true, 'category' => __( 'Fields', 'remaily' ), 'tags' => [ __( 'Formulas', 'remaily' ), __( 'Prices', 'remaily' ), __( 'Payments', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-calculator-addon', 'icon' => Icons::icon( 'calculator' ), 'color' => '#fff1e5', ], 'bookings' => [ 'name' => __( 'Bookings & Time Slots', 'remaily' ), 'description' => __( 'Let people book a time right in your form, straight into your Google Calendar. Only free times show, each booking becomes an event, and nothing is booked twice. For salons, barbers, car services, therapists and consultants.', 'remaily' ), 'premium' => true, 'category' => __( 'Fields', 'remaily' ), 'tags' => [ __( 'Appointments', 'remaily' ), __( 'Time slots', 'remaily' ), __( 'Google Calendar', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-bookings-addon', 'icon' => Icons::icon( 'bookings' ), 'color' => '#e8f7ff', ], 'google-sheets' => [ 'name' => 'Google Sheets', 'description' => __( 'Every message lands in your Google Sheets spreadsheet as a row, a column per field. Send the entries you already have with one click.', 'remaily' ), 'premium' => true, 'category' => __( 'Spreadsheets', 'remaily' ), 'tags' => [ __( 'Spreadsheets', 'remaily' ), __( 'Reports', 'remaily' ), __( 'Sync', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-google-sheets-addon', 'icon' => Icons::icon( 'google-sheets' ), 'color' => '#e5f7ec', ], 'analytics' => [ 'name' => __( 'Analytics', 'remaily' ), 'description' => __( 'Count each message and payment in Google Analytics 4, Tag Manager, Yandex Metrica and the Meta Pixel, with its value. See how many start a form, and the field where the rest give up.', 'remaily' ), 'premium' => true, 'category' => __( 'Analytics', 'remaily' ), 'tags' => [ __( 'Conversions', 'remaily' ), __( 'Goals', 'remaily' ), __( 'Ads', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-analytics-addon', 'icon' => Icons::icon( 'analytics' ), 'color' => '#eaf0ff', ], 'telegram' => [ 'name' => 'Telegram', 'description' => __( 'Get each message in Telegram the moment it\'s sent: in your own chat, your team\'s group or a channel. No more checking your mail every five minutes.', 'remaily' ), 'premium' => true, 'category' => __( 'Messengers', 'remaily' ), 'tags' => [ __( 'Notifications', 'remaily' ), __( 'Bots', 'remaily' ), __( 'Groups', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-telegram-addon', 'icon' => Icons::icon( 'telegram' ), 'color' => '#e6f4fc', ], 'helpscout' => [ 'name' => 'Help Scout', 'description' => __( 'Each message becomes a Help Scout conversation, from the sender as the customer, with their files and a note for your team, such as what the store said of their purchase code.', 'remaily' ), 'premium' => true, 'category' => __( 'Support', 'remaily' ), 'tags' => [ __( 'Support', 'remaily' ), __( 'Tickets', 'remaily' ), __( 'Inbox', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-helpscout-addon', 'icon' => Icons::icon( 'helpscout' ), 'color' => '#e7f3fd', ], 'coupons' => [ 'name' => __( 'Coupons', 'remaily' ), 'description' => __( 'Promo codes in your forms: 10% or 5 off the payment or a Calculator total. Codes stay on your server, with an end date and a limit of uses, and each use is counted.', 'remaily' ), 'premium' => true, 'category' => __( 'Fields', 'remaily' ), 'tags' => [ __( 'Promo codes', 'remaily' ), __( 'Discounts', 'remaily' ), __( 'Payments', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-coupons-addon', 'icon' => Icons::icon( 'coupons' ), 'color' => '#ffe8ef', ], 'mailchimp' => [ 'name' => 'Mailchimp', 'description' => __( 'Add the people who write to you to your Mailchimp audience, with their details as merge fields, your tags and Mailchimp\'s double opt-in. Only those who tick your consent box, if you want.', 'remaily' ), 'premium' => true, 'category' => __( 'Email marketing', 'remaily' ), 'tags' => [ __( 'Newsletter', 'remaily' ), __( 'Audiences', 'remaily' ), __( 'Double opt-in', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-mailchimp-addon', 'icon' => Icons::icon( 'mailchimp' ), 'color' => '#fff8d6', ], 'brevo' => [ 'name' => 'Brevo', 'description' => __( 'Add the people who write to you to your Brevo lists, with their details mapped to Brevo attributes, a welcome email and double opt-in.', 'remaily' ), 'premium' => true, 'category' => __( 'Email marketing', 'remaily' ), 'tags' => [ __( 'Mailing lists', 'remaily' ), __( 'Double opt-in', 'remaily' ), __( 'Attributes', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-brevo-addon', 'icon' => Icons::icon( 'brevo' ), 'color' => '#e7f6ef', ], 'mailerlite' => [ 'name' => 'MailerLite', 'description' => __( 'Add the people who write to you to your MailerLite groups, with their details as fields and MailerLite\'s double opt-in. Only those who tick your consent box, if you want.', 'remaily' ), 'premium' => true, 'category' => __( 'Email marketing', 'remaily' ), 'tags' => [ __( 'Newsletter', 'remaily' ), __( 'Groups', 'remaily' ), __( 'Double opt-in', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-mailerlite-addon', 'icon' => Icons::icon( 'mailerlite' ), 'color' => '#e6f7ef', ], 'kit' => [ 'name' => 'Kit (ConvertKit)', 'description' => __( 'Add the people who write to you to Kit, formerly ConvertKit: join a Kit form to start its sequences, with tags, custom fields and the form\'s double opt-in.', 'remaily' ), 'premium' => true, 'category' => __( 'Email marketing', 'remaily' ), 'tags' => [ __( 'Newsletter', 'remaily' ), __( 'Sequences', 'remaily' ), __( 'Tags', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-kit-addon', 'icon' => Icons::icon( 'convertkit' ), 'color' => '#fdeeee', ], 'whatsapp' => [ 'name' => 'WhatsApp', 'description' => __( 'Answer the people who write to you on WhatsApp the moment they send a form, and get each message on your own WhatsApp.', 'remaily' ), 'premium' => true, 'category' => __( 'Messengers', 'remaily' ), 'tags' => [ __( 'Notifications', 'remaily' ), __( 'Auto-reply', 'remaily' ), __( 'Green-API', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-whatsapp-addon', 'icon' => Icons::icon( 'whatsapp' ), 'color' => '#e6f7e9', ], 'razorpay' => [ 'name' => 'Razorpay', 'description' => __( 'Take payments in India with the Payment field: cards, UPI, netbanking and wallets in Razorpay Checkout. Each payment is checked and captured on your server before the message goes.', 'remaily' ), 'premium' => true, 'category' => __( 'Payments', 'remaily' ), 'tags' => [ __( 'UPI', 'remaily' ), __( 'Cards', 'remaily' ), __( 'India', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-razorpay-addon', 'icon' => Icons::icon( 'razorpay' ), 'color' => '#e8f2ff', ], 'square' => [ 'name' => 'Square', 'description' => __( 'Take card payments with Square in your forms: a card field right in the form, 3-D Secure when the bank asks, and each payment checked before the message goes.', 'remaily' ), 'premium' => true, 'category' => __( 'Payments', 'remaily' ), 'tags' => [ __( 'Cards', 'remaily' ), '3-D Secure', __( 'USA, UK, Europe', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-square-addon', 'icon' => Icons::icon( 'square' ), 'color' => '#f1f1f1', ], 'authorizenet' => [ 'name' => 'Authorize.Net', 'description' => __( 'Take card payments with Authorize.Net in your forms, in its secure payment form over the page. Each transaction is checked before the message goes.', 'remaily' ), 'premium' => true, 'category' => __( 'Payments', 'remaily' ), 'tags' => [ __( 'Cards', 'remaily' ), 'Accept Hosted', __( 'USA', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-authorizenet-addon', 'icon' => Icons::icon( 'authorizenet' ), 'color' => '#e8f1fb', ], 'mercadopago' => [ 'name' => 'Mercado Pago', 'description' => __( 'Take payments with Mercado Pago in your forms: cards, installments, Pix, boleto and Mercado Pago accounts, in Latin America\'s favourite checkout.', 'remaily' ), 'premium' => true, 'category' => __( 'Payments', 'remaily' ), 'tags' => [ 'Pix', 'Boleto', __( 'Latin America', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-mercadopago-addon', 'icon' => Icons::icon( 'mercadopago' ), 'color' => '#e6f4fd', ], 'yookassa' => [ 'name' => __( 'YooKassa', 'remaily' ), 'description' => __( 'Take payments with YooKassa (ЮKassa) in your forms: cards, SBP, SberPay and YooMoney in a widget inside the form, with receipts under 54-FZ.', 'remaily' ), 'premium' => true, 'category' => __( 'Payments', 'remaily' ), 'tags' => [ __( 'SBP', 'remaily' ), __( 'Cards', 'remaily' ), __( 'Receipts', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-yookassa-addon', 'icon' => Icons::icon( 'yookassa' ), 'color' => '#eef0ff', ], 'robokassa' => [ 'name' => __( 'Robokassa', 'remaily' ), 'description' => __( 'Take payments with Robokassa (Робокасса) in your forms: cards, SBP and e-wallets in its own window, with receipts under 54-FZ.', 'remaily' ), 'premium' => true, 'category' => __( 'Payments', 'remaily' ), 'tags' => [ __( 'SBP', 'remaily' ), __( 'Cards', 'remaily' ), __( 'Receipts', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-robokassa-addon', 'icon' => Icons::icon( 'robokassa' ), 'color' => '#eef7ee', ], 'rating' => [ 'name' => __( 'Star rating', 'remaily' ), 'description' => __( 'Ask people to rate you, a product or a visit with stars they click. From one to ten stars, the built-in star or your own SVG icon, and the mail shows ★★★★☆ 4/5.', 'remaily' ), 'premium' => false, 'category' => __( 'Fields', 'remaily' ), 'tags' => [ __( 'Reviews', 'remaily' ), __( 'Feedback', 'remaily' ), __( 'Stars', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-rating-addon', 'icon' => Icons::icon( 'rating' ), 'color' => '#eef0ff', ], 'suggested-answers' => [ 'name' => __( 'Suggested answers', 'remaily' ), 'description' => __( 'While visitors type their question, articles from your site or your docs that may answer it show under the form. Fewer questions reach you, and the mail says which articles didn\'t help.', 'remaily' ), 'premium' => true, 'category' => __( 'Support', 'remaily' ), 'tags' => [ __( 'Docs', 'remaily' ), __( 'FAQ', 'remaily' ), __( 'Fewer tickets', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-suggested-answers-addon', 'icon' => Icons::icon( 'suggested-answers' ), 'color' => '#eaf0ff', ], 'themeforest' => [ 'name' => 'ThemeForest', 'description' => __( 'Check ThemeForest and CodeCanyon purchase codes in your forms. Only buyers get through to support, and each message shows the item, the buyer and how long their support lasts.', 'remaily' ), 'premium' => true, 'category' => __( 'Purchase codes', 'remaily' ), 'tags' => [ __( 'Envato', 'remaily' ), __( 'Support', 'remaily' ), __( 'Licenses', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-themeforest-addon', 'icon' => Icons::icon( 'envato' ), 'color' => '#eef7e6', ], 'gumroad' => [ 'name' => 'Gumroad', 'description' => __( 'Check Gumroad license keys in your forms. Only buyers get through to support, and each message shows the item, the buyer and how long their support lasts.', 'remaily' ), 'premium' => true, 'category' => __( 'Purchase codes', 'remaily' ), 'tags' => [ __( 'License keys', 'remaily' ), __( 'Support', 'remaily' ), __( 'Memberships', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-gumroad-addon', 'icon' => Icons::icon( 'gumroad' ), 'color' => '#ffeaf5', ], 'lemonsqueezy' => [ 'name' => 'Lemon Squeezy', 'description' => __( 'Check Lemon Squeezy license keys in your forms. Only buyers get through to support, and each message shows the product, the buyer and when the license expires.', 'remaily' ), 'premium' => true, 'category' => __( 'Purchase codes', 'remaily' ), 'tags' => [ __( 'License keys', 'remaily' ), __( 'Support', 'remaily' ), __( 'Subscriptions', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-lemonsqueezy-addon', 'icon' => Icons::icon( 'lemon' ), 'color' => '#f0ebff', ], 'edd' => [ 'name' => 'Easy Digital Downloads', 'description' => __( 'Check Easy Digital Downloads license keys in your forms, on your store\'s own site or another one. Only buyers get through to support, and each message shows the product, the buyer and when the license expires.', 'remaily' ), 'premium' => true, 'category' => __( 'Purchase codes', 'remaily' ), 'tags' => [ __( 'Licenses', 'remaily' ), __( 'Support', 'remaily' ), 'Software Licensing' ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-edd-addon', 'icon' => Icons::icon( 'edd' ), 'color' => '#eef1f6', ], 'freemius' => [ 'name' => 'Freemius', 'description' => __( 'Check Freemius license keys in your forms. Only buyers get through to support, and each message shows the plan, the buyer and when the license expires.', 'remaily' ), 'premium' => true, 'category' => __( 'Purchase codes', 'remaily' ), 'tags' => [ __( 'License keys', 'remaily' ), __( 'Support', 'remaily' ), __( 'WordPress', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-freemius-addon', 'icon' => Icons::icon( 'freemius' ), 'color' => '#e8f1ff', ], 'paddle' => [ 'name' => 'Paddle', 'description' => __( 'Check Paddle orders in your forms by their transaction ID or invoice number. Only buyers get through to support, and each message shows the product, the buyer and how long support lasts.', 'remaily' ), 'premium' => true, 'category' => __( 'Purchase codes', 'remaily' ), 'tags' => [ __( 'Orders', 'remaily' ), __( 'Support', 'remaily' ), __( 'Subscriptions', 'remaily' ) ], 'url' => 'https://vlthemes.gumroad.com/l/remaily-paddle-addon', 'icon' => Icons::icon( 'paddle' ), 'color' => '#deddd9', ], ] ); } /** * Add-ons in the plugin's menu, after Settings */ public static function menu() { self::$hook = (string) add_submenu_page( Admin::LIST_PAGE, __( 'Add-ons', 'remaily' ), __( 'Add-ons', 'remaily' ), 'manage_options', self::PAGE, [ __CLASS__, 'render' ] ); } /** * Add-ons stands out in the menu on every screen: a few lines inline with WordPress's menu styles, no request */ public static function menu_style() { // The menu's own id: the color schemes' submenu rules come later and are as specific otherwise $link = '#adminmenu #toplevel_page_edit-post_type-' . PostType::NAME . ' .wp-submenu a[href$="page=' . self::PAGE . '"]'; wp_add_inline_style( 'admin-menu', $link . '{color:#f0b849;font-weight:600}' . $link . ':hover,' . $link . '[aria-current="page"]{color:#fff}#adminmenu .wp-submenu li:has(>a[href$="page=' . self::PAGE . '"]){margin-top:6px;padding-top:6px;border-top:1px solid rgba(255,255,255,.12)}' ); } /** * Whether this is the Add-ons screen * * @return bool */ public static function is_screen() { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; return $screen && self::$hook && self::$hook === $screen->id; } /** * The page's script and styles * * @param string $hook Admin page hook */ public static function enqueue( $hook ) { if ( self::$hook && self::$hook === $hook ) { Assets::enqueue( 'remaily-admin-settings', 'remaily-admin-addons' ); } } /** * An add-on's logo on its background colour ('color' of remaily_addons) * * @param array $card Card */ private static function the_icon( array $card ) { $color = sanitize_hex_color( (string) $card['color'] ); ?> [ label, glyph, count ] ] */ private static function categories( array $cards ) { $glyphs = [ __( 'Fields', 'remaily' ) => 'type', __( 'Spreadsheets', 'remaily' ) => 'layout-grid', __( 'Analytics', 'remaily' ) => 'gauge', __( 'Messengers', 'remaily' ) => 'message-square', __( 'Support', 'remaily' ) => 'inbox', __( 'Email marketing', 'remaily' ) => 'mail', __( 'Payments', 'remaily' ) => 'credit-card', __( 'Purchase codes', 'remaily' ) => 'lock', ]; $order = array_flip( array_keys( self::catalog() ) ); $sorted = $cards; $found = []; // Catalog order, cards of others' add-ons after uksort( $sorted, static fn( $a, $b ) => ( $order[ $a ] ?? PHP_INT_MAX ) <=> ( $order[ $b ] ?? PHP_INT_MAX ) ); foreach ( $sorted as $card ) { $label = (string) $card['category']; if ( '' === $label ) { continue; } $key = self::category_key( $label ); $found[ $key ] = $found[ $key ] ?? [ $label, $glyphs[ $label ] ?? 'blocks', 0 ]; $found[ $key ][2]++; } return $found; } /** * A category's key, for the filter * * @param string $label Category as people read it * * @return string */ private static function category_key( $label ) { return sanitize_title( $label ) ?: md5( $label ); } /** * Every card: installed add-ons, then the rest of the catalog * * @return array [ slug => [ name, description, premium, category, tags, url, icon, installed, … ] ] */ private static function cards() { $catalog = self::catalog(); $cards = []; // An installed add-on's card: its catalog entry, with what the add-on says of itself on top (an add-on that // isn't in the catalog brings its own description, icon and so on) foreach ( Addons::all() as $slug => $addon ) { $item = (array) ( $catalog[ $slug ] ?? [] ); $pick = static fn( $key, $default = '' ) => $addon[ $key ] ?: ( $item[ $key ] ?? $default ); $cards[ $slug ] = [ 'name' => $pick( 'name', $slug ), 'description' => $pick( 'description' ), 'premium' => $addon['premium'], 'category' => $pick( 'category' ), 'tags' => $pick( 'tags', [] ), 'url' => $pick( 'url' ), 'icon' => $pick( 'icon' ), 'color' => $pick( 'color' ), 'docs' => $pick( 'docs' ), 'version' => $addon['version'], 'installed' => true, ]; } // Installed ones in the catalog's order too (the most wanted first), any other add-on after them $order = array_flip( array_keys( $catalog ) ); uksort( $cards, static fn( $a, $b ) => ( $order[ $a ] ?? PHP_INT_MAX ) <=> ( $order[ $b ] ?? PHP_INT_MAX ) ); foreach ( $catalog as $slug => $item ) { if ( !isset( $cards[ $slug ] ) ) { $cards[ $slug ] = wp_parse_args( (array) $item, [ 'name' => $slug, 'description' => '', 'premium' => false, 'category' => '', 'tags' => [], 'url' => '', 'icon' => '', 'color' => '', 'docs' => '', 'version' => '' ] ) + [ 'installed' => false ]; } } return $cards; } /** * A card's state: [ label, tone ] * * @param string $slug Slug * @param array $card Card * * @return array */ private static function state( $slug, array $card ) { if ( !$card['installed'] ) { return [ __( 'Available', 'remaily' ), 'brand' ]; } $addon = Addons::all()[ $slug ]; if ( !Addons::enabled( $slug ) ) { return [ __( 'Off', 'remaily' ), 'muted' ]; } // Its own word first, such as "Not activated" if ( 2 === count( $addon['status'] ) ) { return $addon['status']; } return $addon['ready'] ? [ __( 'On', 'remaily' ), 'success' ] : [ __( 'Needs setting up', 'remaily' ), 'warning' ]; } /** * The Add-ons page: as the Settings page, the filters down the side and the search on top; the cards, and a window * per installed add-on (admin-addons.js) */ public static function render() { $cards = self::cards(); $installed = array_filter( $cards, static fn( $card ) => $card['installed'] ); $available = array_diff_key( $cards, $installed ); $notice = get_transient( 'remaily_addon_notice_' . get_current_user_id() ); if ( $notice ) { delete_transient( 'remaily_addon_notice_' . get_current_user_id() ); } $categories = self::categories( $cards ); $filters = [ 'all' => [ __( 'All', 'remaily' ), $cards, 'blocks' ], 'installed' => [ __( 'Installed', 'remaily' ), $installed, 'circle-check' ], 'available' => [ __( 'Available', 'remaily' ), $available, 'download' ] ]; ?>