3 define('CCRM_EXTVALID_XMLFILE_FIELD', 'field_extension_release_xml');
6 * Loads and Validates fields from the XML info file:
7 * Release version == <version> (required)
8 * Release Date == <releaseDate>
9 * Release Status == <develStage> (required)
10 * Release CiviCRM Compatibility == <compatibility><ver> (required)(multiple ok)
11 * Parent Extension FQN == <extension key="xx.yy.zz"> (required)
12 * Documentation Link == <url desc="http://example.com">
13 * (Postponed) Download URL > downloadUrl (required)
14 * TODO: Fully validate the XML document format against a DOCtype
19 * implements hook_node_validate
21 function ccrm_extensionvalidation_node_validate($node, $form, &$form_state) {
22 // Make sure author of extension is in maintainers
23 if ('extension' == $node->type) {
24 $authornid = $node->uid; $delta = 0; $foundauthor = FALSE;
25 foreach ($form_state['values']['field_maintainers'][LANGUAGE_NONE] as $index => $ref) {
26 if (is_numeric($ref['target_id'])
27 && $form_state['values']['field_maintainers'][LANGUAGE_NONE][$index]['target_id'] == $authornid) {
32 if (FALSE == $foundauthor) {
33 $form_state['values']['field_maintainers'][LANGUAGE_NONE][$delta]['target_id'] = $authornid;
34 $form_state['values']['field_maintainers'][LANGUAGE_NONE][$delta]['_weight'] = $delta;
37 // Set maintainers on releases from parent extension
38 if ($node->type == 'extension_release_civicrm') {
39 $parentnid = $node->field_extension_nr_crm[LANGUAGE_NONE][0]['nid'];
41 if ($node->type == 'extension_release_cms') {
42 $parentnid = $node->field_extension_nr_cms[LANGUAGE_NONE][0]['nid'];
44 if ($node->type == 'extension_release_civicrm' || $node->type == 'extension_release_cms') {
45 $parent_node = is_numeric($parentnid) ? node_load($parentnid) : NULL;
46 if (NULL == $parent_node) {
47 _ccrmextvalid_formseterror('title', t('Could not determine parent extension. Please add via extension page.'));
51 if ($node->type == 'extension_release_cms') {
52 // Delete current maintainers
53 foreach ($form_state['values']['field_extension_release_na_mains'][LANGUAGE_NONE] as $index => $ref) {
54 if (is_numeric($ref['target_id'])) {
55 $form_state['values']['field_extension_release_na_mains'][LANGUAGE_NONE][$index]['target_id'] = NULL;
58 // Check parent node has maintainers set and add maintainers from parent
60 foreach ($parent_node->field_maintainers[LANGUAGE_NONE] as $index => $ref) {
61 if (is_numeric($ref['target_id'])) {
62 $form_state['values']['field_extension_release_na_mains'][LANGUAGE_NONE][$index]['target_id'] = $ref['target_id'];
63 $form_state['values']['field_extension_release_na_mains'][LANGUAGE_NONE][$index]['_weight'] = $delta++;
67 if ($node->type == 'extension_release_civicrm') {
68 // Delete current maintainers
69 foreach ($form_state['values']['field_extension_release_ci_mains'][LANGUAGE_NONE] as $index => $ref) {
70 if (is_numeric($ref['target_id'])) {
71 $form_state['values']['field_extension_release_ci_mains'][LANGUAGE_NONE][$index]['target_id'] = NULL;
74 // Check parent node has maintainers set and add maintainers from parent
76 foreach ($parent_node->field_maintainers[LANGUAGE_NONE] as $index => $ref) {
77 if (is_numeric($ref['target_id'])) {
78 $form_state['values']['field_extension_release_ci_mains'][LANGUAGE_NONE][$index]['target_id'] = $ref['target_id'];
79 $form_state['values']['field_extension_release_ci_mains'][LANGUAGE_NONE][$index]['_weight'] = $delta++;
83 if ($node->type == 'extension_release_civicrm') {
84 _ccrmextvalid_validatexml($node, $form, &$form_state, $parent_node);
88 function _ccrmextvalid_validatexml($node, $form, &$form_state, $parent_node) {
89 $keeperrors = array();
90 $file_id = $form_state['values'][CCRM_EXTVALID_XMLFILE_FIELD][LANGUAGE_NONE][0]['fid'];
93 $file = file_load($file_id);
95 libxml_use_internal_errors(TRUE);
96 $doc = new DOMDocument();
98 $doc->strictErrorChecking = FALSE;
100 if ($doc->load(drupal_realpath($file->uri))) {
101 // Initialize variables to be read
102 $got = array('version' => FALSE, 'releaseDate' => FALSE, 'develStage' => FALSE,
103 'ver' => FALSE, 'extension' => FALSE, 'documentation' => FALSE); //'downloadUrl' =>FALSE (postponed)
104 foreach ($got as $tag => $tagcontents) {
105 $wegotit = _ccrmextvalid_validateTag($node, $form, $form_state, $doc, $tag, $tagcontents, $keeperrors, $parent_node);
106 $got[$tag] = $tagcontents;
108 _ccrmextvalid_assignTag($form, &$form_state, $tag, &$tagcontents);
111 if ($tag == 'extension') {
112 _ccrmextvalid_seterror('title', t("The Fully Qualified Name in the XML file did not match parent extension: @tagcont",
113 array('@tagcont' => $tagcontents)), $keeperrors);
116 _ccrmextvalid_seterror('title', t("The Extension Release XML file contained an invalid or empty value for tag '@tag'",
117 array('@tag' => $tag)), $keeperrors);
123 _ccrmextvalid_seterror( 'title', t('The XML file could not be read'), $keeperrors);
128 _ccrmextvalid_seterror( CCRM_EXTVALID_XMLFILE_FIELD, t('Please upload an XML file descriptor'), $keeperrors);
130 if (!empty($keeperrors)) {
131 _ccrmextvalid_formseterror($keeperrors);
135 // return whether we had a
136 function _ccrmextvalid_validateTag($node, $form, &$form_state, $doc, $tag, &$tagcontents, &$keeperrors, $parent_node) {
137 $gotit = FALSE; $tagcount = 0; $contents = '';
143 $tagcount = _ccrmextvalid_getOneReqdByTag($doc, $tag, &$tagcontents);
146 $tagcount = _ccrmextvalid_getByTag($doc, $tag, &$tagcontents);
148 case 'documentation':
149 $domnodes = $doc->getElementsByTagName('url');
150 $tagcount = $domnodes->length;
151 foreach ($domnodes as $node) {
152 if ($node->hasAttributes()) {
153 $attrib = $node->attributes->getNamedItem('desc');
154 if (strtolower($attrib->value) == "documentation") {
155 $tagcontents = $node->nodeValue;
161 $domnodes = $doc->getElementsByTagName('extension');
162 $tagcount = $domnodes->length;
163 if ($tagcount == 1) {
164 $elmt = $domnodes->item(0);
165 if ($elmt->hasAttributes()) {
166 $tagcontents = $elmt->getAttribute('key');
171 drupal_set_message(t('Invalid tag used in _ccrmextvalid_validateTag: @tag', array('@tag' => $tag)));
177 module_load_include('version.inc', 'ccrm_extensionvalidation');
178 if (ccrm_extensionvalidation_version_isValid($tagcontents)) {
179 $contents = $tagcontents;
184 $tmp = filter_var($tagcontents, FILTER_SANITIZE_STRING);
185 if (preg_match('/\d{4}-\d{2}-\d{2}/', $tmp)) {
186 //$contents = new DateObject($tagcontents, NULL);
192 $contents = _ccrmextvalid_checkallowedval($tagcontents, 'field_extension_release_status');
193 if ($contents && (count($contents) == 1)) {
196 $contents = $contents[0];
199 if ($tagcount == 1) {
200 $tagcontents = array($tagcontents);
202 $vers = array_map('_ccrmextvalid_mapversionval', $tagcontents);
203 $okvers = _ccrmextvalid_checkallowedval($vers, 'field_extension_release_civicrm');
204 if (count($okvers) == count($vers)) {
206 $contents = array_map('_ccrmextvalid_mapversionvaltid', $okvers);
209 case 'documentation':
210 $contents = filter_var($tagcontents, FILTER_VALIDATE_URL);
214 $parentkey = isset($parent_node) ? $parent_node->field_extension_fq_name[LANGUAGE_NONE][0]['safe_value'] : NULL;
215 $contents = $parentkey . ' vs. ' . $tagcontents;
216 if ($tagcontents == $parentkey) {
223 $tagcontents = $contents;
228 function _ccrmextvalid_assignTag($form, &$form_state, $tag, &$tagcontents) {
229 // Add $tagcontents to $form_state
232 module_load_include('version.inc', 'ccrm_extensionvalidation');
233 $form_state['values']['field_extension_release_version'][LANGUAGE_NONE][0]['value'] = t('Version @ver', array('@ver' => $tagcontents));
234 $form_state['values']['field_extension_release_vc'][LANGUAGE_NONE][0]['value'] = ccrm_extensionvalidation_version_normalize($tagcontents);
237 $form_state['values']['field_extension_release_date'][LANGUAGE_NONE][0]['value'] = $tagcontents;
240 $form_state['values']['field_extension_release_status'][LANGUAGE_NONE][0]['value'] = $tagcontents;
243 $form_state['values']['field_extension_release_url'][LANGUAGE_NONE][0]['url'] = $tagcontents;
246 $form_state['values']['field_extension_release_civicrm'][LANGUAGE_NONE] = $tagcontents;
248 case 'documentation':
249 $form_state['values']['field_documentation'][LANGUAGE_NONE][0]['url'] = $tagcontents;
254 // Get the value of a single tag from the XML that should only have one value
255 function _ccrmextvalid_getOneReqdByTag($doc, $tagname, &$values) {
256 $defaultval = $values;
257 $tagcount = _ccrmextvalid_getByTag($doc, $tagname, &$values);
259 form_set_error(CCRM_EXTVALID_XMLFILE_FIELD,
260 t("XML file must contain only one '@tagname' element"), array('tagname' => $tagname));
265 // Get all tags by value from the XML
266 function _ccrmextvalid_getByTag($doc, $tagname, &$values) {
267 $domnodes = $doc->getElementsByTagName($tagname);
268 $nodecount = $domnodes->length;
269 if ($nodecount > 0) {
270 if ($nodecount > 1) {
272 for ($i = 0; $i < $domnodes->length; $i++) {
273 $values[] = $domnodes->item($i)->nodeValue;
277 $values = $domnodes->item(0)->nodeValue;
284 // Compare a value with the allowed values (lower case)
285 function _ccrmextvalid_checkallowedval($vals, $fieldname) {
287 $vals = !is_array($vals) ? array($vals) : $vals;
288 $fld = field_info_field($fieldname);
289 if ($fld['type'] == 'taxonomy_term_reference') {
290 $allowedvals1 = taxonomy_allowed_values($fld);
292 elseif ($fld['type'] == 'list_text') {
293 $allowedvals1 = list_allowed_values(field_info_field($fieldname));
296 // No other field types handled
299 // Taxonomy tags search in values, list search in keys
300 $allowedvals = array_map('strtolower', $allowedvals1);
301 foreach ($vals as $key => $val) {
302 if ($fld['type'] == 'list_text') {
303 if (array_key_exists($val, $allowedvals)) {
308 $retvals = array_merge($retvals, array_keys($allowedvals, $val));
314 // Little helper function for array_map
315 function _ccrmextvalid_mapversionval($val) {
316 return "civicrm {$val}";
319 // Little helper function for array_map
320 function _ccrmextvalid_mapversionvaltid($val) {
321 return array( 'tid' => $val);
324 function _ccrmextvalid_seterror( $name, $message, &$keeperrors) {
325 // Form_set_error can only happen once on an element so collect the errors
326 $keeperrors[$name] = empty($keeperrors[$name]) ? $message : $keeperrors[$name] . " " . $message;
329 function _ccrmextvalid_formseterror($errors) {
330 foreach ($errors as $name => $err) {
331 // No other field is working reliably, tried the file upload button & others so usually use title
332 form_set_error(check_plain($name), check_plain($err));