3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * This class generates form components for custom data
21 * It delegates the work to lower level subclasses and integrates the changes
22 * back in. It also uses a lot of functionality with the CRM API's, so any change
23 * made here could potentially affect the API etc. Be careful, be aware, use unit tests.
26 class CRM_Profile_Form_Edit
extends CRM_Profile_Form
{
27 protected $_postURL = NULL;
28 protected $_cancelURL = NULL;
29 protected $_errorURL = NULL;
33 protected $returnExtra;
36 * Pre processing work done here.
41 public function preProcess() {
42 $this->_mode
= CRM_Profile_Form
::MODE_CREATE
;
44 $this->_onPopupClose
= CRM_Utils_Request
::retrieve('onPopupClose', 'String', $this);
45 $this->assign('onPopupClose', $this->_onPopupClose
);
47 //set the context for the profile
48 $this->_context
= CRM_Utils_Request
::retrieve('context', 'Alphanumeric', $this);
51 $this->_blockNo
= CRM_Utils_Request
::retrieve('blockNo', 'String', $this);
54 $this->_prefix
= CRM_Utils_Request
::retrieve('prefix', 'String', $this);
56 // Fields for the EntityRef widget
57 $this->returnExtra
= CRM_Utils_Request
::retrieve('returnExtra', 'String', $this);
59 $this->assign('context', $this->_context
);
61 if ($this->_blockNo
) {
62 $this->assign('blockNo', $this->_blockNo
);
63 $this->assign('prefix', $this->_prefix
);
66 $this->assign('createCallback', CRM_Utils_Request
::retrieve('createCallback', 'String', $this));
68 if ($this->get('skipPermission')) {
69 $this->_skipPermission
= TRUE;
72 if ($this->get('edit')) {
73 // make sure we have right permission to edit this user
74 $userID = CRM_Core_Session
::getLoggedInContactID();
76 // Set the ID from the query string, otherwise default to the current user
77 $id = CRM_Utils_Request
::retrieve('id', 'Positive', $this, FALSE, $userID);
81 $this->_mode
= CRM_Profile_Form
::MODE_EDIT
;
84 // do not allow edit for anon users in joomla frontend, CRM-4668, unless u have checksum CRM-5228
85 // see also CRM-19079 for modifications to the condition
86 $config = CRM_Core_Config
::singleton();
87 if ($config->userFrameworkFrontend
&& $config->userSystem
->is_joomla
) {
88 CRM_Contact_BAO_Contact_Permission
::validateOnlyChecksum($id, $this);
91 CRM_Contact_BAO_Contact_Permission
::validateChecksumContact($id, $this);
93 $this->_isPermissionedChecksum
= TRUE;
97 // CRM-16784: If there is no ID then this can't be an 'edit'
99 CRM_Core_Error
::statusBounce(ts('No user/contact ID was specified, so the Profile cannot be used in edit mode.'));
104 parent
::preProcess();
106 // and also the profile is of type 'Profile'
108 SELECT module,is_reserved
109 FROM civicrm_uf_group
110 LEFT JOIN civicrm_uf_join ON uf_group_id = civicrm_uf_group.id
111 WHERE civicrm_uf_group.id = %1
114 $params = [1 => [$this->_gid
, 'Integer']];
115 $dao = CRM_Core_DAO
::executeQuery($query, $params);
118 while ($dao->fetch()) {
119 $isProfile = ($isProfile ||
($dao->module
== "Profile"));
122 //Check that the user has the "add contacts" Permission
123 $canAdd = CRM_Core_Permission
::check("add contacts");
125 //Remove need for Profile module type when using reserved profiles [CRM-14488]
126 if (!$dao->N ||
(!$isProfile && !($dao->is_reserved
&& $canAdd))) {
127 CRM_Core_Error
::statusBounce(ts('The requested Profile (gid=%1) is not configured to be used for \'Profile\' edit and view forms in its Settings. Contact the site administrator if you need assistance.',
134 * Build the form object.
137 public function buildQuickForm() {
138 if (empty($this->_ufGroup
['id'])) {
139 CRM_Core_Error
::statusBounce(ts('Invalid'));
143 if ($this->_multiRecord
&& $this->_customGroupTitle
) {
144 $groupTitle = ($this->_multiRecord
& CRM_Core_Action
::UPDATE
) ?
'Edit ' . $this->_customGroupTitle
. ' Record' : $this->_customGroupTitle
;
148 $groupTitle = CRM_Core_BAO_UFGroup
::getFrontEndTitle($this->_ufGroup
['id']);
150 CRM_Utils_System
::setTitle($groupTitle);
151 $this->assign('recentlyViewed', FALSE);
153 if ($this->_context
!= 'dialog') {
154 $this->_postURL
= $this->_ufGroup
['post_URL'];
155 $this->_cancelURL
= $this->_ufGroup
['cancel_URL'];
157 $gidString = $this->_gid
;
158 if (!empty($this->_profileIds
)) {
159 $gidString = implode(',', $this->_profileIds
);
162 if (!$this->_postURL
) {
163 if ($this->_context
== 'Search') {
164 $this->_postURL
= CRM_Utils_System
::url('civicrm/contact/search');
166 elseif ($this->_id
&& $this->_gid
) {
167 $urlParams = "reset=1&id={$this->_id}&gid={$gidString}";
168 if ($this->_isContactActivityProfile
&& $this->_activityId
) {
169 $urlParams .= "&aid={$this->_activityId}";
171 // get checksum if present
172 if ($this->get('cs')) {
173 $urlParams .= "&cs=" . $this->get('cs');
175 $this->_postURL
= CRM_Utils_System
::url('civicrm/profile/view', $urlParams);
179 if (!$this->_cancelURL
) {
180 $this->_cancelURL
= CRM_Utils_System
::url('civicrm/profile',
181 "reset=1&gid={$gidString}"
185 // we do this gross hack since qf also does entity replacement
186 $this->_postURL
= str_replace('&', '&', $this->_postURL
);
187 $this->_cancelURL
= str_replace('&', '&', $this->_cancelURL
);
189 // also retain error URL if set
190 $this->_errorURL
= $_POST['errorURL'] ??
NULL;
191 if ($this->_errorURL
) {
192 // we do this gross hack since qf also does entity replacement
193 $this->_errorURL
= str_replace('&', '&', $this->_errorURL
);
194 $this->addElement('hidden', 'errorURL', $this->_errorURL
);
197 // replace the session stack in case user cancels (and we dont go into postProcess)
198 $session = CRM_Core_Session
::singleton();
199 $session->replaceUserContext($this->_postURL
);
202 parent
::buildQuickForm();
204 $this->assign('cancelURL', $this->_cancelURL
);
206 $cancelButtonValue = !empty($this->_ufGroup
['cancel_button_text']) ?
$this->_ufGroup
['cancel_button_text'] : ts('Cancel');
207 $this->assign('cancelButtonText', $cancelButtonValue);
208 $this->assign('includeCancelButton', CRM_Utils_Array
::value('add_cancel_button', $this->_ufGroup
));
210 if (($this->_multiRecord
& CRM_Core_Action
::DELETE
) && $this->_recordExists
) {
211 $this->_deleteButtonName
= $this->getButtonName('upload', 'delete');
212 $this->addElement('submit', $this->_deleteButtonName
, ts('Delete'));
217 //get the value from session, this is set if there is any file
219 $uploadNames = $this->get('uploadNames');
221 if (!empty($uploadNames)) {
222 $buttonName = 'upload';
225 $buttonName = 'next';
229 'type' => $buttonName,
230 'name' => !empty($this->_ufGroup
['submit_button_text']) ?
$this->_ufGroup
['submit_button_text'] : ts('Save'),
234 $this->addButtons($buttons);
236 $this->addFormRule(['CRM_Profile_Form', 'formRule'], $this);
240 * Process the user submitted custom data values.
243 public function postProcess() {
244 parent
::postProcess();
246 // Send back data for the EntityRef widget
247 if ($this->returnExtra
) {
248 $contact = civicrm_api3('Contact', 'getsingle', [
250 'return' => $this->returnExtra
,
252 foreach (explode(',', $this->returnExtra
) as $field) {
253 $field = trim($field);
254 $this->ajaxResponse
['extra'][$field] = $contact[$field] ??
NULL;
258 // When saving (not deleting) and not in an ajax popup
259 if (empty($_POST[$this->_deleteButtonName
]) && $this->_context
!= 'dialog') {
260 CRM_Core_Session
::setStatus(ts('Your information has been saved.'), ts('Thank you.'), 'success');
263 $session = CRM_Core_Session
::singleton();
264 // only replace user context if we do not have a postURL
265 if (!$this->_postURL
) {
266 $gidString = $this->_gid
;
267 if (!empty($this->_profileIds
)) {
268 $gidString = implode(',', $this->_profileIds
);
271 $urlParams = "reset=1&id={$this->_id}&gid={$gidString}";
272 if ($this->_isContactActivityProfile
&& $this->_activityId
) {
273 $urlParams .= "&aid={$this->_activityId}";
275 // Get checksum if present
276 if ($this->get('cs')) {
277 $urlParams .= "&cs=" . $this->get('cs');
279 // Generate one if needed
280 elseif (!CRM_Contact_BAO_Contact_Permission
::allow($this->_id
)) {
281 $urlParams .= "&cs=" . CRM_Contact_BAO_Contact_Utils
::generateChecksum($this->_id
);
283 $url = CRM_Utils_System
::url('civicrm/profile/view', $urlParams);
286 // Replace tokens from post URL
288 'contact_id' => $this->_id
,
292 $contact = civicrm_api('contact', 'get', $contactParams);
293 $contact = reset($contact['values']);
295 $dummyMail = new CRM_Mailing_BAO_Mailing();
296 $dummyMail->body_text
= $this->_postURL
;
297 $tokens = $dummyMail->getTokens();
299 $url = CRM_Utils_Token
::replaceContactTokens($this->_postURL
, $contact, FALSE, CRM_Utils_Array
::value('text', $tokens));
302 $session->replaceUserContext($url);
306 * Intercept QF validation and do our own redirection.
308 * We use this to send control back to the user for a user formatted page
309 * This allows the user to maintain the same state and display the error messages
310 * in their own theme along with any modifications
312 * This is a first version and will be tweaked over a period of time
316 * true if no error found
318 public function validate() {
319 $errors = parent
::validate();
321 if (!$errors && !empty($_POST['errorURL'])) {
323 foreach ($this->_errors
as $name => $mess) {
328 CRM_Utils_System
::setUFMessage($message);
330 $message = urlencode($message);
332 $errorURL = $_POST['errorURL'];
333 if (strpos($errorURL, '?') !== FALSE) {
339 $errorURL .= "gid={$this->_gid}&msg=$message";
340 CRM_Utils_System
::redirect($errorURL);