Answers.Body: <p>The documentation says they are only possible for contact records. See <a href="https://docs.civicrm.org/user/en/latest/organising-your-data/creating-custom-fields/#multiple-record-fieldsets" rel="nofollow noreferrer">https://docs.civicrm.org/user/en/latest/organising-your-data/creating-custom-fields/#multiple-record-fieldsets</a>. I expect the reason for this is that it would have been a lot more effort to add it for all for other entities. There are some built in ones for contacts so I guess it was easier to extend for contacts. It wasn't there from the start</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-11-21T12:31:14.550Z
Answers.Text: I forgot to reply here, thanks for the explanation! I've done a little investigating and done some work and got something to work. I've posted it as answer! I hope to revisit it and see if I can make a patch for it to work on all entities (hopefully).
Answers.UserDisplayName: AlbionBrown
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-11-12T18:41:30.943Z
Answers.LastActivityDate: 2019-11-13T08:36:38.673Z
Answers.LastEditDate: 2019-11-13T08:36:38.673Z
Answers.UserDisplayName: Mick Kahn
Answers.Body: <p>I found a way to get the Participant entity to accept multiple values.</p>
  
  <p>To recap, I needed a custom field set that contained single value text, note and file fields, but needed to be able to add multiple instances of the set itself to a Participant. </p>
  
  <p>I added the following code to the top of the<code>CRM_Event_Form_Participant::postProcess</code> method, underneath the line     <code>$params = $this-&gt;controller-&gt;exportValues($this-&gt;_name);</code>.</p>
  
  <pre><code>// Get CiviCRM's configuration
  $config = CRM_Core_Config::singleton();
  $uploadDirectory = $config-&gt;uploadDir;
  
  // Filter keys that are in the form of custom_[number]_-[number over 1]
  $custom_field_keys = preg_grep('/custom_[0-9]+_-[2-9]+/', array_keys($this-&gt;_submitValues));
  foreach ($custom_field_keys as $key) {
  
    // Save these additional values in the $params array
    $params[$key] = $this-&gt;_submitValues[$key];
  }
  
  // Filter out keys that are in the form of custom_[number]_-[number over 1]
  $custom_file_keys = preg_grep('/custom_[0-9]+_-[2-9]+/', array_keys($this-&gt;_submitFiles));
  
  foreach ($custom_file_keys as $key) {
  
    // rename the uploaded file with a unique number at the end
    $file = $this-&gt;_submitFiles[$key];
  
    $element = new HTML_QuickForm_file($key);
    $element-&gt;_value = $file;
  
    $newName = CRM_Utils_File::makeFileName($file['name']);
    $status = $element-&gt;moveUploadedFile($uploadDirectory, $newName);
    if (!$status) {
      CRM_Core_Error::statusBounce(ts('We could not move the uploaded file %1 to the upload directory %2. Please verify that the \'Temporary Files\' setting points to a valid path which is writable by your web server.', [
        1 =&gt; $value['name'],
        2 =&gt; $uploadDirectory,
      ]));
    }
    if (!empty($data['values'][$pageName][$uploadName]['name'])) {
      @unlink($uploadDirectory . $data['values'][$pageName][$uploadName]);
    }
  
    $value = [
      'name' =&gt; $uploadDirectory . $newName,
      'type' =&gt; $element-&gt;_value['type'],
    ];
    //CRM-19460 handle brackets if present in $uploadName, similar things we do it for all other inputs.
    $value = $element-&gt;_prepareValue($value, TRUE);
    $params = HTML_QuickForm::arrayMerge($params, $value);
  }
  </code></pre>
  
  <p>It begins by first getting the upload directory path from configuration and then finding all keys in the <code>_submitValues</code> property that are the second value onwards. Adding these values directly to the <code>$params</code> array appears to work with no additional work.</p>
  
  <p>For files, the same beginning is needed where we find file field keys that are the second onwards, then we must create a new file field instance with <code>HTML_QuickForm_file</code> for each one, so we may utilise the <code>HTML_QuickForm_file::moveUploadedFile()</code> method. If the file is moved successfully, we prepare the new file values and merge them into the <code>$params</code> array.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-11-20T18:23:47.563Z
Answers.Text: is this a potential patch for core or released as an extension so others can benefit from your work
Answers.UserDisplayName: petednz - fuzion
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-11-21T12:28:14.627Z
Answers.Text: It could potentially be a patch, I think I've identified to point where multiple value field sets fail though, which I'd like to share and work on. Ultimately, it's because when you add a new instance of a custom field set on the form, it just returns the markup to do so, it doesn't actually add it to the _elements or _elementsIndex array properties of the form. I think if this happened, it might just work. The contact entity works, so I might start there.
Answers.UserDisplayName: AlbionBrown
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-11-20T10:53:38.290Z
Answers.LastActivityDate: 2019-11-20T10:53:38.290Z
Answers.UserDisplayName: AlbionBrown
Body: <p>I've added a custom field set to the Participant entity, but I need to it allow for multiple values. I've noticed that the Contact entity has no problem with this, but is there a reason why it doesn't give you this option for other entities? </p>
  
  <p>I've found the cause in this function in the <code>templates/CRM/Custom/Form/Group.tpl</code> file:</p>
  
  <pre><code>  /**
     * Check if this is a contact-related set and show/hide other options accordingly
     */
    function showHideStyle() {
      var
        extend = $(this).val(),
        contactTypes = {/literal}{$contactTypes}{literal},
        showStyle = "{/literal}{$showStyle}{literal}",
        showMultiple = "{/literal}{$showMultiple}{literal}",
        showMaxMultiple = "{/literal}{$showMaxMultiple}{literal}",
        isContact = ($.inArray(extend, contactTypes) &gt;= 0);
  
      if (isContact) {
        $("tr#style_row, tr#is_multiple_row").show();
        if ($('#is_multiple :checked').length) {
          $("tr#multiple_row").show();
        }
      }
      else {
        $("tr#style_row, tr#is_multiple_row, tr#multiple_row").hide();
      }
  
      if (showStyle) {
        $("tr#style_row").show();
      }
  
      if (showMultiple) {
        $("tr#style_row, tr#is_multiple_row").show();
      }
  
      if (!showMaxMultiple) {
        $("tr#multiple_row").hide();
      }
      else if ($('#is_multiple').prop('checked')) {
        $("tr#multiple_row").show();
      }
    }
  </code></pre>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2019-11-12T22:07:27.647Z
Comments.Text: It would be useful to elaborate on the use case - what are the multiple values, for what kinds of fields?
Comments.UserDisplayName: Joe McLaughlin
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2019-11-13T10:01:38.667Z
Comments.Text: I need participants to be able to add multiple files. Each of which have titles, descriptions and other fields.
Comments.UserDisplayName: AlbionBrown
Tags: <forms><civicrm-entity>
Title: Is there a reason why custom field sets cannot be multiple value?
ThreadText: # Is there a reason why custom field sets cannot be multiple value?
  
  (Asked by: username_0 on 2019-11-12)
  
  I've added a custom field set to the Participant entity, but I need to it allow for multiple values. I've noticed that the Contact entity has no problem with this, but is there a reason why it doesn't give you this option for other entities? 
  I've found the cause in this function in the templates/CRM/Custom/Form/Group.tpl file:
    /**
     * Check if this is a contact-related set and show/hide other options accordingly
     */
    function showHideStyle() {
      var
        extend = $(this).val(),
        contactTypes = {/literal}{$contactTypes}{literal},
        showStyle = "{/literal}{$showStyle}{literal}",
        showMultiple = "{/literal}{$showMultiple}{literal}",
        showMaxMultiple = "{/literal}{$showMaxMultiple}{literal}",
        isContact = ($.inArray(extend, contactTypes) >= 0);
  
      if (isContact) {
        $("tr#style_row, tr#is_multiple_row").show();
        if ($('#is_multiple :checked').length) {
          $("tr#multiple_row").show();
        }
      }
      else {
        $("tr#style_row, tr#is_multiple_row, tr#multiple_row").hide();
      }
  
      if (showStyle) {
        $("tr#style_row").show();
      }
  
      if (showMultiple) {
        $("tr#style_row, tr#is_multiple_row").show();
      }
  
      if (!showMaxMultiple) {
        $("tr#multiple_row").hide();
      }
      else if ($('#is_multiple').prop('checked')) {
        $("tr#multiple_row").show();
      }
    }
  
  **Tags:** forms, civicrm-entity
  
  > **Comments:**
  > **username_1**: It would be useful to elaborate on the use case - what are the multiple values, for what kinds of fields?  
  > **username_0**: I need participants to be able to add multiple files. Each of which have titles, descriptions and other fields.  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_2 on 2019-11-12. Score: 2
  
  The documentation says they are only possible for contact records. See <https://docs.civicrm.org/user/en/latest/organising-your-data/creating-custom-fields/#multiple-record-fieldsets>. I expect the reason for this is that it would have been a lot more effort to add it for all for other entities. There are some built in ones for contacts so I guess it was easier to extend for contacts. It wasn't there from the start
  
  > **Comments:**
  > **username_0**: I forgot to reply here, thanks for the explanation! I've done a little investigating and done some work and got something to work. I've posted it as answer! I hope to revisit it and see if I can make a patch for it to work on all entities (hopefully).  
  
  ---
  
  ### Answer by username_0 on 2019-11-20. Score: 2
  
  I found a way to get the Participant entity to accept multiple values.
  To recap, I needed a custom field set that contained single value text, note and file fields, but needed to be able to add multiple instances of the set itself to a Participant. 
  I added the following code to the top of theCRM_Event_Form_Participant::postProcess method, underneath the line     $params = $this->controller->exportValues($this->_name);.
  // Get CiviCRM's configuration
  $config = CRM_Core_Config::singleton();
  $uploadDirectory = $config->uploadDir;
  
  // Filter keys that are in the form of custom_[number]_-[number over 1]
  $custom_field_keys = preg_grep('/custom_[0-9]+_-[2-9]+/', array_keys($this->_submitValues));
  foreach ($custom_field_keys as $key) {
  
    // Save these additional values in the $params array
    $params[$key] = $this->_submitValues[$key];
  }
  
  // Filter out keys that are in the form of custom_[number]_-[number over 1]
  $custom_file_keys = preg_grep('/custom_[0-9]+_-[2-9]+/', array_keys($this->_submitFiles));
  
  foreach ($custom_file_keys as $key) {
  
    // rename the uploaded file with a unique number at the end
    $file = $this->_submitFiles[$key];
  
    $element = new HTML_QuickForm_file($key);
    $element->_value = $file;
  
    $newName = CRM_Utils_File::makeFileName($file['name']);
    $status = $element->moveUploadedFile($uploadDirectory, $newName);
    if (!$status) {
      CRM_Core_Error::statusBounce(ts('We could not move the uploaded file %1 to the upload directory %2. Please verify that the \'Temporary Files\' setting points to a valid path which is writable by your web server.', [
        1 => $value['name'],
        2 => $uploadDirectory,
      ]));
    }
    if (!empty($data['values'][$pageName][$uploadName]['name'])) {
      @unlink($uploadDirectory . $data['values'][$pageName][$uploadName]);
    }
  
    $value = [
      'name' => $uploadDirectory . $newName,
      'type' => $element->_value['type'],
    ];
    //CRM-19460 handle brackets if present in $uploadName, similar things we do it for all other inputs.
    $value = $element->_prepareValue($value, TRUE);
    $params = HTML_QuickForm::arrayMerge($params, $value);
  }
  
  It begins by first getting the upload directory path from configuration and then finding all keys in the _submitValues property that are the second value onwards. Adding these values directly to the $params array appears to work with no additional work.
  For files, the same beginning is needed where we find file field keys that are the second onwards, then we must create a new file field instance with HTML_QuickForm_file for each one, so we may utilise the HTML_QuickForm_file::moveUploadedFile() method. If the file is moved successfully, we prepare the new file values and merge them into the $params array.
  
  > **Comments:**
  > **username_3**: is this a potential patch for core or released as an extension so others can benefit from your work  
  > **username_0**: It could potentially be a patch, I think I've identified to point where multiple value field sets fail though, which I'd like to share and work on. Ultimately, it's because when you add a new instance of a custom field set on the form, it just returns the markup to do so, it doesn't actually add it to the _elements or _elementsIndex array properties of the form. I think if this happened, it might just work. The contact entity works, so I might start there.  
  
  ---
  
  