I want to extend my extension with a custom Activity type that has it's own custom fields. The Activity type is not part of a case.

I can not find in civix help and in http://wiki.civicrm.org/confluence/display/CRMDOC/Create+an+Extension how to do that.

Who knows?

  • Did you do this? And if you did, how did you later refer to the activity_type_ids created - by lookup by name/label or did you store the ID somewhere? I'm needing to do the same. Thanks. Mar 21 '16 at 10:39
  • Also interested. I think a detailed example would be a useful tool. I can help make one once I understand it myself. Apr 8 '16 at 17:53

And this is an example of a function to create an Activity type

   * Function to create activity type
   * @param array $params
   * @return array
   * @throws Exception when params invalid
   * @throws Exception when error from API create
   * @access public
   * @static
  public static function createActivityType($params) {
    $activityTypeData = array();
    $params['option_group_id'] = self::getOptionGroupIdWithName('activity_type');
    if (!isset($params['name']) || empty($params['name'])) {
      throw new Exception('When trying to create an Activity Type name is a mandatory parameter and can not be empty');
    if (empty($params['label']) || !isset($params['label'])) {
      $params['label'] = self::buildLabelFromName($params['name']);
    if (!isset($params['is_active'])) {
      $params['is_active'] = 1;
    if (self::getActivityTypeWithName($params['name']) == FALSE) {
      try {
        $activityType = civicrm_api3('OptionValue', 'Create', $params);
        $activityTypeData = $activityType['values'][$activityType['id']];
      } catch (CiviCRM_API3_Exception $ex) {
        throw new Exception('Could not create activity type with name ' . $params['name']
          . ', error from API OptionValue Create: ' . $ex->getMessage());
    return $activityTypeData;
  • Where would this function be placed/integrated? Apr 6 '16 at 20:32

You first create the activity type in your code. Then you create the custom group with extends = Activity, where you then have to set the activity type you want to link the data too in the param 'extends_entity_column_value', something like this:

 if ($customGroupParams['extends'] == 'Activity') {
      $extendsActivity = CRM_Streetimport_Utils::getActivityTypeWithName($customGroupData['extends_entity_column_value']);
      $customGroupParams['extends_entity_column_value'] = CRM_Core_DAO::VALUE_SEPARATOR.$extendsActivity['value'];

and then you can add your custom fields. Does that answer your question?


I've done this with the API. Pretty much everything you create in CiviCRM has a machine name field. This can be used as a known identifier, whereas the numerical Id fields cannot be known across different installations.

I made a helper which significantly shortened my install code:

 * Helper function for creating data structures.
 * @param string $entity - name of the API entity.
 * @param Array $params_min parameters to use for search.
 * @param Array $params_extra these plus $params_min are used if a create call
 *              is needed.
function pelf_get_or_create($entity, $params_min, $params_extra) {
  $params_min += ['sequential' => 1];
  $result = civicrm_api3($entity, 'get', $params_min);
  if (!$result['count']) {
    // Couldn't find it, create it now.
    $result = civicrm_api3($entity, 'create', $params_extra + $params_min);
  return $result['values'][0];

Then the install can do things like:

  // Ensure we have the activity type set up for Prospects.
  $my_activity_type = pelf_get_or_create('OptionValue', [
    'option_group_id' => "activity_type",
    'name' => "my_unique_activity",
  [ 'label' => 'Something suitable']);

  // Ensure we have the custom field group we need for prospects.
  $customgroup = pelf_get_or_create('CustomGroup', [
    'name' => "another_unique_name",
    'extends' => "Activity",
    'extends_entity_column_value' => $my_activity_type['value'],
  ['title' => 'Some custom Details']);

  // Add a field.
  $my_field_1 = pelf_get_or_create('CustomField', [
      'name' => "another_name_for_field",
      'custom_group_id' => $customgroup['id'],
      'data_type' => "String",
      'html_type' => "Text",
      'is_required' => "1",
      'default_value' => "0",
    ['label' => 'Explain it']);

Nb. I had to create a select field, too. This was a pain because you have to create the option group for its options, then all its options, then finally create the field itself, passing the id of the option group to the field create. I omitted it here because it's basically more of the same.

Anyway, with that code in my install hook, I can then easily fetch them in other code in my extension by using the names as lookups. AFAICS the name field is a machine-only field - there's usually a 'label' or 'title' field that is used by the UI.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.