count_pending_workflows(); $badge = ''; if ($pending_count > 0) { $badge = sprintf( ' %2$s', (int) $pending_count, esc_html(sprintf( /* translators: %d: number of orders awaiting approval */ _n('%d order awaiting approval', '%d orders awaiting approval', $pending_count, 'crewcommerce'), $pending_count )) ); } add_menu_page(__('CrewCommerce', 'crewcommerce'), __('CrewCommerce', 'crewcommerce') . $badge, 'manage_options', 'crewcommerce', array($this, 'dashboard_page'), 'dashicons-groups', 56); add_submenu_page('crewcommerce', __('Dashboard', 'crewcommerce'), __('Dashboard', 'crewcommerce'), 'manage_options', 'crewcommerce', array($this, 'dashboard_page')); add_submenu_page('crewcommerce', __('Companies', 'crewcommerce'), __('Companies', 'crewcommerce'), 'manage_options', 'crewcommerce-companies', array($this, 'companies_page')); add_submenu_page('crewcommerce', __('Approvals', 'crewcommerce'), __('Approvals', 'crewcommerce') . $badge, 'manage_options', 'crewcommerce-approvals', array($this, 'approvals_page')); add_submenu_page('crewcommerce', __('Settings', 'crewcommerce'), __('Settings', 'crewcommerce'), 'manage_options', 'crewcommerce-settings', array($this, 'settings_page')); if (!self::pro_active()) { add_submenu_page('crewcommerce', __('CrewCommerce Pro', 'crewcommerce'), __('Upgrade to Pro', 'crewcommerce'), 'manage_options', 'crewcommerce-pro', array($this, 'pro_page'), 99); } } /* ------------------------------------------------------------------ * Settings * ---------------------------------------------------------------- */ public function register_settings() { register_setting('crewcommerce_settings', 'crewcommerce_settings', array( 'type' => 'array', 'sanitize_callback' => array($this, 'sanitize_settings'), )); add_settings_section('crewcommerce_approval_settings', __('Approval Rules', 'crewcommerce'), array($this, 'approval_settings_section_callback'), 'crewcommerce_settings'); add_settings_field('approval_threshold', __('Order Value Threshold', 'crewcommerce'), array($this, 'approval_threshold_callback'), 'crewcommerce_settings', 'crewcommerce_approval_settings'); add_settings_field('require_approval_roles', __('Apply Rules To', 'crewcommerce'), array($this, 'approval_roles_callback'), 'crewcommerce_settings', 'crewcommerce_approval_settings'); add_settings_section('crewcommerce_workflow_settings', __('Workflow', 'crewcommerce'), array($this, 'workflow_settings_section_callback'), 'crewcommerce_settings'); add_settings_field('approval_required_message', __('Order Confirmation Message', 'crewcommerce'), array($this, 'approval_message_callback'), 'crewcommerce_settings', 'crewcommerce_workflow_settings'); add_settings_section('crewcommerce_data_settings', __('Data', 'crewcommerce'), '__return_false', 'crewcommerce_settings'); add_settings_field('delete_data_on_uninstall', __('When CrewCommerce Is Deleted', 'crewcommerce'), array($this, 'delete_data_on_uninstall_callback'), 'crewcommerce_settings', 'crewcommerce_data_settings'); } /** * Sanitize the settings this plugin owns. Keys it doesn't know (an * add-on's settings) are kept as stored and passed to the * crewcommerce_sanitize_settings filter. */ public function sanitize_settings($input) { $input = is_array($input) ? $input : array(); $sanitized = get_option('crewcommerce_settings', array()); $sanitized = is_array($sanitized) ? $sanitized : array(); $sanitized['approval_threshold'] = isset($input['approval_threshold']) ? max(0, (float) $input['approval_threshold']) : 0; $roles = isset($input['require_approval_roles']) ? array_map('sanitize_key', (array) $input['require_approval_roles']) : array(); $sanitized['require_approval_roles'] = array_values(array_intersect($roles, array('requester', 'manager'))); $sanitized['approval_required_message'] = isset($input['approval_required_message']) ? sanitize_textarea_field($input['approval_required_message']) : ''; $sanitized['delete_data_on_uninstall'] = !empty($input['delete_data_on_uninstall']); /** * Sanitize settings added by an add-on. * * @param array $sanitized The settings to save. * @param array $input The raw submitted values. */ return apply_filters('crewcommerce_sanitize_settings', $sanitized, $input); } public function approval_settings_section_callback() { echo '
' . esc_html__('Decide which orders wait for a manager at the customer’s company to approve them.', 'crewcommerce') . '
'; } public function workflow_settings_section_callback() { echo '' . esc_html__('What people see once an order is held for approval.', 'crewcommerce') . '
'; } public function approval_threshold_callback() { $settings = get_option('crewcommerce_settings', array()); $value = isset($settings['approval_threshold']) ? (float) $settings['approval_threshold'] : 0; $symbol = html_entity_decode(get_woocommerce_currency_symbol(), ENT_QUOTES); ?> get_roles_checked(); $roles = array( 'requester' => __('Requesters — team members who place orders', 'crewcommerce'), 'manager' => __('Managers — their orders go to another Manager or the Admin', 'crewcommerce'), ); ?> get_order_statistics(); $counts = wp_count_posts('crewcommerce_company'); $company_cnt = isset($counts->publish) ? (int) $counts->publish : 0; $approvals = CrewCommerce_Approvals::get_instance(); $stale_hours = $approvals->get_stale_hours(); $items = array(); $unassigned = $approvals->get_unassigned_pending_workflows(); if (!empty($unassigned)) { $items[] = array( /* translators: %d: number of orders */ 'text' => sprintf(_n('%d order is waiting for someone to claim it — its company has no Manager or Admin.', '%d orders are waiting for someone to claim them — their companies have no Manager or Admin.', count($unassigned), 'crewcommerce'), count($unassigned)), 'link' => admin_url('admin.php?page=crewcommerce-approvals'), 'cta' => __('Claim an order', 'crewcommerce'), ); } $missing = CrewCommerce_Team_Accounts::get_instance()->get_companies_missing_approvers(); foreach ($missing as $company) { $items[] = array( /* translators: %s: company name */ 'text' => sprintf(__('%s has no Manager or Admin, so its orders that need approval will wait for you to claim them.', 'crewcommerce'), $company->post_title), 'link' => admin_url('admin.php?page=crewcommerce-companies&company=' . $company->ID), 'cta' => __('Open the company', 'crewcommerce'), ); } $stale = $approvals->get_stale_pending_workflows($stale_hours); if (!empty($stale)) { $items[] = array( /* translators: 1: number of orders, 2: number of hours */ 'text' => sprintf(_n('%1$d order has waited more than %2$d hours for approval.', '%1$d orders have waited more than %2$d hours for approval.', count($stale), 'crewcommerce'), count($stale), $stale_hours), 'link' => admin_url('admin.php?page=crewcommerce-approvals'), 'cta' => __('Review approvals', 'crewcommerce'), ); } /** * Items for the dashboard's "Needs your attention" box. Each item is * an array with text, link and cta (link text). * * @param array $items Attention items. */ $items = (array) apply_filters('crewcommerce_admin_attention_items', $items); ?> get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery "SELECT aw.*, u1.display_name AS requester_name, u2.display_name AS approver_name FROM {$wpdb->prefix}crewcommerce_approval_workflows aw LEFT JOIN {$wpdb->users} u1 ON aw.requester_id = u1.ID LEFT JOIN {$wpdb->users} u2 ON aw.approver_id = u2.ID ORDER BY aw.updated_at DESC LIMIT 10" ); if (empty($rows)) { echo '' . esc_html__('No approvals yet.', 'crewcommerce') . '
'; return; } $now = current_time('timestamp'); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- compared with updated_at, stored in site time. echo '| post_title); ?> | display_name) : '—'; ?> | get_team_members($company->ID))); ?> | post_date))); ?> |
' . esc_html__('Company not found.', 'crewcommerce') . '
| #get_order_number()); ?> #order_id); ?> | requester_name ? $workflow->requester_name : '—'); ?> | approver_name ? $workflow->approver_name : '—'); ?> | status]) ? $labels[$workflow->status] : ucfirst($workflow->status)); ?> | created_at), $now)); ?> — | updated_at))); ?> | status) : ?> — |