Merge remote-tracking branch 'upstream/4.5' into 4.5-master-2015-03-09-21-44-34
authorkurund <kurund@civicrm.org>
Mon, 9 Mar 2015 16:27:45 +0000 (21:57 +0530)
committerkurund <kurund@civicrm.org>
Mon, 9 Mar 2015 16:27:45 +0000 (21:57 +0530)
Conflicts:
CRM/Contact/Page/AJAX.php
CRM/Core/Form.php
CRM/Core/Page/AJAX.php
sql/civicrm_generated.mysql
xml/version.xml

17 files changed:
1  2 
CRM/Activity/Page/AJAX.php
CRM/Batch/Page/AJAX.php
CRM/Campaign/Page/AJAX.php
CRM/Contact/Page/AJAX.php
CRM/Core/Form.php
CRM/Core/Page/AJAX.php
CRM/Core/xml/Menu/Contact.xml
CRM/Financial/Page/AJAX.php
CRM/Group/Page/AJAX.php
CRM/Mailing/BAO/Component.php
CRM/Mailing/BAO/Mailing.php
CRM/Mailing/Form/Component.php
CRM/Mailing/Page/AJAX.php
CRM/Pledge/Page/AJAX.php
api/v3/MailingComponent.php
api/v3/MessageTemplate.php
templates/CRM/Contact/Page/View/Summary.js

index dc6b5323ba1e0d57a6fc2c2d565c3e0636e98f8c,3ef11f8ac7837a0448526c8425c193aeb5c4758e..5e11dc87b1ecb1a336e924cbb1bd2b906baf0f00
@@@ -69,17 -65,9 +69,18 @@@ class CRM_Activity_Page_AJAX 
      $activities = CRM_Case_BAO_Case::getCaseActivity($caseID, $params, $contactID, $context, $userID);
  
      $iFilteredTotal = $iTotal = $params['total'];
 -    $selectorElements = array('display_date', 'subject', 'type', 'with_contacts', 'reporter', 'status', 'links', 'class');
 +    $selectorElements = array(
 +      'display_date',
 +      'subject',
 +      'type',
 +      'with_contacts',
 +      'reporter',
 +      'status',
 +      'links',
 +      'class',
 +    );
  
+     header('Content-Type: application/json');
      echo CRM_Utils_JSON::encodeDataTableSelector($activities, $sEcho, $iTotal, $iFilteredTotal, $selectorElements);
      CRM_Utils_System::civiExit();
    }
  
      $iFilteredTotal = $iTotal = $params['total'];
      $selectorElements = array(
 -      'activity_type', 'subject', 'source_contact',
 -      'target_contact', 'assignee_contact',
 -      'activity_date', 'status','links', 'class',
 +      'activity_type',
 +      'subject',
 +      'source_contact',
 +      'target_contact',
 +      'assignee_contact',
 +      'activity_date',
 +      'status',
 +      'links',
 +      'class',
      );
  
+     header('Content-Type: application/json');
      echo CRM_Utils_JSON::encodeDataTableSelector($activities, $sEcho, $iTotal, $iFilteredTotal, $selectorElements);
      CRM_Utils_System::civiExit();
    }
Simple merge
Simple merge
index 26c4fc749874b7aba067d75e7582cd6bd182e077,21dbe7ef0d6dcab09fa45daff59281b9f8695aa0..0da9dd3698176d151dc5572bf41880b3e15d1e9e
@@@ -343,199 -272,19 +286,18 @@@ class CRM_Contact_Page_AJAX 
      CRM_Utils_JSON::output($values);
    }
  
 -  static function groupTree() {
 +  public static function groupTree() {
+     header('Content-Type: application/json');
      $gids = CRM_Utils_Type::escape($_GET['gids'], 'String');
      echo CRM_Contact_BAO_GroupNestingCache::json($gids);
      CRM_Utils_System::civiExit();
    }
  
-   /**
-    * @deprecated
-    * Old quicksearch function. No longer used in core.
-    * @todo: Remove this function and associated menu entry in CiviCRM 5
-    */
-   public static function search() {
-     $json = TRUE;
-     $name = CRM_Utils_Array::value('name', $_GET, '');
-     if (!array_key_exists('name', $_GET)) {
-       $name = CRM_Utils_Array::value('s', $_GET) . '%';
-       $json = FALSE;
-     }
-     $name = CRM_Utils_Type::escape($name, 'String');
-     $whereIdClause = '';
-     if (!empty($_GET['id'])) {
-       $json = TRUE;
-       if (is_numeric($_GET['id'])) {
-         $id = CRM_Utils_Type::escape($_GET['id'], 'Integer');
-         $whereIdClause = " AND civicrm_contact.id = {$id}";
-       }
-       else {
-         $name = $_GET['id'];
-       }
-     }
-     $elements = array();
-     if ($name || isset($id)) {
-       $name = $name . '%';
-       //contact's based of relationhip type
-       $relType = NULL;
-       if (isset($_GET['rel'])) {
-         $relation = explode('_', $_GET['rel']);
-         $relType = CRM_Utils_Type::escape($relation[0], 'Integer');
-         $rel = CRM_Utils_Type::escape($relation[2], 'String');
-       }
-       //shared household info
-       $shared = NULL;
-       if (isset($_GET['sh'])) {
-         $shared = CRM_Utils_Type::escape($_GET['sh'], 'Integer');
-         if ($shared == 1) {
-           $contactType = 'Household';
-           $cName = 'household_name';
-         }
-         else {
-           $contactType = 'Organization';
-           $cName = 'organization_name';
-         }
-       }
-       // contacts of type household
-       $hh = $addStreet = $addCity = NULL;
-       if (isset($_GET['hh'])) {
-         $hh = CRM_Utils_Type::escape($_GET['hh'], 'Integer');
-       }
-       //organization info
-       $organization = $street = $city = NULL;
-       if (isset($_GET['org'])) {
-         $organization = CRM_Utils_Type::escape($_GET['org'], 'Integer');
-       }
-       if (isset($_GET['org']) || isset($_GET['hh'])) {
-         $json = FALSE;
-         $splitName = explode(' :: ', $name);
-         if ($splitName) {
-           $contactName = trim(CRM_Utils_Array::value('0', $splitName));
-           $street = trim(CRM_Utils_Array::value('1', $splitName));
-           $city = trim(CRM_Utils_Array::value('2', $splitName));
-         }
-         else {
-           $contactName = $name;
-         }
-         if ($street) {
-           $addStreet = "AND civicrm_address.street_address LIKE '$street%'";
-         }
-         if ($city) {
-           $addCity = "AND civicrm_address.city LIKE '$city%'";
-         }
-       }
-       if ($organization) {
-         $query = "
- SELECT CONCAT_WS(' :: ',sort_name,LEFT(street_address,25),city) 'sort_name',
- civicrm_contact.id 'id'
- FROM civicrm_contact
- LEFT JOIN civicrm_address ON ( civicrm_contact.id = civicrm_address.contact_id
-                                 AND civicrm_address.is_primary=1
-                              )
- WHERE civicrm_contact.contact_type='Organization' AND organization_name LIKE '%$contactName%'
- {$addStreet} {$addCity} {$whereIdClause}
- ORDER BY organization_name ";
-       }
-       elseif ($shared) {
-         $query = "
- SELECT CONCAT_WS(':::' , sort_name, supplemental_address_1, sp.abbreviation, postal_code, cc.name )'sort_name' , civicrm_contact.id 'id' , civicrm_contact.display_name 'disp' FROM civicrm_contact LEFT JOIN civicrm_address ON (civicrm_contact.id =civicrm_address.contact_id AND civicrm_address.is_primary =1 )LEFT JOIN civicrm_state_province sp ON (civicrm_address.state_province_id =sp.id )LEFT JOIN civicrm_country cc ON (civicrm_address.country_id =cc.id )WHERE civicrm_contact.contact_type ='{$contactType}' AND {$cName} LIKE '%$name%' {$whereIdClause} ORDER BY {$cName} ";
-       }
-       elseif ($hh) {
-         $query = "
- SELECT CONCAT_WS(' :: ' , sort_name, LEFT(street_address,25),city) 'sort_name' , location_type_id 'location_type_id', is_primary 'is_primary', is_billing 'is_billing', civicrm_contact.id 'id'
- FROM civicrm_contact
- LEFT JOIN civicrm_address ON (civicrm_contact.id =civicrm_address.contact_id AND civicrm_address.is_primary =1 )
- WHERE civicrm_contact.contact_type ='Household'
- AND household_name LIKE '%$contactName%' {$addStreet} {$addCity} {$whereIdClause} ORDER BY household_name ";
-       }
-       elseif ($relType) {
-         if (!empty($_GET['case'])) {
-           $query = "
- SELECT distinct(c.id), c.sort_name
- FROM civicrm_contact c
- LEFT JOIN civicrm_relationship ON civicrm_relationship.contact_id_{$rel} = c.id
- WHERE c.sort_name LIKE '%$name%'
- AND civicrm_relationship.relationship_type_id = $relType
- GROUP BY sort_name
- ";
-         }
-       }
-       else {
-         $query = "
- SELECT sort_name, id
- FROM civicrm_contact
- WHERE sort_name LIKE '%$name'
- {$whereIdClause}
- ORDER BY sort_name ";
-       }
-       $limit = 10;
-       if (isset($_GET['limit'])) {
-         $limit = CRM_Utils_Type::escape($_GET['limit'], 'Positive');
-       }
-       $query .= " LIMIT 0,{$limit}";
-       $dao = CRM_Core_DAO::executeQuery($query);
-       if ($shared) {
-         while ($dao->fetch()) {
-           echo $dao->sort_name;
-           CRM_Utils_System::civiExit();
-         }
-       }
-       else {
-         while ($dao->fetch()) {
-           if ($json) {
-             $elements[] = array(
-               'name' => addslashes($dao->sort_name),
-               'id' => $dao->id,
-             );
-           }
-           else {
-             echo $elements = "$dao->sort_name|$dao->id|$dao->location_type_id|$dao->is_primary|$dao->is_billing\n";
-           }
-         }
-         //for adding new household address / organization
-         if (empty($elements) && !$json && ($hh || $organization)) {
-           echo CRM_Utils_Array::value('s', $_GET);
-         }
-       }
-     }
-     if (isset($_GET['sh'])) {
-       echo "";
-       CRM_Utils_System::civiExit();
-     }
-     if (empty($elements)) {
-       $name = str_replace('%', '', $name);
-       $elements[] = array(
-         'name' => $name,
-         'id' => $name,
-       );
-     }
-     if ($json) {
-       echo json_encode($elements);
-     }
-     CRM_Utils_System::civiExit();
-   }
    /**
 -   * Function to delete custom value
 -   *
 +   * Delete custom value.
     */
 -  static function deleteCustomValue() {
 +  public static function deleteCustomValue() {
+     header('Content-Type: text/plain');
      $customValueID = CRM_Utils_Type::escape($_REQUEST['valueID'], 'Positive');
      $customGroupID = CRM_Utils_Type::escape($_REQUEST['groupID'], 'Positive');
  
        }
        list($displayName,
          $userEmail
 -      ) = CRM_Contact_BAO_Contact_Location::getEmailDetails($contactID);
 +        ) = CRM_Contact_BAO_Contact_Location::getEmailDetails($contactID);
+       header('Content-Type: text/plain');
        if ($userEmail) {
          echo $userEmail;
        }
index 1bf6a2f24d6c2159027b5f6b877f46ebc75cbb62,47303ba3b41849283824c71d1dc7aa02bf0fcf57..3b70af47c69f4118805d96baaf0d3abfc1d9a022
@@@ -1824,43 -1768,45 +1824,46 @@@ class CRM_Core_Form extends HTML_QuickF
     */
    private function preProcessChainSelectFields() {
      foreach ($this->_chainSelectFields as $control => $target) {
-       $targetField = $this->getElement($target);
-       $targetType = $targetField->getAttribute('data-callback') == 'civicrm/ajax/jqCounty' ? 'county' : 'stateProvince';
-       $options = array();
-       // If the control field is on the form, setup chain-select and dynamically populate options
-       if ($this->elementExists($control)) {
-         $controlField = $this->getElement($control);
-         $controlType = $targetType == 'county' ? 'stateProvince' : 'country';
-         $targetField->setAttribute('class', $targetField->getAttribute('class') . ' crm-chain-select-target');
-         $css = (string) $controlField->getAttribute('class');
-         $controlField->updateAttributes(array(
-           'class' => ($css ? "$css " : 'crm-select2 ') . 'crm-chain-select-control',
-           'data-target' => $target,
-         ));
-         $controlValue = $controlField->getValue();
-         if ($controlValue) {
-           $options = CRM_Core_BAO_Location::getChainSelectValues($controlValue, $controlType, TRUE);
-           if (!$options) {
-             $targetField->setAttribute('placeholder', $targetField->getAttribute('data-none-prompt'));
+       // The 'target' might get missing if extensions do removeElement() in a form hook.
+       if ($this->elementExists($target)) {
+         $targetField = $this->getElement($target);
+         $targetType = $targetField->getAttribute('data-callback') == 'civicrm/ajax/jqCounty' ? 'county' : 'stateProvince';
+         $options = array();
+         // If the control field is on the form, setup chain-select and dynamically populate options
+         if ($this->elementExists($control)) {
+           $controlField = $this->getElement($control);
+           $controlType = $targetType == 'county' ? 'stateProvince' : 'country';
+           $targetField->setAttribute('class', $targetField->getAttribute('class') . ' crm-chain-select-target');
+           $css = (string) $controlField->getAttribute('class');
+           $controlField->updateAttributes(array(
+             'class' => ($css ? "$css " : 'crm-select2 ') . 'crm-chain-select-control',
+             'data-target' => $target,
+           ));
+           $controlValue = $controlField->getValue();
+           if ($controlValue) {
+             $options = CRM_Core_BAO_Location::getChainSelectValues($controlValue, $controlType, TRUE);
+             if (!$options) {
+               $targetField->setAttribute('placeholder', $targetField->getAttribute('data-none-prompt'));
+             }
 -          } else {
++          } 
++          else {
+             $targetField->setAttribute('placeholder', $targetField->getAttribute('data-empty-prompt'));
+             $targetField->setAttribute('disabled', 'disabled');
            }
          }
+         // Control field not present - fall back to loading default options
          else {
-           $targetField->setAttribute('placeholder', $targetField->getAttribute('data-empty-prompt'));
-           $targetField->setAttribute('disabled', 'disabled');
+           $options = CRM_Core_PseudoConstant::$targetType();
          }
+         if (!$targetField->getAttribute('multiple')) {
+           $options = array('' => $targetField->getAttribute('placeholder')) + $options;
+           $targetField->removeAttribute('placeholder');
+         }
+         $targetField->_options = array();
+         $targetField->loadArray($options);
        }
-       // Control field not present - fall back to loading default options
-       else {
-         $options = CRM_Core_PseudoConstant::$targetType();
-       }
-       if (!$targetField->getAttribute('multiple')) {
-         $options = array('' => $targetField->getAttribute('placeholder')) + $options;
-         $targetField->removeAttribute('placeholder');
-       }
-       $targetField->_options = array();
-       $targetField->loadArray($options);
      }
    }
  
     */
    private function validateChainSelectFields() {
      foreach ($this->_chainSelectFields as $control => $target) {
-       if ($this->elementExists($control)) {
+       if ($this->elementExists($control) && $this->elementExists($target)) {
 -        $controlValue = (array)$this->getElementValue($control);
 +        $controlValue = (array) $this->getElementValue($control);
          $targetField = $this->getElement($target);
          $controlType = $targetField->getAttribute('data-callback') == 'civicrm/ajax/jqCounty' ? 'stateProvince' : 'country';
 -        $targetValue = array_filter((array)$targetField->getValue());
 +        $targetValue = array_filter((array) $targetField->getValue());
          if ($targetValue || $this->getElementError($target)) {
            $options = CRM_Core_BAO_Location::getChainSelectValues($controlValue, $controlType, TRUE);
            if ($targetValue) {
index 21adef2cefd486c6d9b94c2a2ae1196b9744d8f6,315b8fba3d4f8bcb4bbad31c27054dfa1ce93d33..69ae14537d2d2556023502e63f0203315a8b7423
@@@ -199,44 -198,15 +199,19 @@@ class CRM_Core_Page_AJAX 
    }
  
    /**
 -   * Set headers appropriate for a js file
 +   * Set headers appropriate for a js file.
 +   *
 +   * @param int|NULL $ttl
 +   *   Time-to-live (seconds).
     */
 -  static function setJsHeaders() {
 -    // Encourage browsers to cache for a long time - 1 year
 -    $year = 60*60*24*364;
 -    header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + $year));
 +  public static function setJsHeaders($ttl = NULL) {
 +    if ($ttl === NULL) {
 +      // Encourage browsers to cache for a long time - 1 year
 +      $ttl = 60 * 60 * 24 * 364;
 +    }
 +    header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + $ttl));
      header('Content-Type:     application/javascript');
 -    header("Cache-Control: max-age=$year, public");
 +    header("Cache-Control: max-age=$ttl, public");
    }
  
-   /**
-    * Send autocomplete results to the client. Input can be a simple or nested array.
-    * @param array $results
-    *   If nested array, also provide:.
-    * @param string $val
-    *   Array key to use as the value.
-    * @param string $key
-    *   Array key to use as the key.
-    * @deprecated
-    */
-   public static function autocompleteResults($results, $val = 'label', $key = 'id') {
-     $output = array();
-     if (is_array($results)) {
-       foreach ($results as $k => $v) {
-         if (is_array($v)) {
-           echo $v[$val] . '|' . $v[$key] . "\n";
-         }
-         else {
-           echo "$v|$k\n";
-         }
-       }
-     }
-     CRM_Utils_System::civiExit();
-   }
  }
 -
Simple merge
index f5698efeb691f36d7fad0f9ee81c52eda2e7842a,a069036e9f2d4535438a84cb2af3f3cb1c10230a..f9e194520b2b14fc111e7762a28927ae329daa9a
@@@ -446,20 -439,14 +446,21 @@@ class CRM_Financial_Page_AJAX 
        $financialitems = $row;
      }
  
 -    $iFilteredTotal = $iTotal =  $params['total'];
 -    $selectorElements =
 -      array(
 -        'check', 'contact_type', 'sort_name',
 -        'amount', 'trxn_id', 'transaction_date', 'payment_method', 'status', 'name', 'action',
 -      );
 +    $iFilteredTotal = $iTotal = $params['total'];
 +    $selectorElements = array(
 +      'check',
 +      'contact_type',
 +      'sort_name',
 +      'amount',
 +      'trxn_id',
 +      'transaction_date',
 +      'payment_method',
 +      'status',
 +      'name',
 +      'action',
 +    );
  
+     header('Content-Type: application/json');
      echo CRM_Utils_JSON::encodeDataTableSelector($financialitems, $sEcho, $iTotal, $iFilteredTotal, $selectorElements);
      CRM_Utils_System::civiExit();
    }
index 2a346f8eac9c2a9065ec764e8cb599a6526c1b89,4b2aa2029b29f06135e37088fcf1b122ee9c558a..ce150a1422aa140ef1f41c01ed147134165ba779
@@@ -106,12 -90,13 +106,13 @@@ class CRM_Group_Page_AJAX 
        if (empty($params['showOrgInfo'])) {
          unset($selectorElements[6]);
        }
 -     //add setting so this can be tested by unit test
 -     //@todo - ideally the portion of this that retrieves the groups should be extracted into a function separate
 -     // from the one which deals with web inputs & outputs so we have a properly testable & re-usable function
 -      if(!empty($params['is_unit_test'])) {
 +      //add setting so this can be tested by unit test
 +      //@todo - ideally the portion of this that retrieves the groups should be extracted into a function separate
 +      // from the one which deals with web inputs & outputs so we have a properly testable & re-usable function
 +      if (!empty($params['is_unit_test'])) {
          return array($groups, $iFilteredTotal);
        }
+       header('Content-Type: application/json');
        echo CRM_Utils_JSON::encodeDataTableSelector($groups, $sEcho, $iTotal, $iFilteredTotal, $selectorElements);
        CRM_Utils_System::civiExit();
      }
index 7a632418a3b67a246a42d7b281bbadac26837a5b,3da5da0dbcaeb0dc2a9431a4f498dfce99bd8d81..05d87cee7dd55a23739fac1fa183dea383bdf9a6
@@@ -77,20 -79,24 +77,24 @@@ class CRM_Mailing_BAO_Component extend
    }
  
    /**
 -   * Create and Update mailing component
 +   * Create and Update mailing component.
     *
 -   * @param array $params (reference ) an assoc array of name/value pairs
 -   * @param array $ids (deprecated) the array that holds all the db ids
 +   * @param array $params
 +   *   (reference ) an assoc array of name/value pairs.
 +   * @param array $ids
 +   *   (deprecated) the array that holds all the db ids.
     *
 -   * @return object CRM_Mailing_BAO_Component object
 +   * @return CRM_Mailing_BAO_Component
     *
 -   * @access public
 -   * @static
     */
 -  static function add(&$params, $ids = array()) {
 +  public static function add(&$params, $ids = array()) {
      $id = CRM_Utils_Array::value('id', $params, CRM_Utils_Array::value('id', $ids));
      $component = new CRM_Mailing_DAO_Component();
-     $component->id = $id;
+     if ($id) {
+       $component->id = $id;
+       $component->find(TRUE);
+     }
      $component->copyValues($params);
      if (empty($id) && empty($params['body_text'])) {
        $component->body_text = CRM_Utils_String::htmlToText(CRM_Utils_Array::value('body_html', $params));
Simple merge
Simple merge
index 60e7d43e193c1a2b89190bb3c3b4aeb339c4c873,b3f76c13c3b8a7fa50e76d09801c18c83a1ac9a7..5a0fef0788c1378a0f8053f682f4477cc5a3ec8e
@@@ -96,14 -91,11 +96,15 @@@ class CRM_Mailing_Page_AJAX 
  
      $iFilteredTotal = $iTotal = $params['total'];
      $selectorElements = array(
 -      'subject', 'mailing_creator', 'recipients',
 -      'start_date', 'openstats', 'links',
 +      'subject',
 +      'mailing_creator',
 +      'recipients',
 +      'start_date',
 +      'openstats',
 +      'links',
      );
  
+     header('Content-Type: application/json');
      echo CRM_Utils_JSON::encodeDataTableSelector($mailings, $sEcho, $iTotal, $iFilteredTotal, $selectorElements);
      CRM_Utils_System::civiExit();
    }
index 186ef74f822413bafb805d18e0141223fc252ec0,7faeff9b3c7771651eb1429a74e3a108b9f089db..33cec34871433739303ee0d8ac6f2978a4e97117
mode 100755,100644..100755
index a44f6fd36efc9ec8278109b2788494831d918d91,6284cdbc0cb874a73c502e011299778bb76c3238..626232974f9a55eb8aa7988e344c04b61b0d25e6
@@@ -44,13 -51,30 +44,26 @@@ function civicrm_api3_mailing_component
    return _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $params);
  }
  
+ /**
+  * Adjust Metadata for Create action.
+  *
+  * The metadata is used for setting defaults, documentation & validation.
+  *
+  * @param array $spec
+  *   Array of parameters determined by getfields.
+  */
+ function _civicrm_api3_mailing_component_create_spec(&$spec) {
+   $spec['is_active']['api.default'] = 1;
+ }
  /**
 - * Get a mailing_component
 - *
 - * Allowed @params array keys are:
 - * {@getfields mailing_component_get}
 - * @example mailing_componentCreate.php
 + * Get a MailingComponent.
   *
 - * @param $params
 + * @param array $params
   *
 - * @return array of retrieved mailing_component property values.
 - * @access public
 + * @return array
 + *   API result array.
   */
  function civicrm_api3_mailing_component_get($params) {
    return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params);
index 370cb833a057f8e1af8c361d8cef03759f572f5f,07b77ba2520fcae4af7da27065308cea0d3b8ce5..a58219306ed28fc71fd4198ff860f2bcce528308
@@@ -96,11 -91,29 +96,31 @@@ function civicrm_api3_message_template_
  
  /**
   * Sends a template.
 + *
 + * @param array $params
   */
  function civicrm_api3_message_template_send($params) {
-   CRM_Core_BAO_MessageTemplates::sendTemplate($params);
+   // Change external param names to internal ones
+   $fieldSpec = array();
+   _civicrm_api3_message_template_send_spec($fieldSpec);
+   foreach ($fieldSpec as $field => $spec) {
+     if (isset($spec['api.aliases']) && array_key_exists($field, $params)) {
+       $params[CRM_Utils_Array::first($spec['api.aliases'])] = $params[$field];
+       unset($params[$field]);
+     }
+   }
+   if (empty($params['messageTemplateID'])) {
+     if (empty($params['groupName']) || empty($params['valueName'])) {
+       // Can't use civicrm_api3_verify_mandatory for this because it would give the wrong field names
+       throw new API_Exception(
+         "Mandatory key(s) missing from params array: requires id or option_group_name + option_value_name",
+         "mandatory_missing",
+         array("fields" => array('id', 'option_group_name', 'option_value_name'))
+       );
+     }
+   }
+   CRM_Core_BAO_MessageTemplate::sendTemplate($params);
  }
  
  /**
   * The metadata is used for setting defaults, documentation &
   * validation.
   *
 - * @param array $params array or parameters determined by getfields
 + * @param array $params
 + *   Array of parameters determined by getfields.
   */
  function _civicrm_api3_message_template_send_spec(&$params) {
-   $params['messageTemplateID']['api.required'] = 1;
-   $params['messageTemplateID']['title'] = 'Message Template ID';
-   $params['contactId']['api.required'] = 1;
-   $params['contactId']['title'] = 'Contact ID';
-   $params['toEmail']['api.required'] = 1;
-   $params['toEmail']['title'] = 'To Email';
-   $params['toName']['api.required'] = 1;
-   $params['toName']['title'] = 'To Name';
+   $params['id']['description'] = 'ID of the template';
+   $params['id']['title'] = 'Message Template ID';
+   $params['id']['api.aliases'] = array('messageTemplateID', 'message_template_id');
+   $params['option_group_name']['description'] = 'option group name of the template (required if no id supplied)';
+   $params['option_group_name']['title'] = 'Option Group Name';
+   $params['option_group_name']['api.aliases'] = array('groupName');
+   $params['option_value_name']['description'] = 'option value name of the template (required if no id supplied)';
+   $params['option_value_name']['title'] = 'Option Value Name';
+   $params['option_value_name']['api.aliases'] = array('valueName');
+   $params['contact_id']['description'] = 'contact id if contact tokens are to be replaced';
+   $params['contact_id']['title'] = 'Contact ID';
+   $params['contact_id']['api.aliases'] = array('contactId');
+   $params['template_params']['description'] = 'additional template params (other than the ones already set in the template singleton)';
+   $params['template_params']['title'] = 'Template Params';
+   $params['template_params']['api.aliases'] = array('tplParams');
+   $params['from']['description'] = 'the From: header';
+   $params['from']['title'] = 'From';
+   $params['to_name']['description'] = 'the recipient’s name';
+   $params['to_name']['title'] = 'Recipient Name';
+   $params['to_name']['api.aliases'] = array('toName');
+   $params['to_email']['description'] = 'the recipient’s email - mail is sent only if set';
+   $params['to_email']['title'] = 'Recipient Email';
+   $params['to_email']['api.aliases'] = array('toEmail');
+   $params['cc']['description'] = 'the Cc: header';
+   $params['cc']['title'] = 'CC';
+   $params['bcc']['description'] = 'the Bcc: header';
+   $params['bcc']['title'] = 'BCC';
+   $params['reply_to']['description'] = 'the Reply-To: header';
+   $params['reply_to']['title'] = 'Reply To';
+   $params['reply_to']['api.aliases'] = array('replyTo');
+   $params['attachments']['description'] = 'email attachments';
+   $params['attachments']['title'] = 'Attachments';
+   $params['is_test']['description'] = 'whether this is a test email (and hence should include the test banner)';
+   $params['is_test']['title'] = 'Is Test';
+   $params['is_test']['api.aliases'] = array('isTest');
+   $params['pdf_filename']['description'] = 'filename of optional PDF version to add as attachment (do not include path)';
+   $params['pdf_filename']['title'] = 'PDF Filename';
+   $params['pdf_filename']['api.aliases'] = array('PDFFilename');
  }