Skip to content
Snippets Groups Projects
civicrm.metabox.contact.add.php 15.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Kevin Cristiano's avatar
    Kevin Cristiano committed
    <?php
    /*
     +--------------------------------------------------------------------+
     | Copyright CiviCRM LLC. All rights reserved.                        |
     |                                                                    |
     | This work is published under the GNU AGPLv3 license with some      |
     | permitted exceptions and without any warranty. For full license    |
     | and copyright information, see https://civicrm.org/licensing       |
     +--------------------------------------------------------------------+
     */
    
    /**
     *
     * @package CRM
     * @copyright CiviCRM LLC https://civicrm.org/licensing
     *
     */
    
    // This file must not accessed directly.
    if (!defined('ABSPATH')) {
      exit;
    }
    
    /**
     * Define CiviCRM_For_WordPress_Admin_Metabox_Contact_Add Class.
     *
     * @since 5.34
     */
    class CiviCRM_For_WordPress_Admin_Metabox_Contact_Add {
    
      /**
       * @var object
       * Plugin object reference.
       * @since 5.34
       * @access public
       */
      public $civi;
    
      /**
       * @var object
       * Admin object reference.
       * @since 5.34
       * @access public
       */
      public $admin;
    
      /**
       * Instance constructor.
       *
       * @since 5.34
       */
      public function __construct() {
    
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        // Bail if CiviCRM is not installed.
        if (!CIVICRM_INSTALLED) {
          return;
        }
    
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        // Store reference to CiviCRM plugin object.
        $this->civi = civi_wp();
    
        // Store reference to admin object.
        $this->admin = civi_wp()->admin;
    
        // Wait for admin class to register hooks.
        add_action('civicrm/admin/hooks/registered', [$this, 'register_hooks']);
    
      }
    
      /**
       * Register hooks.
       *
       * @since 5.34
       */
      public function register_hooks() {
    
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        /*
         * Bail if the current WordPress User cannot add Contacts.
         * Usefully, this also returns FALSE if CiviCRM fails to initialize.
         */
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        if (!$this->civi->users->check_civicrm_permission('add_contacts')) {
          return;
        }
    
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        // Add our meta boxes.
        add_action('wp_dashboard_setup', [$this, 'meta_box_add']);
    
        // Add resources prior to page load.
        add_action('admin_enqueue_scripts', [$this, 'enqueue_js']);
        add_action('admin_enqueue_scripts', [$this, 'enqueue_css']);
    
        // Register our form submit hander.
        add_action('admin_init', [$this, 'form_submitted']);
    
        // Register AJAX handler.
        add_action('wp_ajax_civicrm_contact_add', [$this, 'ajax_contact_add']);
    
      }
    
      /**
       * Enqueue Javascript on the dashboard.
       *
       * @since 5.34
       *
       * @param str $hook The filename of the displayed screen.
       */
      public function enqueue_js($hook) {
    
        // Bail if not the dashboard.
    
        if ('index.php' !== $hook) {
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
          return;
        }
    
        // Enqueue Javascript.
        wp_enqueue_script(
          'civicrm-contact-add-script',
          CIVICRM_PLUGIN_URL . 'assets/js/civicrm.contact.add.js',
          ['jquery'],
    
          CIVICRM_PLUGIN_VERSION,
          FALSE
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        );
    
        // Init settings and localisation array.
        $vars = [
          'settings' => [
            'ajax_url' => admin_url('admin-ajax.php'),
          ],
          'localisation' => [
            'add' => __('Add Contact', 'civicrm'),
            'adding' => __('Adding...', 'civicrm'),
            'added' => __('Contact Added', 'civicrm'),
          ],
        ];
    
        // Localise the WordPress way.
        wp_localize_script(
          'civicrm-contact-add-script',
          'CiviCRM_Quick_Add_Vars',
          $vars
        );
    
      }
    
      /**
       * Enqueue stylesheet on the dashboard.
       *
       * @since 5.34
       *
       * @param str $hook The filename of the displayed screen.
       */
      public function enqueue_css($hook) {
    
        // Bail if not the dashboard.
    
        if ('index.php' !== $hook) {
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
          return;
        }
    
        // Enqueue common CSS.
        wp_enqueue_style(
          'civicrm-admin-styles',
          CIVICRM_PLUGIN_URL . 'assets/css/civicrm.admin.css',
          NULL,
          CIVICRM_PLUGIN_VERSION,
          'all'
        );
    
      }
    
      // ---------------------------------------------------------------------------
      // Meta Box Loader
      // ---------------------------------------------------------------------------
    
      /**
       * Register "Quick Add Contact" meta box.
       *
       * @since 5.34
       */
      public function meta_box_add() {
    
        // Bail if user cannot access CiviCRM.
        if (!current_user_can('access_civicrm')) {
          return;
        }
    
        // Init data.
        $data = [];
    
        // Create "Quick Add Contact" metabox.
        add_meta_box(
          'civicrm_metabox_contact_add',
          __('Quick Add Contact to CiviCRM', 'civicrm'),
          // Callback.
          [$this, 'meta_box_render'],
          // Screen ID.
          'dashboard',
          // Column: options are 'normal' and 'side'.
          'side',
          // Vertical placement: options are 'core', 'high', 'low'.
          'high',
          $data
        );
    
      }
    
      // ---------------------------------------------------------------------------
      // Meta Box Renderer
      // ---------------------------------------------------------------------------
    
      /**
       * Render "Quick Add Contact" meta box.
       *
       * @since 5.34
       *
       * @param mixed $unused Unused param.
       * @param array $metabox Array containing id, title, callback, and args elements.
       */
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
      public function meta_box_render($unused, $metabox) {
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
    
        if (!$this->civi->initialize()) {
          return;
        }
    
        // Check our session for data.
        $session = CRM_Core_Session::singleton();
        $recents = $session->get('quick_add_recents');
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        if (!empty($recents) && is_array($recents)) {
          foreach ($recents as $key => $value) {
            $recents[$key] = CRM_Utils_String::purifyHtml($value);
          }
        }
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        // Maybe add a class to the "Recently Added" wrapper.
        $visiblity_class = '';
        if (!empty($recents)) {
          $visiblity_class = ' contacts-added';
        }
    
        // Detect error message.
        $error = '';
        $error_css = ' display: none;';
    
        // Nonce not needed since $_GET['quick-add-error'] must match certain pre-defined slugs.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $quick_add_error = isset($_GET['quick-add-error']) ? sanitize_text_field(wp_unslash($_GET['quick-add-error'])) : '';
        if (!empty($quick_add_error)) {
          switch ($quick_add_error) {
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
    
            case 'civicrm':
              $error = __('Failed to init CiviCRM.', 'civicrm');
              break;
    
            case 'first-name':
              $error = __('Please enter a first name.', 'civicrm');
              break;
    
            case 'last-name':
              $error = __('Please enter a last name.', 'civicrm');
              break;
    
            case 'email':
              $error = __('Please enter a valid email.', 'civicrm');
              break;
    
            case 'api':
              $error = __('Could not create Contact.', 'civicrm');
              break;
    
            case 'missing':
              $error = __('Could not find the created Contact.', 'civicrm');
              break;
    
          }
    
          $error_css = '';
        }
    
        // Set submit button options.
        $options = [
          'data-security' => esc_attr(wp_create_nonce('civicrm_metabox_contact_add')),
        ];
    
        // Include template file.
        include CIVICRM_PLUGIN_DIR . 'assets/templates/metaboxes/metabox.contact.add.php';
    
      }
    
      // ---------------------------------------------------------------------------
      // Form Handler
      // ---------------------------------------------------------------------------
    
      /**
       * Perform actions when the form has been submitted.
       *
       * @since 5.34
       */
      public function form_submitted() {
    
    
        // Nonce is checked in self::form_nonce_check().
        // phpcs:ignore WordPress.Security.NonceVerification.Missing
        if (!isset($_POST['civicrm_quick_add_submit'])) {
          return;
    
        // Save Contact.
        $this->form_nonce_check();
        $this->form_save_contact();
        $this->form_redirect();
    
    
       * Save the CiviCRM Contact.
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
       *
       * @since 5.34
       */
      public function form_save_contact() {
    
        if (!$this->civi->initialize()) {
          $this->form_redirect(['quick-add-error' => 'civicrm']);
        }
    
    
        // Nonce is checked in self::form_nonce_check().
        // phpcs:disable WordPress.Security.NonceVerification.Recommended
        // phpcs:disable WordPress.Security.NonceVerification.Missing
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
    
    
        // Bail if there's no valid First Name.
        $first_name = empty($_POST['civicrm_quick_add_first_name']) ? '' : sanitize_text_field(wp_unslash($_POST['civicrm_quick_add_first_name']));
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        if ($first_name === '') {
          $this->form_redirect(['quick-add-error' => 'first-name']);
        }
    
        // Bail if there's no valid Last Name.
    
        $last_name = empty($_POST['civicrm_quick_add_last_name']) ? '' : sanitize_text_field(wp_unslash($_POST['civicrm_quick_add_last_name']));
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        if ($last_name === '') {
          $this->form_redirect(['quick-add-error' => 'last-name']);
        }
    
        // Bail if there's no valid Email.
    
        $email = empty($_POST['civicrm_quick_add_email']) ? '' : sanitize_email(wp_unslash($_POST['civicrm_quick_add_email']));
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        if (!is_email($email)) {
          $this->form_redirect(['quick-add-error' => 'email']);
        }
    
    
        // phpcs:enable WordPress.Security.NonceVerification.Recommended
        // phpcs:enable WordPress.Security.NonceVerification.Missing
    
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        // Build params to create Contact.
        $params = [
          'version' => 3,
          'contact_type' => 'Individual',
          'first_name' => $first_name,
          'last_name' => $last_name,
          'email' => $email,
        ];
    
        // Call the API.
        $result = civicrm_api('Contact', 'create', $params);
    
        // Bail if there's an error.
    
        if (!empty($result['is_error']) && 1 === (int) $result['is_error']) {
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
          $this->form_redirect(['quick-add-error' => 'api']);
        }
    
        // Bail if there are no results.
        if (empty($result['values'])) {
          $this->form_redirect(['quick-add-error' => 'missing']);
        }
    
        // The result set should contain only one item.
        $contact = array_pop($result['values']);
    
        // Construct list item containing link to "View Contact" screen.
        $url = $this->civi->admin->get_admin_link('civicrm/contact/view', 'reset=1&cid=' . $contact['id']);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        $link = CRM_Utils_String::purifyHtml('<li><a href="' . $url . '" target="_blank">' . $contact['display_name'] . '</a></li>');
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
    
        // Check our session for existing data.
        $session = CRM_Core_Session::singleton();
        $recents = $session->get('quick_add_recents');
    
        // Maybe init array.
        if (empty($recents) || !is_array($recents)) {
          $recents = [$link];
        }
        else {
    
          // Keep the list to a maximum of 5.
          if (count($recents) > 4) {
            $discard = array_pop($recents);
          }
    
          // Prepend this link to it.
          array_unshift($recents, $link);
    
        }
    
        // Resave data in session.
        $session->set('quick_add_recents', $recents);
    
      }
    
      /**
       * Check the nonce.
       *
       * @since 5.34
       */
      private function form_nonce_check() {
    
        // Do we trust the source of the data?
        check_admin_referer('civicrm_quick_add_action', 'civicrm_quick_add_nonce');
    
      }
    
      /**
       * Redirect to the Settings page with an extra param.
       *
       * @since 5.34
       *
       * @param array $args The query arguments.
       */
      private function form_redirect($args = []) {
    
        // Maybe use default array of arguments.
        if (empty($args)) {
          $args = [
            'contact-added' => 'true',
          ];
        }
    
        // Redirect to our admin page.
        wp_safe_redirect(add_query_arg($args, admin_url('index.php')));
        exit;
    
      }
    
      // ---------------------------------------------------------------------------
      // AJAX Handler
      // ---------------------------------------------------------------------------
    
      /**
       * Save the Contact details to CiviCRM.
       *
       * @since 5.34
       */
      public function ajax_contact_add() {
    
        // Default response.
    
        $response = [
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
          'notice' => __('Could not save the contact.', 'civicrm'),
          'added' => FALSE,
        ];
    
        // Since this is an AJAX request, check security.
        $result = check_ajax_referer('civicrm_metabox_contact_add', FALSE, FALSE);
        if ($result === FALSE) {
    
          $response['notice'] = __('Authentication failed.', 'civicrm');
          wp_send_json($response);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        }
    
        // Bail if CiviCRM not inited.
        if (!$this->civi->initialize()) {
    
          $response['notice'] = __('CiviCRM not loaded.', 'civicrm');
          wp_send_json($response);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        }
    
        // Bail if user cannot create Contacts.
        if (!CRM_Core_Permission::check('add contacts')) {
    
          $response['notice'] = __('Permission denied.', 'civicrm');
          wp_send_json($response);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        }
    
        // Bail if there is no valid data.
    
        $data = filter_input(INPUT_POST, 'value', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        if (empty($data)) {
    
          $response['notice'] = __('No data received.', 'civicrm');
          wp_send_json($response);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        }
    
        // Bail if first name is not valid.
    
        $first_name = sanitize_text_field(wp_unslash($data['first_name']));
        if (empty($first_name)) {
          $response['notice'] = __('Please enter a first name.', 'civicrm');
          wp_send_json($response);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        }
    
        // Bail if last name is not valid.
    
        $last_name = sanitize_text_field(wp_unslash($data['last_name']));
        if (empty($last_name)) {
          $response['notice'] = __('Please enter a last name.', 'civicrm');
          wp_send_json($response);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        }
    
        // Bail if email is not valid.
    
        $email = sanitize_email(wp_unslash($data['email']));
        if (!is_email($email)) {
          $response['notice'] = __('Please enter a valid email.', 'civicrm');
          wp_send_json($response);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        // Build params to check for an existing Contact.
        $contact = [
    
          'first_name' => $first_name,
          'last_name' => $last_name,
          'email' => $email,
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        // Bail if there is an existing Contact.
        $existing_id = civi_wp()->admin->get_by_dedupe_unsupervised($contact);
        if ($existing_id !== FALSE && $existing_id !== 0) {
          $open = '<a href="' . $this->civi->admin->get_admin_link('civicrm/contact/view', 'reset=1&cid=' . $existing_id) . '">';
    
          /* translators: 1: The opening anchor tag, 2: The closing anchor tag. */
          $response['notice'] = sprintf(__('There seems to be %1$san existing Contact%2$s with these details.', 'civicrm'), $open, '</a>');
          wp_send_json($response);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        }
    
        // Build params to create Contact.
        $params = [
          'version' => 3,
          'contact_type' => 'Individual',
        ] + $contact;
    
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        // Call the API.
        $result = civicrm_api('Contact', 'create', $params);
    
        // Bail if there's an error.
    
        if (!empty($result['is_error']) && 1 === (int) $result['is_error']) {
          /* translators: %s: The error message. */
          $response['notice'] = sprintf(__('Could not create Contact: %s', 'civicrm'), $result['error_message']);
          wp_send_json($response);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        }
    
        // Bail if there are no results.
        if (empty($result['values'])) {
    
          $response['notice'] = __('Could not find the created Contact.', 'civicrm');
          wp_send_json($response);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        }
    
        // The result set should contain only one item.
        $contact = array_pop($result['values']);
    
        // Construct list item containing link to "View Contact" screen.
        $url = $this->civi->admin->get_admin_link('civicrm/contact/view', 'reset=1&cid=' . $contact['id']);
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        $link = CRM_Utils_String::purifyHtml('<li><a href="' . $url . '" target="_blank">' . $contact['display_name'] . '</a></li>');
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
    
        // Check our session for existing data.
        $session = CRM_Core_Session::singleton();
        $recents = $session->get('quick_add_recents');
    
        // Maybe init array.
        if (empty($recents) || !is_array($recents)) {
          $recents = [$link];
        }
        else {
    
          // Keep the list to a maximum of 5.
          if (count($recents) > 4) {
            $discard = array_pop($recents);
          }
    
          // Prepend this link to it.
          array_unshift($recents, $link);
    
        }
    
        // Resave data in session.
        $session->set('quick_add_recents', $recents);
    
        // Data response.
        $data = [
          'notice' => __('Contact added.', 'civicrm'),
          'data' => $contact,
          'link' => $link,
          'saved' => TRUE,
        ];
    
        // Return the data.
        wp_send_json($data);
    
      }
    
    }