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 the`CRM_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.  
  
  ---
  
  