Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
trim_common.inc
1 <?php
17 /*
18 * TRIM Package Library Functions
19 *
20 * This file contains global functions that are useful throughout the TRIM module
21 *
22 *
23 * @author Edison Wang <ewang@squiz.com.au>
24 * @author Huan Nguyen <hnguyen@squiz.net>
25 * @version $Revision: 1.21 $
26 * @package MySource_Matrix_Packages
27 * @subpackage trim
28 */
29 
30 
32 {
33 
40  public static function constructTrimRequest()
41  {
42  $trim_request = Array (
43  'HideVersionNumbers' => TRUE,
44  'ProvideTimingResults' => FALSE,
45  'ForceRealTimeCacheUpdate' => TRUE,
46  );
47 
48  return $trim_request;
49 
50  }//end constructTrimRequest()
51 
52 
53 
63  public static function constructRecordSearchClauseGroupedXml ($search_clause = Array(), $classifications)
64  {
65 
66  if (!isset ($search_clause['search_groups'])) return '';
67  $group_clause = new SimpleXMLElement('<RecordSearchGroups/>');
68  foreach ($search_clause['search_groups'] as $group) {
69  if (isset ($group['content'])) {
70  foreach ($group['content'] as $field) {
71  if (preg_match('/^udf:/', $field['name'])) {
72  $RecordUserFieldSearchClause = $group_clause->addChild('RecordUserFieldSearchClause');
73  $RecordUserFieldSearchClause->addChild('UserDefinedFieldName', $field['name']) ;
74  $RecordUserFieldSearchClause->addChild('From', $field['keyword']) ;
75  }
76  else {
77  $RecordStringSearchClause = $group_clause->addChild('RecordStringSearchClause');
78  $RecordStringSearchClause->addChild('Type', $field['name']) ;
79  $RecordStringSearchClause->addChild('Arg', $field['keyword']) ;
80  }
81  }
82  // add group logic operator
83  $group_logic = isset($group['group_logic']) ? $group['group_logic'] : 'AND';
84  if (count($group['content']) > 1) {
85  if($group_logic === 'AND') {
86  $group_clause->addChild('RecordAndSearchClause');
87  }
88  else {
89  $group_clause->addChild('RecordOrSearchClause');
90  }
91  }
92  }
93  }
94  // add global logic operator
95  if (count($search_clause['search_groups']) > 1) {
96  $global_logic = isset($search_clause['global_logic']) ? $search_clause['global_logic'] : 'AND';
97  if($global_logic === 'AND') {
98  $group_clause->addChild('RecordAndSearchClause');
99  }
100  else {
101  $group_clause->addChild('RecordOrSearchClause');
102  }
103  }
104 
105  // add classification restrictions
106  if (!empty($classifications)) {
107  $i = 0;
108  foreach ($classifications as $uri => $title) {
109  $RecordClassificationSearchClause = $group_clause->addChild('RecordClassificationSearchClause');
110  $RecordClassificationSearchClause->addChild('ClassificationUri', $uri);
111  if($i > 0) {
112  $group_clause->addChild('RecordOrSearchClause');
113  }
114  $i++;
115  }
116  if(count($search_clause['search_groups']) > 0 )
117  $group_clause->addChild('RecordAndSearchClause');
118  }
119 
120 
121  // convert to string
122  $result = '';
123  foreach ($group_clause->children() as $child) {
124  $result .= $child->asXML();
125  }
126  return $result;
127  }//end constructRecordSearchClauseGroupedXml()
128 
129 
141  public static function constructRecordSearch($search_clauses=Array(), $record_search_options)
142  {
143  $recordSearch = Array (
144  'Id' => 'recordsearch',
145  'Sort1Descending' => (boolean) $record_search_options['sort_1_descending'],
146  'Sort2Descending' => (boolean) $record_search_options['sort_2_descending'],
147  'Sort3Descending' => (boolean) $record_search_options['sort_3_descending'],
148  'TargetForUpdate' => FALSE,
149  'IsForUpdate' => FALSE,
150  'Limit' => $record_search_options['limit'],
151  'Sort1' => $record_search_options['sort_1'],
152  'Sort2' => $record_search_options['sort_2'],
153  'Sort3' => $record_search_options['sort_3'],
154  //'Record'.$record_search_type.'Clause' => $search_clause,
155  'FilterRecordTypesInclude' => $record_search_options['record_type_filter'],
156  'FilterRecordTypesExclude' => '',
157  'FilterRecordClassInclude' => '',
158  'FilterRecordClassExclude' => '',
159  'FilterRecordDispositionInclude' => '',
160  'FilterRecordDispositionExclude' => '',
161  'FilterFileTypes' => $record_search_options['file_types'],
162  'FilterFinalizedState' => "Both",
163  'Uri' => '',
164  'IgnoreOnError' => FALSE,
165  );
166 
167  $recordSearch = array_merge($recordSearch, $search_clauses);
168  return $recordSearch;
169 
170  }//end constructRecordSearch()
171 
172 
181  public static function constructShortcutRecordUri($uri)
182  {
183  $sru = Array (
184  'Uri' => $uri,
185  'TargetForUpdate' => TRUE,
186  'IsForUpdate' => TRUE,
187  'Limit' => 0,
188  );
189 
190  return $sru;
191 
192  }//end constructShortcutRecordUri()
193 
194 
203  public static function constructShortcutRecordUris($uris)
204  {
205  $srus = Array (
206  'Uris' => $uris,
207  'TargetForUpdate' => FALSE,
208  'IsForUpdate' => FALSE,
209  'Limit' => 10,
210  );
211  return $srus;
212 
213  }//end constructShortcutRecordUris()
214 
215 
224  public static function constructDownload($download_limit='4194304')
225  {
226  $download = Array (
227  'Checkout' => FALSE,
228  'MaximumTransferBytes' => $download_limit,
229  'TransferInset' => '0',
230  'DownloadId' => 'chunk',
231  'finished' => FALSE,
232  'TargetForUpdate' => FALSE,
233  'TransferType' => 'inline',
234  );
235 
236  return $download;
237 
238  }//end constructDownload()
239 
240 
249  public static function constructUpload($base64PayLoad)
250  {
251  $upload = Array (
252  'TransferType' => 'inline',
253  'Final' => TRUE,
254  'Base64Payload' => $base64PayLoad,
255  'TargetForUpdate' => TRUE,
256  );
257 
258  return $upload;
259 
260  }//end constructUpload()
261 
262 
271  public static function constructFetch($record_properties=Array(), $search_limit)
272  {
273  $spec_props = Array();
274  foreach ($record_properties as $key => $value) {
275  $spec_props[] = Array (
276  'Name' => $key,
277  );
278  }//end foreach
279 
280  $fetch = Array (
281  'Id' => 'recordfetch',
282  'IsForUpdate' => FALSE,
283  'Items' => $spec_props,
284  'TargetForUpdate' => FALSE,
285  'Limit' => $search_limit,
286  'Populate' => 0,
287  'HideVersion' => FALSE,
288  );
289  return $fetch;
290 
291  }//end constructFetch()
292 
293 
300  public static function constructCreate($container, $title, $author, $security_level, $additional_fields)
301  {
302  $record_type = Array (
303  'Name' => 'recRecordType',
304  'Val' => 'inject:recordtype',
305  );
306  $container = Array (
307  'Name' => 'recContainer',
308  'Val' => $container,
309  );
310  $record_title = Array (
311  'Name' => 'recTitle',
312  'Val' => $title,
313  );
314  $record_security_profile = Array (
315  'Name' => 'recSecurity',
316  'Val' => $security_level, // Unrestricted = 10
317  );
318  $record_assignee = Array (
319  'Name' => 'recCurrentLoc',
320  'Val' => $author,
321  );
322  $record_author = Array (
323  'Name' => 'recAuthorLoc',
324  'Val' => $author,
325  );
326 
327  $create = Array (
328  'Saving' => TRUE,
329  'VerifyAndCreateWarning' => FALSE,
330 
331  'TargetForUpdate' => TRUE,
332  'Items' => Array (
333  'InputProperty' => Array (
334  $record_type,
335  $record_title,
336  $record_assignee,
337  $container,
338  $record_security_profile,
339  $record_author,
340  ),
341  ),
342  'TrimObjectType' => "record",
343  );
344 
345  foreach ($additional_fields as $field_name => $value) {
346  $new_input_property = Array (
347  'Name' => $field_name,
348  'Val' => $value,
349  );
350  $create['Items']['InputProperty'][] = $new_input_property;
351  }//end if
352 
353 
354  return $create;
355 
356  }//end constructCreate()
357 
358 
365  public static function getURIFromRecordNumber($connection, $record_number)
366  {
367  $results = Array();
368  $RecordStringSearchClause = Array (
369  'RecordStringSearchClause' => Array (
370  'Arg' => $record_number,
371  'Type' => "RecordNumber",
372  ),
373  );
374  $record_properties = Array ('recTitle');
375  $record_search_options = self::getDefaultRecordSearchOptions();
376 
377  $operations = Array (
378  'Fetch' => self::constructFetch(Array(), 1),
379  'RecordSearch' => self::constructRecordSearch($RecordStringSearchClause, $record_search_options),
380  );
381 
382  $results = self::executeRequest($connection, $operations, 'Fetch');
383 
384  if (!empty($results)) {
385  if (isset($results[0]['Uri'])) {
386  return $results[0]['Uri'];
387  }//end
388  }//end if
389 
390  return 0;
391 
392  }//end getURIFromRecordNumber()
393 
394 
395 
402  public static function getURIsFromClassification($connection, $classification)
403  {
404  $results = Array();
405 
406  $specific_items = Array();
407  $specific_items['plnTitle'] = 1;
408 
409  $operations = Array (
410  'ClassificationStringSelect' => Array (
411  'Arg' => $classification,
412  'Type' => 'ByWord',
413  'TargetForUpdate' => FALSE,
414  'IsForUpdate' => FALSE,
415  'Limit' => 100000,
416  ),
417  'Fetch' => self::constructFetch($specific_items, 100000),
418  );
419 
420  $results = self::executeRequest($connection, $operations, 'Fetch');
421 
422  return $results;
423 
424  }//end getURIsFromClassification()
425 
426 
427 
428 
435  public static function getRecordTypeNamesFromUris($connection, $uris)
436  {
437  $results = Array();
438 
439  $specific_items = Array();
440  $specific_items['rtyName'] = 1;
441 
442  $operations = Array (
443  'RecordTypeUrisSelect' => Array (
444  'Uris' => $uris,
445  'BehaviorFilter' => 'Document',
446  'TypeFilter' => 'All',
447  'TargetForUpdate' => FALSE,
448  'IsForUpdate' => FALSE,
449  'Limit' => 100000,
450  ),
451  'Fetch' => self::constructFetch($specific_items, 100000),
452  );
453 
454  $results = self::executeRequest($connection, $operations, 'Fetch');
455 
456  foreach ($results as $record_info) {
457  $record_names[$record_info['Uri']] = $record_info['rtyName'];
458  }
459  return $record_names;
460 
461  }//end getRecordTypeNamesFromUris()
462 
463 
464 
471  public static function getAllRecordTypes($connection)
472  {
473  $results = Array();
474 
475  $specific_items = Array();
476  $specific_items['rtyName'] = 1;
477  $behaviours = self::getRecordBehaviours();
478 
479  foreach($behaviours as $behaviour) {
480  $operations = Array (
481  'RecordTypeSimpleSelect' => Array (
482  'Type' => 'All',
483  'BehaviorFilter' => $behaviour,
484  'TypeFilter' => 'All',
485  'TargetForUpdate' => FALSE,
486  'IsForUpdate' => FALSE,
487  'Limit' => 100000,
488  ),
489  'Fetch' => self::constructFetch($specific_items, 100000),
490  );
491 
492 
493  $result = self::executeRequest($connection, $operations, 'Fetch');
494  $results = array_merge($results, $result);
495  }
496 
497  foreach ($results as $record_info) {
498  $record_names[$record_info['Uri']] = $record_info['rtyName'];
499  }
500  return $record_names;
501 
502  }//end getAllRecordTypes()
503 
504 
513  public static function constructRecordContainerAltClause($container_uri)
514  {
515  $search_clause = Array (
516  'Uri' => $container_uri,
517  'IncludeAlternateContainers' => FALSE,
518  );
519 
520  return $search_clause;
521 
522  }//end constructR
523 
524 
531  public static function getDocumentListUnderContainer($container_uri)
532  {
533  $results = Array();
534  $RecordContainerAltClause = Array (
535  'RecordContainerAltClause' => self::constructRecordContainerAltClause($container_uri),
536  );
537 
538  $operations = Array (
539  'Fetch' => self::constructFetch(),
540  'RecordSearch' => self::constructRecordSearch($RecordContainerAltClause),
541  );
542 
543  $results = self::executeRequest($operations, 'Fetch');
544 
545 
546  return $results;
547 
548  }//end getDocumentListUnderContainer()
549 
550 
557  public static function getRecordSearchTypes()
558  {
559  $rsTypes = Array (
560  'StringSearch' => 'Record Content',
561  'DateRangeSearch' => 'Record Date Range',
562  'NumberRangeSearch' => 'Record Number Range',
563  );
564 
565  return $rsTypes;
566  }//end getRecordSearchTypes()
567 
568 
569 
576  public static function getRecordBehaviours()
577  {
578  $types = Array (
579  'Document' => 'Document',
580  'Folder' => 'Folder',
581  'Series' => 'Series',
582  'Box' => 'Box',
583  'PaperFolder' => 'PaperFolder',
584  'Blueprint' => 'Blueprint',
585  'Clause' => 'Clause',
586  );
587 
588  return $types;
589 
590  }//end getRecordBehaviours()
591 
592 
599  public static function getRecordStringSearchClauseType()
600  {
601  $slTypes = Array (
602  'AnyWord' => 'AnyWord',
603  'Consignment' => 'Consignment',
604  'DocumentContent' => 'DocumentContent',
605  'NotesWord' => 'NotesWord',
606  'RecordNumber' => 'RecordNumber',
607  'TitleWord' => 'TitleWord',
608  'Caveat' => 'Caveat',
609  'FileTypes' => 'FileTypes',
610  );
611 
612  return $slTypes;
613 
614  }//end getRecordStringSearchClauseType()
615 
616 
623  public static function getRecordDateRangeSearchClauseType()
624  {
625  $drTypes = Array (
626  'DateClosed' => 'DateClosed',
627  'DateCreated' => 'DateCreated',
628  'DateDue' => 'DateDue',
629  'DateLastMoved' => 'DateLastMoved',
630  'DateModified' => 'DateModified',
631  'DateRegistered' => 'DateRegistered',
632  'DueForReturn' => 'DueForReturn',
633  'RequestedDate' => 'RequestedDate',
634  'DateFinalized' => 'DateFinalized',
635  'RetentionReviewDate' => 'RetentionReviewDate',
636  'DatePublished' => 'DatePublished',
637  'DateLastAction' => 'DateLastAction',
638  'DateUpdated' => 'DateUpdated',
639  );
640 
641  return $drTypes;
642 
643  }//end getRecordDateRangeSearchClauseType()
644 
645 
652  public static function getRecordProperties()
653  {
654  $properties = Array (
655  'recAccessControl' => 'Access Control',
656  'recAccessionNbr' => 'Accession Number',
657  'recActions' => 'All Actions',
658  'recAddresseeLoc' => 'Addressee',
659  'recAllContacts' => 'All Contacts',
660  'recAllHolds' => 'All Holds',
661  'recAllMeetings' => 'Meetings (All)',
662  'recAllParts' => 'All Parts',
663  'recAllVersions' => 'All Versions',
664  'recAltContainer' => 'Alternative Container',
665  'recAltContainers' => 'Alternative Containers',
666  'recAltContents' => 'Alternatively Contains',
667  'recArchiveInterimDate' => 'Date Due for Interim Archival',
668  'recArchiveLocalDate' => 'Date Due for Local Archival',
669  'recArchivePermDate' => 'Date Due for Permanent Archival',
670  'recAuthorLoc' => 'Author',
671  'recAutoPartRule' => 'Automated Part Rule',
672  'recBarcode' => 'TRIM Barcode',
673  'recBlueprintTitle' => 'Blueprint Title',
674  'recCheckedOutOn' => 'Checked Out On',
675  'recCheckedOutPath' => 'Checked Out Path',
676  'recCheckedOutTo' => 'Checked Out To',
677  'recClassification' => 'Record Classification',
678  'recClientLoc' => 'Client',
679  'recConsignment' => 'Consignment',
680  'recContainer' => 'Container',
681  'recContents' => 'Contained Records',
682  'recContainerTitle' => 'Container Title',
683  'recCreatorLoc' => 'Creator',
684  'recCurrDisp' => 'Disposition',
685  'recCurrentAction' => 'Current Action',
686  'recCurrentLoc' => 'Assignee',
687  'recCurrentLocationStatus' => 'Assignee Status',
688  'recCurrentVersion' => 'Latest Finalized Version',
689  'recDateAssigned' => 'Date Assigned',
690  'recDateClosed' => 'Date Closed',
691  'recDateCreated' => 'Date Created',
692  'recDateDue' => 'Date Due',
693  'recDateFinalized' => 'Date Declared As Final',
694  'recDateInactive' => 'Date Inactive',
695  'recDateModified' => 'Date Modified',
696  'recDatePublished' => 'Date Published',
697  'recDateReg' => 'Date Registered',
698  'recDateUpdated' => 'Date Last Updated',
699  'recDestructionDate' => 'Date Due for Destruction',
700  'recDocumentAttachPending' => 'Document Attach is Pending',
701  'recDocumentSize' => 'Size',
702  'recDocumentType' => 'Document Type',
703  'recDOSfile' => 'DOS file',
704  'recEDetails' => 'Document Details',
705  'recEnablerFlags' => 'Flags',
706  'recEnclosed' => 'Enclosed?',
707  'recESource' => 'Source Document',
708  'recEStatus' => 'Edit Status',
709  'recEStore' => 'Document Store',
710  'recEStoreId' => 'Document Store ID',
711  'recExtension' => 'Extension',
712  'recExternalId' => 'External ID',
713  'recFinalizeOnSave' => 'Declare As Final After Saving',
714  'recForeignBarcode' => 'Foreign Barcode',
715  'recFullClassification' => 'Full Classification Number',
716  'recGeneratedTitle' => 'Title (Structured Part)',
717  'recHasHold' => 'Is in one or more Holds',
718  'recHasLinks' => 'Has Links',
719  'recHistory' => 'Audit Events',
720  'recHomeLoc' => 'Home Location',
721  'recHomeLocationStatus' => 'Home Location Status',
722  'recHomeSpace' => 'Home Space',
723  'recIsCheckedOut' => 'Checked Out?',
724  'recIsContainer' => 'Is a Container',
725  'recIsElectronic' => 'Is Electronic',
726  'recIsPart' => 'Is Part',
727  'recIsRoot' => 'Is First Part',
728  'recIsSigned' => 'Signature',
729  'recKeywords' => 'All Thesaurus Terms',
730  'recLastActionDate' => 'Last Action Date',
731  'recLastPartRecord' => 'Latest Part',
732  'recLatestVersion' => 'Latest Version',
733  'recLongNumber' => 'Expanded Number',
734  'recMeeting' => 'Meeting (Most Recent)',
735  'recMovementHistory' => 'Movement History',
736  'recNeedsDataEntryForm' => 'Needs Data Entry Form',
737  'recNextPartRecord' => 'Next Part',
738  'recNextTaskDue' => 'Next Scheduled Task Due',
739  'recNotes' => 'Notes',
740  'recNumber' => 'Record Number',
741  'recNumberRenditions' => 'Number of Renditions',
742  'recOtherLoc' => 'Other Contact',
743  'recOverdueActions' => 'Overdue Actions',
744  'recOwnerLoc' => 'Owner Location',
745  'recPendingDispEvents' => 'Disposition Schedule',
746  'recPreserveHierarchyOnDataEntry' => 'Preserve Hierarchy On Data Entry',
747  'recPrevPartRecord' => 'Previous Part',
748  'recPrimaryContactLoc' => 'Primary Contact',
749  'recPriority' => 'Priority',
750  'recRcClass' => 'Record Class',
751  'recRecordType' => 'Record Type',
752  'recRedactedFrom' => 'Redacted From',
753  'recRedactionsOf' => 'All Redactions',
754  'recRelatedRecord' => 'Related Record',
755  'recRelatedRecs' => 'Related Records',
756  'recRepresentLoc' => 'Representative',
757  'recRequests' => 'All Record Requests',
758  'recRetSchedule' => 'Retention Schedule',
759  'recReturnDueDateTime' => 'Due for Return On',
760  'recReviewDate' => 'Retention Review Date',
761  'recRevisionNumber' => 'Revision Number',
762  'recRootPartRecord' => 'First Part',
763  'recSecurity' => 'Security',
764  'recSeriesRecord' => 'Series Record',
765  'recSuggestedFileName' => 'Suggested File Name',
766  'recTerms' => 'Attached Thesaurus Terms',
767  'recTitle' => 'Title',
768  'recTopActions' => 'Top Actions',
769  'recTypedTitle' => 'Title (Free Text Part)',
770  'recViewPaneId' => 'View Pane ID',
771  );
772 
773  return $properties;
774 
775  }//end getRecordProperties()
776 
777 
784  public static function getRecordSortProperties()
785  {
786  $properties = Array (
787  'Number' => 'Number',
788  'FreeTitle' => 'FreeTitle',
789  'ExternalId' => 'ExternalId',
790  'DateRegistered' => 'DateRegistered',
791  'DateCreated' => 'DateCreated',
792  'DateDue' => 'DateDue',
793  'DateClosed' => 'DateClosed',
794  'Priority' => 'Priority',
795  'Consignment' => 'Consignment',
796  'DateInactive' => 'DateInactive',
797  'DateLastAction' => 'DateLastAction',
798  'RecordType' => 'RecordType',
799  'StructuredTitle' => 'StructuredTitle',
800  'DateFinalized' => 'DateFinalized',
801  'DatePublished' => 'DatePublished',
802  );
803 
804  return $properties;
805 
806  }//end getRecordSortProperties()
807 
814  public static function getSecurityLevel()
815  {
816  $slvl = Array (
817  'Public' => 'Public',
818  'Unrestricted' => 'Unrestricted',
819  'In-Confidence' => 'In-Confidence',
820  'Protected' => 'Protected',
821  'Highly Protected' => 'Highly Protected',
822  );
823  return $slvl;
824 
825  }//end getSecurityLevel()
826 
827 
834  public static function getDefaultRecordSearchOptions()
835  {
836  $record_search_options = Array();
837  $record_search_options['limit'] = '30';
838  $record_search_options['file_types'] = '';
839  $record_search_options['sort_1'] = 'None';
840  $record_search_options['sort_2'] = 'None';
841  $record_search_options['sort_3'] = 'None';
842  $record_search_options['sort_1_descending'] = FALSE;
843  $record_search_options['sort_2_descending'] = FALSE;
844  $record_search_options['sort_3_descending'] = FALSE;
845  $record_search_options['record_type_filter'] = '';
846 
847  return $record_search_options;
848 
849  }//end getDefaultRecordSearchOptions()
850 
859  public static function getPackagedFiles($object_list, $hash_key, &$final_file_path)
860  {
861  if (!isset($object_list['error'])) {
862  $file_content = base64_decode($object_list['Base64Payload']);
863  $file_name = $object_list['AttachmentId'].'.'.strtolower($object_list['FileType']);
864 
865  @mkdir(SQ_TEMP_PATH.'/'.$hash_key);
866  $file_path = SQ_TEMP_PATH.'/'.$hash_key.'/'.$file_name;
867  $file_path = $final_file_path.'/'.$file_name;
868  if (file_exists($file_path)) {
869  // Try to unlink it
870  unlink($file_path);
871  }//end if
872  if (!$handle = fopen($file_path, 'w')) {
873  log_dump('Could not open file '.$file_path);
874  }//end if
875  if (fwrite($handle, $file_content) === FALSE) {
876  log_dump('Could not write to file '.$file_path);
877  }//end if
878  fclose($handle);
879 
880  return TRUE;
881  } else {
882  return FALSE;
883  }//end else
884 
885  }//end getPackagedFiles($object_list)
886 
887 
898  public static function executeRequest($connection, $operations=Array(), $display_type='Fetch', $search_clause_xml = '', $hash_key='', &$final_file_path='')
899  {
900  // TRIM API call usually takes a while to search records
901  $default_socket_timeout = ini_get('default_socket_timeout');
902  ini_set('default_socket_timeout', -1);
903 
904  $Execute = Array (
905  'req' => self::constructTrimRequest(),
906  );
907  $Execute['req'] = array_merge($Execute['req'], $operations);
908 
909  $object_list = Array(); // Result List
910 
911  // Get the soapclient object.
912  $client = new SoapClient($connection['wsdl'], $connection['authentication']);
913 
914  // We need to convert the TRIM Execute object to XML, so we can swap the elements.
915 
916  try {
917  $xml = $client->ConvertToXml($Execute);
918  } catch (SoapFault $e) {
919  $error_message = "Failed to ConvertToXml: ".$e->getMessage()."\n";
920  if (SQ_PHP_CLI || SQ_IN_CRON) {
921  $error_message .= "Last Request:\n".$client->__getLastRequest()."\n";
922  $error_message .= "Last Response:\n".$client->__getLastResponse()."\n";
923  }
924  trigger_error($error_message, E_USER_WARNING);
925  return Array();
926  }
927 
928 
929  // Now we have to swap Fetch and RecordSearch element, because RecordSearch element has to
930  // come first in the soap request, otherwise TRIM wont know where the Fetch object should get the
931  // result from.
932  $xml = $xml->ConvertToXmlResult;
933  if (isset($operations['Fetch']) && isset($operations['RecordSearch'])) {
934  $xml = self::SwapElement($xml, 'RecordSearch', 'Fetch');
935  // Add grouped search clauses xml to the converted xml
936  if(!empty($search_clause_xml)) {
937  $xml = preg_replace('/<\/RecordSearch>/', $search_clause_xml.'</RecordSearch>', $xml);
938  }
939  } else if (isset($operations['Download']) && isset($operations['ShortcutRecordUri'])) {
940  $xml = self::SwapElement($xml, 'ShortcutRecordUri', 'Download');
941  }
942  else if (isset($operations['Fetch']) && isset($operations['ClassificationStringSelect'])) {
943  $xml = self::SwapElement($xml, 'ClassificationStringSelect', 'Fetch');
944  }
945  else if (isset($operations['Fetch']) && isset($operations['RecordTypeUrisSelect'])) {
946  $xml = self::SwapElement($xml, 'RecordTypeUrisSelect', 'Fetch');
947  }
948  else if (isset($operations['Fetch']) && isset($operations['RecordTypeSimpleSelect'])) {
949  $xml = self::SwapElement($xml, 'RecordTypeSimpleSelect', 'Fetch');
950  }
951 
952 
953  // Now its time to get the data
954  try {
955  $data = $client->ExecuteXml(Array('xml' => $xml));
956  } catch (SoapFault $e) {
957  $error_message = "Failed to ExecuteXml: ".$e->getMessage()."\n";
958  if (SQ_PHP_CLI || SQ_IN_CRON) {
959  $error_message .= "Last Request:\n".$client->__getLastRequest()."\n";
960  $error_message .= "Last Response:\n".$client->__getLastResponse()."\n";
961  }
962  trigger_error($error_message, E_USER_WARNING);
963  return Array();
964  }
965 
966  // The data returned is in XML format, so we have to parse it.
967  $object_list = self::parseTRIMData($data->ExecuteXmlResult, $display_type);
968 
969  if (isset($operations['Fetch'])) {
970  return $object_list;
971  } else if (isset($operations['Download'])) {
972  $res = self::getPackagedFiles($object_list, $hash_key, $final_file_path);
973  } else if (isset($operations['Upload'])) {
974  if ($object_list['UploadId'] != $up_id) {
975  echo 'Upload Successful, new UploadId: '.$object_list['UploadId'];
976  }
977  }//end else if
978 
979  ini_set('default_socket_timeout', $default_socket_timeout);
980 
981  }//end execute
982 
983 
992  public static function executeGetAssigneeRequest($connection, $assignee_name)
993  {
994  $Execute = Array (
995  'req' => self::constructTrimRequest(),
996  );
997 
998  // BEGIN GET ASSIGNEE URI
999  $LocationStringSelect = Array (
1000  'Arg' => $assignee_name,
1001  'HideActiveLocations' => FALSE,
1002  'HideInactiveLocations' => FALSE,
1003  'HideInternalLocations' => FALSE,
1004  'HideExternalLocations' => FALSE,
1005  'HideAdhocLocations' => FALSE,
1006  'TargetForUpdate' => FALSE,
1007  'IsForUpdate' => FALSE,
1008  'Limit' => 1,
1009  );
1010  // END GET ASSIGNEE URI
1011  $FetchInjectionUriLocationId = Array (
1012  'Id' => 'LocationId',
1013  'TargetForUpdate' => FALSE,
1014  );
1015 
1016  $Execute['req']['FetchInjectionUri'] = $FetchInjectionUriLocationId;
1017  $Execute['req']['LocationStringSelect'] = $LocationStringSelect;
1018 
1019  $client = new SoapClient($connection['wsdl'], $connection['authentication']);
1020 
1021  try {
1022  $xml = $client->ConvertToXml($Execute);
1023  } catch (SoapFault $e) {
1024  $error_message = "Failed to ConvertToXml: ".$e->getMessage()."\n";
1025  if (SQ_PHP_CLI || SQ_IN_CRON) {
1026  $error_message .= "Last Request:\n".$client->__getLastRequest()."\n";
1027  $error_message .= "Last Response:\n".$client->__getLastResponse()."\n";
1028  }
1029  trigger_error($error_message, E_USER_WARNING);
1030  return 0;
1031  }
1032  $xml = self::SwapElement($xml->ConvertToXmlResult, 'LocationStringSelect', 'FetchInjectionUri');
1033 
1034  try {
1035  $data = $client->ExecuteXml(Array('xml' => $xml));
1036  } catch (SoapFault $e) {
1037  $error_message = "Failed to ExecuteXml: ".$e->getMessage()."\n";
1038  if (SQ_PHP_CLI || SQ_IN_CRON) {
1039  $error_message .= "Last Request:\n".$client->__getLastRequest()."\n";
1040  $error_message .= "Last Response:\n".$client->__getLastResponse()."\n";
1041  }
1042  trigger_error($error_message, E_USER_WARNING);
1043  return 0;
1044  }
1045 
1046  $return_xml = new SimpleXMLElement((string) $data->ExecuteXmlResult);
1047  $trim_userid = 0;
1048  if ((string) $return_xml->SearchResult->FoundCount > 0) {
1049  $trim_userid = (string) $return_xml->FetchInjectionUriResult->Uri;
1050  }//end if
1051 
1052  return $trim_userid;
1053 
1054  }//end executeGetAssigneeRequest()
1055 
1056 
1074  public static function executeCreateRequest($connection, $username, $new_record_info)
1075  {
1076  // Check Whether the username exists in TRIM
1077  $trim_userid = self::executeGetAssigneeRequest($connection, $username);
1078 
1079  if (!$trim_userid) {
1080  trigger_error('There is no such user with username "'.$username.'" in TRIM. Record creation aborted.', E_USER_WARNING);
1081  return FALSE;
1082  }//end if
1083 
1084 
1085  // BEGIN GET RECORD TYPE "document" URI
1086  $RecordTypeStringSelect = Array (
1087  'Arg' => $new_record_info['record_type'],
1088  'Id' => 'recordtype',
1089  'TargetForUpdate' => FALSE,
1090  'IsForUpdate' => FALSE,
1091  'Limit' => 10,
1092  'BehaviorFilter' => ucwords($new_record_info['record_type']),
1093  'TypeFilter' => 'All',
1094  );
1095 
1096  $FetchInjectionUriRecordType = Array (
1097  'Id' => 'recordtype',
1098  'TargetForUpdate' => FALSE,
1099  );
1100  // END GET RECORD TYPE "document" URI
1101 
1102  $parent_record_uri = self::getURIFromRecordNumber($connection, $new_record_info['parent_record_number']);
1103 
1104  if (!$parent_record_uri) {
1105  trigger_error('There is no URI correspond to record number "'.$new_record_info['parent_record_number'].'" in TRIM. Record creation aborted.', E_USER_WARNING);
1106  return FALSE;
1107  }//end if
1108 
1109  $operations = Array (
1110  'Create' => self::constructCreate($parent_record_uri, $new_record_info['record_title'], $trim_userid, $new_record_info['security_level'], $new_record_info['additional_fields']),
1111  );
1112 
1113  $data = NULL;
1114  try {
1115  $Execute = Array (
1116  'req' => self::constructTrimRequest(),
1117  );
1118 
1119  $Execute['req'] = array_merge($Execute['req'], $operations);
1120  $Execute['req']['RecordTypeStringSelect'] = $RecordTypeStringSelect;
1121  $Execute['req']['FetchInjectionUri'] = $FetchInjectionUriRecordType;
1122 
1123  $client = new SoapClient($connection['wsdl'], $connection['authentication']);
1124 
1125  $xml = $client->ConvertToXml($Execute);
1126  $xml = self::SwapElement($xml->ConvertToXmlResult, 'RecordTypeStringSelect', 'Create');
1127  $xml = self::SwapElement($xml, 'FetchInjectionUri', 'Create');
1128 
1129  $data = $client->ExecuteXml(Array('xml' => $xml));
1130 
1131 
1132  } catch (SoapFault $e) {
1133  $error_message = "Create Request Failed: ".$e->getMessage()."\n";
1134  if (SQ_PHP_CLI || SQ_IN_CRON) {
1135  $error_message .= "Last Request:\n".$client->__getLastRequest()."\n";
1136  $error_message .= "Last Response:\n".$client->__getLastResponse()."\n";
1137  }
1138  trigger_error($error_message, E_USER_WARNING);
1139  return 0;
1140  }//end try catch
1141 
1142  if(isset($data->error) && !empty ($data->error)) {
1143  trigger_error($data->error, E_USER_WARNING);
1144  return 0;
1145  }
1146 
1147  $new_record_uri = 0;
1148  if (!empty($data)) {
1149  $return_xml = new SimpleXMLElement((string) $data->ExecuteXmlResult);
1150  if (isset($return_xml->ErrorResult)) {
1151  // We have an error, the submission has not been successful
1152  trigger_error('Unable to create new record. '.(string) $return_xml->ErrorResult->Message.')');
1153  } else {
1154  $new_record_uri = (string) $return_xml->FetchResult->Objects->TrimObject->attributes()->Uri;
1155  }//end else
1156  }//end if
1157 
1158  return $new_record_uri;
1159 
1160  }//end executeCreateRequest
1161 
1162 
1171  private static function downloadFile($object_list)
1172  {
1173  if (!isset($object_list['error'])) {
1174  $file_content = base64_decode($object_list['Base64Payload']);
1175  $file_name = $object_list['AttachmentId'].'.'.strtolower($object_list['FileType']);
1176  $file_path = '/home/hnguyen/matrix_installs/matrix_dev/data/temp/'.$file_name;
1177 
1178  if (!$handle = fopen($file_path, 'w')) {
1179  log_dump('Could not open file '.$file_path);
1180  exit;
1181  }//end if
1182  if (fwrite($handle, $file_content) === FALSE) {
1183  log_dump('Could not write to file '.$file_path);
1184  exit;
1185  }//end if
1186  fclose($handle);
1187 
1188  // We'll be outputting a file
1189  header('Content-type: application/'.strtolower($object_list['FileType']));
1190 
1191  // Set the name of the file
1192  header("Content-Disposition: attachment; filename=$file_name");
1193 
1194  // Get the source file
1195  readfile($file_path);
1196 
1197  // Then remove the source file.
1198  unlink($file_path);
1199 
1200  } else {
1201  log_dump($object_list['error']);
1202  }//end if else
1203 
1204  }//end downloadFile()
1205 
1206 
1219  public static function SwapElement($xml, $ele_1, $ele_2)
1220  {
1221  // if element 1 comes prior to element 2, there is no need to swap
1222  if(strpos($xml, '<'.$ele_1) < strpos($xml, '<'.$ele_2)) return $xml;
1223 
1224  $match_1 = Array();
1225  preg_match('/<'.$ele_1.'>([^"]*)<\/'.$ele_1.'>/', $xml, $match_1);
1226  $part_1 = $match_1[0];
1227 
1228  $match_2 = Array();
1229  preg_match('/<'.$ele_2.'>([^"]*)<\/'.$ele_2.'>/', $xml, $match_2);
1230  $part_2 = $match_2[0];
1231 
1232  $xml = preg_replace('/<'.$ele_1.'>([^"]*)<\/'.$ele_1.'>/', 'PART_2', $xml);
1233  $xml = preg_replace('/<'.$ele_2.'>([^"]*)<\/'.$ele_2.'>/', 'PART_1', $xml);
1234 
1235  $xml = str_replace('PART_2', $part_2, $xml);
1236  $xml = str_replace('PART_1', $part_1, $xml);
1237 
1238  return $xml;
1239 
1240  }//end SwapElement
1241 
1242 
1252  private static function parseTRIMData($data, $operation='Fetch')
1253  {
1254  $object_list = Array();
1255  $xml = new SimpleXMLElement($data);
1256 
1257  if ($operation == 'Fetch') {
1258  if($xml->SearchResult->FoundCount == 0) return $object_list;
1259  $i = 0;
1260  foreach ($xml->FetchResult->Objects->TrimObject as $object) {
1261  $uri = NULL;
1262  $version = NULL;
1263  foreach ($object->attributes() as $name => $attr_value) {
1264  if ((string)$name == 'Uri') $uri = (string)$attr_value;
1265  else if ($name = 'Version') {
1266  $version = (string) $attr_value;
1267  }
1268  }//end foreach
1269 
1270  $object_list[$i] = Array (
1271  'Uri' => $uri,
1272  'Version' => $version,
1273  );
1274 
1275  foreach ($object->Values->Value as $value) {
1276  $attr_type = NULL;
1277  $attr_val = NULL;
1278  $error = NULL;
1279  foreach ($value->attributes() as $name => $attr_value) {
1280  if ((string)$name == 'Name') {
1281  $attr_type = (string)$attr_value;
1282  }//end if
1283  if ((string)$name == 'ErrorMessage') {
1284  $error = (string)$attr_value;
1285  }//end if
1286  if ((string)$name == 'Val') {
1287  $attr_val = (string) $attr_value;
1288  }//end if
1289  }//end foreach
1290  if (!empty($error) && $error !== "false") {
1291  $object_list[$i]['error'] = $error;
1292  $object_list[$i]['FoundCount'] = (string) $xml->SearchResult->FoundCount;
1293  }
1294  $object_list[$i][$attr_type] = $attr_val;
1295  }//end foreach
1296  $i++;
1297  }//end foreach
1298  } else if ($operation == 'Download') {
1299  if (isset($xml->ErrorResult)) {
1300  $object_list['error'] = (string)$xml->ErrorResult->Message;
1301  } else {
1302  foreach ($xml->DownloadResult->children() as $key => $value) {
1303  $object_list[$key] = (string)$value;
1304  }//end foreach
1305  }
1306  } else if ($operation == 'Upload') {
1307  if (isset($xml->ErrorResult)) {
1308  $object_list['error'] = (string)$xml->ErrorResult->Message;
1309  } else {
1310  foreach ($xml->UploadResult->children() as $key => $value) {
1311  $object_list[$key] = (string)$value;
1312  }//end foreach
1313  }
1314  }
1315 
1316  return $object_list;
1317 
1318  }//end parseTRIMData()
1319 
1320 
1327  public static function test_trim_connection($connection)
1328  {
1329 
1330  //test a cURL connection first otherwise the Soap constructor will crash and leave edit interfaces inaccessible or cause Cron deadlocks.
1331  if (!isset($connection['authentication']['proxy_login'])){ //TODO: test cURL with proxies involved
1332  $options = array(
1333  'FOLLOWLOCATION' => 1,
1334  'RETURNTRANSFER' => 1,
1335  'HTTPAUTH' => CURLAUTH_BASIC,
1336  'TIMEOUT' => 5,
1337  'USERPWD' => $connection['authentication']['login'].":".$connection['authentication']['password'],
1338  );
1339  $details = fetch_url($connection['wsdl'], $options, array(), FALSE);
1340  $contents = $details['response'];
1341  if (strpos($contents, '?xml') === FALSE) return FALSE;
1342  }
1343 
1344  //check if recTitle property is set
1345  try {
1346  $client = new SoapClient($connection['wsdl'], $connection['authentication']);
1347 
1348  $data = $client->ListProperty(Array('PropertyName' => 'recTitle'));
1349 
1350  $name = (string) $data->ListPropertyResult->Name;
1351 
1352  if ($name == 'recTitle') {
1353  return TRUE;
1354  } else {
1355  return FALSE;
1356  }//end else
1357  } catch (SoapFault $e) {
1358  echo $e->getMessage();
1359  return FALSE;
1360  }//end if
1361 
1362  }//end test_trim_connection()
1363 
1374  public static function getRecordPropertiesFromUri($connection, $uri, $record_properties)
1375  {
1376  $operations = Array (
1377  'Fetch' => self::constructFetch($record_properties, 1),
1378  'ShortcutRecordUri' => self::constructShortcutRecordUri($uri),
1379  );
1380  $object_list = self::executeRequest($connection, $operations, 'Fetch');
1381 
1382  return $object_list;
1383  }
1384 
1385 }//end class