2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
38 require_once('soap/SoapHelperFunctions.php');
39 require_once('soap/SoapTypes.php');
41 /*************************************************************************************
43 THIS IS FOR SUGARCRM USERS
46 *************************************************************************************/
47 $disable_date_format = true;
51 array('session'=>'xsd:string'),
52 array('return'=>'xsd:int'),
56 * Return if the user is an admin or not
58 * @param String $session -- Session ID returned by a previous call to login.
59 * @return int 1 or 0 depending on if the user is an admin
61 function is_user_admin($session){
62 if(validate_authenticated($session)){
64 return is_admin($current_user);
74 array('user_auth'=>'tns:user_auth', 'application_name'=>'xsd:string'),
75 array('return'=>'tns:set_entry_result'),
79 * Log the user into the application
81 * @param UserAuth array $user_auth -- Set user_name and password (password needs to be
82 * in the right encoding for the type of authentication the user is setup for. For Base
83 * sugar validation, password is the MD5 sum of the plain text password.
84 * @param String $application -- The name of the application you are logging in from. (Currently unused).
85 * @return Array(session_id, error) -- session_id is the id of the session that was
86 * created. Error is set if there was any error during creation.
88 function login($user_auth, $application){
89 global $sugar_config, $system_config;
91 $error = new SoapError();
95 $system_config = new Administration();
96 $system_config->retrieveSettings('system');
97 $authController = new AuthenticationController((!empty($sugar_config['authenticationClass'])? $sugar_config['authenticationClass'] : 'SugarAuthenticate'));
99 $isLoginSuccess = $authController->login($user_auth['user_name'], $user_auth['password'], array('passwordEncrypted' => true));
100 $usr_id=$user->retrieve_user_id($user_auth['user_name']);
102 $user->retrieve($usr_id);
105 if ($isLoginSuccess) {
106 if ($_SESSION['hasExpiredPassword'] =='1') {
107 $error->set_error('password_expired');
108 $GLOBALS['log']->fatal('password expired for user ' . $user_auth['user_name']);
109 LogicHook::initialize();
110 $GLOBALS['logic_hook']->call_custom_logic('Users', 'login_failed');
111 return array('id'=>-1, 'error'=>$error);
113 if(!empty($user) && !empty($user->id) && !$user->is_group) {
115 global $current_user;
116 $current_user = $user;
118 } else if($usr_id && isset($user->user_name) && ($user->getPreference('lockout') == '1')) {
119 $error->set_error('lockout_reached');
120 $GLOBALS['log']->fatal('Lockout reached for user ' . $user_auth['user_name']);
121 LogicHook::initialize();
122 $GLOBALS['logic_hook']->call_custom_logic('Users', 'login_failed');
123 return array('id'=>-1, 'error'=>$error);
124 } else if(function_exists('mcrypt_cbc')){
125 $password = decrypt_string($user_auth['password']);
126 $authController = new AuthenticationController((!empty($sugar_config['authenticationClass'])? $sugar_config['authenticationClass'] : 'SugarAuthenticate'));
127 if($authController->login($user_auth['user_name'], $password) && isset($_SESSION['authenticated_user_id'])){
134 global $current_user;
135 //$current_user = $user;
137 $current_user->loadPreferences();
138 $_SESSION['is_valid_session']= true;
139 $_SESSION['ip_address'] = query_client_ip();
140 $_SESSION['user_id'] = $current_user->id;
141 $_SESSION['type'] = 'user';
142 $_SESSION['avail_modules']= get_user_module_list($current_user);
143 $_SESSION['authenticated_user_id'] = $current_user->id;
144 $_SESSION['unique_key'] = $sugar_config['unique_key'];
146 $current_user->call_custom_logic('after_login');
147 return array('id'=>session_id(), 'error'=>$error);
149 $error->set_error('invalid_login');
150 $GLOBALS['log']->fatal('SECURITY: User authentication for '. $user_auth['user_name']. ' failed');
151 LogicHook::initialize();
152 $GLOBALS['logic_hook']->call_custom_logic('Users', 'login_failed');
153 return array('id'=>-1, 'error'=>$error);
157 //checks if the soap server and client are running on the same machine
161 array('return'=>'xsd:int'),
165 * Check to see if the soap server and client are on the same machine.
166 * We don't allow a server to sync to itself.
168 * @return true -- if the SOAP server and client are on the same machine
169 * @return false -- if the SOAP server and client are not on the same machine.
171 function is_loopback(){
172 if(query_client_ip() == $_SERVER['SERVER_ADDR'])
178 * Validate the provided session information is correct and current. Load the session.
180 * @param String $session_id -- The session ID that was returned by a call to login.
181 * @return true -- If the session is valid and loaded.
182 * @return false -- if the session is not valid.
184 function validate_authenticated($session_id){
185 if(!empty($session_id)){
186 session_id($session_id);
189 if(!empty($_SESSION['is_valid_session']) && is_valid_ip_address('ip_address') && $_SESSION['type'] == 'user'){
191 global $current_user;
193 $current_user = new User();
194 $current_user->retrieve($_SESSION['user_id']);
201 LogicHook::initialize();
202 $GLOBALS['log']->fatal('SECURITY: The session ID is invalid');
203 $GLOBALS['logic_hook']->call_custom_logic('Users', 'login_failed');
208 * Use the same logic as in SugarAuthenticate to validate the ip address
210 * @param string $session_var
211 * @return bool - true if the ip address is valid, false otherwise.
213 function is_valid_ip_address($session_var){
214 global $sugar_config;
215 // grab client ip address
216 $clientIP = query_client_ip();
218 // check to see if config entry is present, if not, verify client ip
219 if (!isset ($sugar_config['verify_client_ip']) || $sugar_config['verify_client_ip'] == true) {
220 // check to see if we've got a current ip address in $_SESSION
221 // and check to see if the session has been hijacked by a foreign ip
222 if (isset ($_SESSION[$session_var])) {
223 $session_parts = explode(".", $_SESSION[$session_var]);
224 $client_parts = explode(".", $clientIP);
225 if(count($session_parts) < 4) {
228 // match class C IP addresses
229 for ($i = 0; $i < 3; $i ++) {
230 if ($session_parts[$i] == $client_parts[$i]) {
239 // we have a different IP address
240 if ($_SESSION[$session_var] != $clientIP && empty ($classCheck)) {
241 $GLOBALS['log']->fatal("IP Address mismatch: SESSION IP: {$_SESSION[$session_var]} CLIENT IP: {$clientIP}");
253 array('session'=>'xsd:string'),
254 array('return'=>'xsd:int'),
258 * Perform a seamless login. This is used internally during the sync process.
260 * @param String $session -- Session ID returned by a previous call to login.
261 * @return true -- if the session was authenticated
262 * @return false -- if the session could not be authenticated
264 function seamless_login($session){
265 if(!validate_authenticated($session)){
268 $_SESSION['seamless_login'] = true;
274 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'query'=>'xsd:string', 'order_by'=>'xsd:string','offset'=>'xsd:int', 'select_fields'=>'tns:select_fields', 'max_results'=>'xsd:int', 'deleted'=>'xsd:int'),
275 array('return'=>'tns:get_entry_list_result'),
279 * Retrieve a list of beans. This is the primary method for getting list of SugarBeans from Sugar using the SOAP API.
281 * @param String $session -- Session ID returned by a previous call to login.
282 * @param String $module_name -- The name of the module to return records from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
283 * @param String $query -- SQL where clause without the word 'where'
284 * @param String $order_by -- SQL order by clause without the phrase 'order by'
285 * @param String $offset -- The record offset to start from.
286 * @param Array $select_fields -- A list of the fields to be included in the results. This optional parameter allows for only needed fields to be retrieved.
287 * @param String $max_results -- The maximum number of records to return. The default is the sugar configuration value for 'list_max_entries_per_page'
288 * @param Number $deleted -- false if deleted records should not be include, true if deleted records should be included.
289 * @return Array 'result_count' -- The number of records returned
290 * 'next_offset' -- The start of the next page (This will always be the previous offset plus the number of rows returned. It does not indicate if there is additional data unless you calculate that the next_offset happens to be closer than it should be.
291 * 'field_list' -- The vardef information on the selected fields.
292 * Array -- 'field'=> 'name' -- the name of the field
293 * 'type' -- the data type of the field
294 * 'label' -- the translation key for the label of the field
295 * 'required' -- Is the field required?
296 * 'options' -- Possible values for a drop down field
297 * 'entry_list' -- The records that were retrieved
298 * 'error' -- The SOAP error, if any
300 function get_entry_list($session, $module_name, $query, $order_by,$offset, $select_fields, $max_results, $deleted ){
301 global $beanList, $beanFiles;
302 $error = new SoapError();
303 if(!validate_authenticated($session)){
304 $error->set_error('invalid_login');
305 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
308 if($module_name == 'CampaignProspects'){
309 $module_name = 'Prospects';
312 if(empty($beanList[$module_name])){
313 $error->set_error('no_module');
314 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
316 global $current_user;
317 if(!check_modules_access($current_user, $module_name, 'read')){
318 $error->set_error('no_access');
319 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
322 // If the maximum number of entries per page was specified, override the configuration value.
323 if($max_results > 0){
324 global $sugar_config;
325 $sugar_config['list_max_entries_per_page'] = $max_results;
329 $class_name = $beanList[$module_name];
330 require_once($beanFiles[$class_name]);
331 $seed = new $class_name();
332 if(! ($seed->ACLAccess('Export') && $seed->ACLAccess('list')))
334 $error->set_error('no_access');
335 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
340 if($offset == '' || $offset == -1){
344 $response = $seed->retrieveTargetList($query, $select_fields, $offset,-1,-1,$deleted);
346 $response = $seed->get_list($order_by, $query, $offset,-1,-1,$deleted,true);
348 $list = $response['list'];
351 $output_list = array();
353 // retrieve the vardef information on the bean's fields.
354 $field_list = array();
355 foreach($list as $value)
357 if(isset($value->emailAddress)){
358 $value->emailAddress->handleLegacyRetrieve($value);
360 $value->fill_in_additional_detail_fields();
361 $output_list[] = get_return_value($value, $module_name);
362 if(empty($field_list)){
363 $field_list = get_field_list($value);
367 // Filter the search results to only include the requested fields.
368 $output_list = filter_return_list($output_list, $select_fields, $module_name);
370 // Filter the list of fields to only include information on the requested fields.
371 $field_list = filter_return_list($field_list,$select_fields, $module_name);
373 // Calculate the offset for the start of the next page
374 $next_offset = $offset + sizeof($output_list);
376 return array('result_count'=>sizeof($output_list), 'next_offset'=>$next_offset,'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
381 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'id'=>'xsd:string', 'select_fields'=>'tns:select_fields'),
382 array('return'=>'tns:get_entry_result'),
386 * Retrieve a single SugarBean based on ID.
388 * @param String $session -- Session ID returned by a previous call to login.
389 * @param String $module_name -- The name of the module to return records from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
390 * @param String $id -- The SugarBean's ID value.
391 * @param Array $select_fields -- A list of the fields to be included in the results. This optional parameter allows for only needed fields to be retrieved.
394 function get_entry($session, $module_name, $id,$select_fields ){
395 return get_entries($session, $module_name, array($id), $select_fields);
400 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'ids'=>'tns:select_fields', 'select_fields'=>'tns:select_fields'),
401 array('return'=>'tns:get_entry_result'),
405 * Retrieve a list of SugarBean's based on provided IDs.
407 * @param String $session -- Session ID returned by a previous call to login.
408 * @param String $module_name -- The name of the module to return records from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
409 * @param Array $ids -- An array of SugarBean IDs.
410 * @param Array $select_fields -- A list of the fields to be included in the results. This optional parameter allows for only needed fields to be retrieved.
411 * @return Array 'field_list' -- Var def information about the returned fields
412 * 'entry_list' -- The records that were retrieved
413 * 'error' -- The SOAP error, if any
415 function get_entries($session, $module_name, $ids,$select_fields ){
416 global $beanList, $beanFiles;
417 $error = new SoapError();
418 $field_list = array();
419 $output_list = array();
420 if(!validate_authenticated($session)){
421 $error->set_error('invalid_login');
422 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
425 if($module_name == 'CampaignProspects'){
426 $module_name = 'Prospects';
429 if(empty($beanList[$module_name])){
430 $error->set_error('no_module');
431 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
433 global $current_user;
434 if(!check_modules_access($current_user, $module_name, 'read')){
435 $error->set_error('no_access');
436 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
439 $class_name = $beanList[$module_name];
440 require_once($beanFiles[$class_name]);
442 //todo can modify in there to call bean->get_list($order_by, $where, 0, -1, -1, $deleted);
443 //that way we do not have to call retrieve for each bean
444 //perhaps also add a select_fields to this, so we only get the fields we need
445 //and not do a select *
446 foreach($ids as $id){
447 $seed = new $class_name();
450 $seed = $seed->retrieveTarget($id);
452 if ($seed->retrieve($id) == null)
456 if ($seed->deleted == 1) {
458 $list[] = array('name'=>'warning', 'value'=>'Access to this object is denied since it has been deleted or does not exist');
459 $list[] = array('name'=>'deleted', 'value'=>'1');
460 $output_list[] = Array('id'=>$id,
461 'module_name'=> $module_name,
462 'name_value_list'=>$list,
466 if(! $seed->ACLAccess('DetailView')){
467 $error->set_error('no_access');
468 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
470 $output_list[] = get_return_value($seed, $module_name);
472 if(empty($field_list)){
473 $field_list = get_field_list($seed);
478 $output_list = filter_return_list($output_list, $select_fields, $module_name);
479 $field_list = filter_field_list($field_list,$select_fields, $module_name);
481 return array( 'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
486 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'name_value_list'=>'tns:name_value_list'),
487 array('return'=>'tns:set_entry_result'),
491 * Update or create a single SugarBean.
493 * @param String $session -- Session ID returned by a previous call to login.
494 * @param String $module_name -- The name of the module to return records from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
495 * @param Array $name_value_list -- The keys of the array are the SugarBean attributes, the values of the array are the values the attributes should have.
496 * @return Array 'id' -- the ID of the bean that was written to (-1 on error)
497 * 'error' -- The SOAP error if any.
499 function set_entry($session,$module_name, $name_value_list){
500 global $beanList, $beanFiles;
502 $error = new SoapError();
503 if(!validate_authenticated($session)){
504 $error->set_error('invalid_login');
505 return array('id'=>-1, 'error'=>$error->get_soap_array());
507 if(empty($beanList[$module_name])){
508 $error->set_error('no_module');
509 return array('id'=>-1, 'error'=>$error->get_soap_array());
511 global $current_user;
512 if(!check_modules_access($current_user, $module_name, 'write')){
513 $error->set_error('no_access');
514 return array('id'=>-1, 'error'=>$error->get_soap_array());
517 $class_name = $beanList[$module_name];
518 require_once($beanFiles[$class_name]);
519 $seed = new $class_name();
521 foreach($name_value_list as $value){
522 if($value['name'] == 'id'){
523 $seed->retrieve($value['value']);
527 foreach($name_value_list as $value){
528 $GLOBALS['log']->debug($value['name']." : ".$value['value']);
529 $seed->$value['name'] = $value['value'];
531 if(! $seed->ACLAccess('Save') || ($seed->deleted == 1 && !$seed->ACLAccess('Delete')))
533 $error->set_error('no_access');
534 return array('id'=>-1, 'error'=>$error->get_soap_array());
537 if($seed->deleted == 1){
538 $seed->mark_deleted($seed->id);
540 return array('id'=>$seed->id, 'error'=>$error->get_soap_array());
546 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'name_value_lists'=>'tns:name_value_lists'),
547 array('return'=>'tns:set_entries_result'),
551 * Update or create a list of SugarBeans
553 * @param String $session -- Session ID returned by a previous call to login.
554 * @param String $module_name -- The name of the module to return records from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
555 * @param Array $name_value_lists -- Array of Bean specific Arrays where the keys of the array are the SugarBean attributes, the values of the array are the values the attributes should have.
556 * @return Array 'ids' -- Array of the IDs of the beans that was written to (-1 on error)
557 * 'error' -- The SOAP error if any.
559 function set_entries($session,$module_name, $name_value_lists){
560 $error = new SoapError();
562 if(!validate_authenticated($session)){
563 $error->set_error('invalid_login');
567 'error' => $error->get_soap_array()
571 return handle_set_entries($module_name, $name_value_lists, FALSE);
578 'set_note_attachment',
579 array('session'=>'xsd:string','note'=>'tns:note_attachment'),
580 array('return'=>'tns:set_entry_result'),
584 * Add or replace the attachment on a Note.
586 * @param String $session -- Session ID returned by a previous call to login.
587 * @param Binary $note -- The flie contents of the attachment.
588 * @return Array 'id' -- The ID of the new note or -1 on error
589 * 'error' -- The SOAP error if any.
591 function set_note_attachment($session,$note)
594 $error = new SoapError();
595 if(!validate_authenticated($session)){
596 $error->set_error('invalid_login');
597 return array('id'=>-1, 'error'=>$error->get_soap_array());
600 require_once('modules/Notes/NoteSoap.php');
601 $ns = new NoteSoap();
602 return array('id'=>$ns->saveFile($note), 'error'=>$error->get_soap_array());
607 'get_note_attachment',
608 array('session'=>'xsd:string', 'id'=>'xsd:string'),
609 array('return'=>'tns:return_note_attachment'),
613 * Retrieve an attachment from a note
614 * @param String $session -- Session ID returned by a previous call to login.
615 * @param Binary $note -- The flie contents of the attachment.
616 * @return Array 'id' -- The ID of the new note or -1 on error
617 * 'error' -- The SOAP error if any.
619 * @param String $session -- Session ID returned by a previous call to login.
620 * @param String $id -- The ID of the appropriate Note.
621 * @return Array 'note_attachment' -- Array String 'id' -- The ID of the Note containing the attachment
622 * String 'filename' -- The file name of the attachment
623 * Binary 'file' -- The binary contents of the file.
624 * 'error' -- The SOAP error if any.
626 function get_note_attachment($session,$id)
628 $error = new SoapError();
629 if(!validate_authenticated($session)){
630 $error->set_error('invalid_login');
631 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
636 $note->retrieve($id);
637 if(!$note->ACLAccess('DetailView')){
638 $error->set_error('no_access');
639 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
641 require_once('modules/Notes/NoteSoap.php');
642 $ns = new NoteSoap();
643 if(!isset($note->filename)){
644 $note->filename = '';
646 $file= $ns->retrieveFile($id,$note->filename);
648 $error->set_error('no_file');
652 return array('note_attachment'=>array('id'=>$id, 'filename'=>$note->filename, 'file'=>$file), 'error'=>$error->get_soap_array());
656 'relate_note_to_module',
657 array('session'=>'xsd:string', 'note_id'=>'xsd:string', 'module_name'=>'xsd:string', 'module_id'=>'xsd:string'),
658 array('return'=>'tns:error_value'),
662 * Attach a note to another bean. Once you have created a note to store an
663 * attachment, the note needs to be related to the bean.
665 * @param String $session -- Session ID returned by a previous call to login.
666 * @param String $note_id -- The ID of the note that you want to associate with a bean
667 * @param String $module_name -- The name of the module to return records from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
668 * @param String $module_id -- The ID of the bean that you want to associate the note with
669 * @return no error for success, error for failure
671 function relate_note_to_module($session,$note_id, $module_name, $module_id){
672 global $beanList, $beanFiles;
673 $error = new SoapError();
674 if(!validate_authenticated($session)){
675 $error->set_error('invalid_login');
676 return $error->get_soap_array();
678 if(empty($beanList[$module_name])){
679 $error->set_error('no_module');
680 return $error->get_soap_array();
682 global $current_user;
683 if(!check_modules_access($current_user, $module_name, 'read')){
684 $error->set_error('no_access');
685 return $error->get_soap_array();
687 $class_name = $beanList['Notes'];
688 require_once($beanFiles[$class_name]);
689 $seed = new $class_name();
690 $seed->retrieve($note_id);
691 if(!$seed->ACLAccess('ListView')){
692 $error->set_error('no_access');
693 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
696 if($module_name != 'Contacts'){
697 $seed->parent_type=$module_name;
698 $seed->parent_id = $module_id;
702 $seed->contact_id=$module_id;
708 return $error->get_soap_array();
713 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'module_id'=>'xsd:string', 'select_fields'=>'tns:select_fields'),
714 array('return'=>'tns:get_entry_result'),
718 * Retrieve the collection of notes that are related to a bean.
720 * @param String $session -- Session ID returned by a previous call to login.
721 * @param String $module_name -- The name of the module to return records from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
722 * @param String $module_id -- The ID of the bean that you want to associate the note with
723 * @param Array $select_fields -- A list of the fields to be included in the results. This optional parameter allows for only needed fields to be retrieved.
724 * @return Array 'result_count' -- The number of records returned (-1 on error)
725 * 'next_offset' -- The start of the next page (This will always be the previous offset plus the number of rows returned. It does not indicate if there is additional data unless you calculate that the next_offset happens to be closer than it should be.
726 * 'field_list' -- The vardef information on the selected fields.
727 * 'entry_list' -- The records that were retrieved
728 * 'error' -- The SOAP error, if any
730 function get_related_notes($session,$module_name, $module_id, $select_fields){
731 global $beanList, $beanFiles;
732 $error = new SoapError();
733 if(!validate_authenticated($session)){
734 $error->set_error('invalid_login');
735 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
737 if(empty($beanList[$module_name])){
738 $error->set_error('no_module');
739 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
741 global $current_user;
742 if(!check_modules_access($current_user, $module_name, 'read')){
743 $error->set_error('no_access');
744 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
747 $class_name = $beanList[$module_name];
748 require_once($beanFiles[$class_name]);
749 $seed = new $class_name();
750 $seed->retrieve($module_id);
751 if(!$seed->ACLAccess('DetailView')){
752 $error->set_error('no_access');
753 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
755 $list = $seed->get_linked_beans('notes','Note', array(), 0, -1, 0);
757 $output_list = Array();
758 $field_list = Array();
759 foreach($list as $value)
761 $output_list[] = get_return_value($value, 'Notes');
762 if(empty($field_list))
764 $field_list = get_field_list($value);
767 $output_list = filter_return_list($output_list, $select_fields, $module_name);
768 $field_list = filter_field_list($field_list,$select_fields, $module_name);
770 return array('result_count'=>sizeof($output_list), 'next_offset'=>0,'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
775 array('session'=>'xsd:string'),
776 array('return'=>'tns:error_value'),
780 * Log out of the session. This will destroy the session and prevent other's from using it.
782 * @param String $session -- Session ID returned by a previous call to login.
783 * @return Empty error on success, Error on failure
785 function logout($session){
786 global $current_user;
788 $error = new SoapError();
789 LogicHook::initialize();
790 if(validate_authenticated($session)){
791 $current_user->call_custom_logic('before_logout');
793 $GLOBALS['logic_hook']->call_custom_logic('Users', 'after_logout');
794 return $error->get_soap_array();
796 $error->set_error('no_session');
797 $GLOBALS['logic_hook']->call_custom_logic('Users', 'after_logout');
798 return $error->get_soap_array();
803 array('session'=>'xsd:string', 'module_name'=>'xsd:string'),
804 array('return'=>'tns:module_fields'),
808 * Retrieve vardef information on the fields of the specified bean.
810 * @param String $session -- Session ID returned by a previous call to login.
811 * @param String $module_name -- The name of the module to return records from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
812 * @return Array 'module_fields' -- The vardef information on the selected fields.
813 * 'error' -- The SOAP error, if any
815 function get_module_fields($session, $module_name){
816 global $beanList, $beanFiles;
817 $error = new SoapError();
818 $module_fields = array();
819 if(! validate_authenticated($session)){
820 $error->set_error('invalid_session');
821 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
823 if(empty($beanList[$module_name])){
824 $error->set_error('no_module');
825 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
827 global $current_user;
828 if(!check_modules_access($current_user, $module_name, 'read')){
829 $error->set_error('no_access');
830 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
832 $class_name = $beanList[$module_name];
834 if(empty($beanFiles[$class_name]))
836 $error->set_error('no_file');
837 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
840 require_once($beanFiles[$class_name]);
841 $seed = new $class_name();
842 if($seed->ACLAccess('ListView', true) || $seed->ACLAccess('DetailView', true) || $seed->ACLAccess('EditView', true) )
844 return get_return_module_fields($seed, $module_name, $error);
848 $error->set_error('no_access');
849 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
854 'get_available_modules',
855 array('session'=>'xsd:string'),
856 array('return'=>'tns:module_list'),
860 * Retrieve the list of available modules on the system available to the currently logged in user.
862 * @param String $session -- Session ID returned by a previous call to login.
863 * @return Array 'modules' -- An array of module names
864 * 'error' -- The SOAP error, if any
866 function get_available_modules($session){
867 $error = new SoapError();
869 if(! validate_authenticated($session)){
870 $error->set_error('invalid_session');
871 return array('modules'=> $modules, 'error'=>$error->get_soap_array());
873 $modules = array_keys($_SESSION['avail_modules']);
875 return array('modules'=> $modules, 'error'=>$error->get_soap_array());
880 'update_portal_user',
881 array('session'=>'xsd:string', 'portal_name'=>'xsd:string', 'name_value_list'=>'tns:name_value_list'),
882 array('return'=>'tns:error_value'),
886 * Update the properties of a contact that is portal user. Add the portal user name to the user's properties.
888 * @param String $session -- Session ID returned by a previous call to login.
889 * @param String $portal_name -- The portal user_name of the contact
890 * @param Array $name_value_list -- collection of 'name'=>'value' pairs for finding the contact
891 * @return Empty error on success, Error on failure
893 function update_portal_user($session,$portal_name, $name_value_list){
894 global $beanList, $beanFiles;
895 $error = new SoapError();
896 if(! validate_authenticated($session)){
897 $error->set_error('invalid_session');
898 return $error->get_soap_array();
900 $contact = new Contact();
902 $searchBy = array('deleted'=>0);
903 foreach($name_value_list as $name_value){
904 $searchBy[$name_value['name']] = $name_value['value'];
906 if($contact->retrieve_by_string_fields($searchBy) != null){
907 if(!$contact->duplicates_found){
908 $contact->portal_name = $portal_name;
909 $contact->portal_active = 1;
910 if($contact->ACLAccess('Save')){
913 $error->set_error('no_access');
915 return $error->get_soap_array();
917 $error->set_error('duplicates');
918 return $error->get_soap_array();
920 $error->set_error('no_records');
921 return $error->get_soap_array();
926 array('session'=>'xsd:string'),
927 array('return'=>'xsd:string'),
931 * Return the user_id of the user that is logged into the current session.
933 * @param String $session -- Session ID returned by a previous call to login.
934 * @return String -- the User ID of the current session
937 function get_user_id($session){
938 if(validate_authenticated($session)){
939 global $current_user;
940 return $current_user->id;
949 array('session'=>'xsd:string'),
950 array('return'=>'xsd:string'),
954 * Return the ID of the default team for the user that is logged into the current session.
956 * @param String $session -- Session ID returned by a previous call to login.
957 * @return String -- the Team ID of the current user's default team
958 * 1 for Community Edition
961 function get_user_team_id($session){
962 if(validate_authenticated($session))
973 array('return'=>'xsd:string'),
977 * Return the current time on the server in the format 'Y-m-d H:i:s'. This time is in the server's default timezone.
979 * @return String -- The current date/time 'Y-m-d H:i:s'
981 function get_server_time(){
982 return date('Y-m-d H:i:s');
988 array('return'=>'xsd:string'),
992 * Return the current time on the server in the format 'Y-m-d H:i:s'. This time is in GMT.
994 * @return String -- The current date/time 'Y-m-d H:i:s'
996 function get_gmt_time(){
997 return TimeDate::getInstance()->nowDb();
1003 array('return'=>'xsd:string'),
1007 * Retrieve the specific flavor of sugar.
1009 * @return String 'CE' -- For Community Edition
1010 * 'PRO' -- For Professional
1011 * 'ENT' -- For Enterprise
1013 function get_sugar_flavor(){
1014 global $sugar_flavor;
1016 return $sugar_flavor;
1021 'get_server_version',
1023 array('return'=>'xsd:string'),
1027 * Retrieve the version number of Sugar that the server is running.
1029 * @return String -- The current sugar version number.
1032 function get_server_version(){
1034 $admin = new Administration();
1035 $admin->retrieveSettings('info');
1036 if(isset($admin->settings['info_sugar_version'])){
1037 return $admin->settings['info_sugar_version'];
1045 'get_relationships',
1046 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'module_id'=>'xsd:string', 'related_module'=>'xsd:string', 'related_module_query'=>'xsd:string', 'deleted'=>'xsd:int'),
1047 array('return'=>'tns:get_relationships_result'),
1051 * Retrieve a collection of beans tha are related to the specified bean.
1052 * As of 4.5.1c, all combinations of related modules are supported
1054 * @param String $session -- Session ID returned by a previous call to login.
1055 * @param String $module_name -- The name of the module that the primary record is from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
1056 * @param String $module_id -- The ID of the bean in the specified module
1057 * @param String $related_module -- The name of the related module to return records from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
1058 * @param String $related_module_query -- A portion of the where clause of the SQL statement to find the related items. The SQL query will already be filtered to only include the beans that are related to the specified bean.
1059 * @param Number $deleted -- false if deleted records should not be include, true if deleted records should be included.
1062 function get_relationships($session, $module_name, $module_id, $related_module, $related_module_query, $deleted){
1063 $error = new SoapError();
1065 if(!validate_authenticated($session)){
1066 $error->set_error('invalid_login');
1067 return array('ids'=>$ids,'error'=> $error->get_soap_array());
1069 global $beanList, $beanFiles;
1070 $error = new SoapError();
1072 if(empty($beanList[$module_name]) || empty($beanList[$related_module])){
1073 $error->set_error('no_module');
1074 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1076 $class_name = $beanList[$module_name];
1077 require_once($beanFiles[$class_name]);
1078 $mod = new $class_name();
1079 $mod->retrieve($module_id);
1080 if(!$mod->ACLAccess('DetailView')){
1081 $error->set_error('no_access');
1082 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1085 $id_list = get_linked_records($related_module, $module_name, $module_id);
1087 if ($id_list === FALSE) {
1088 $error->set_error('no_relationship_support');
1089 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1091 elseif (count($id_list) == 0) {
1092 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1097 $id_list_quoted = array_map(create_function('$str','return "\'" . $str . "\'";'), $id_list);
1098 $in = implode(", ", $id_list_quoted);
1100 $related_class_name = $beanList[$related_module];
1101 require_once($beanFiles[$related_class_name]);
1102 $related_mod = new $related_class_name();
1104 $sql = "SELECT {$related_mod->table_name}.id FROM {$related_mod->table_name} ";
1107 $sql .= " WHERE {$related_mod->table_name}.id IN ({$in}) ";
1109 if (!empty($related_module_query)) {
1110 $sql .= " AND ( {$related_module_query} )";
1113 $result = $related_mod->db->query($sql);
1114 while ($row = $related_mod->db->fetchByAssoc($result)) {
1115 $list[] = $row['id'];
1118 $return_list = array();
1120 foreach($list as $id) {
1121 $related_class_name = $beanList[$related_module];
1122 $related_mod = new $related_class_name();
1123 $related_mod->retrieve($id);
1125 $return_list[] = array(
1127 'date_modified' => $related_mod->date_modified,
1128 'deleted' => $related_mod->deleted
1132 return array('ids' => $return_list, 'error' => $error->get_soap_array());
1138 array('session'=>'xsd:string','set_relationship_value'=>'tns:set_relationship_value'),
1139 array('return'=>'tns:error_value'),
1143 * Set a single relationship between two beans. The items are related by module name and id.
1145 * @param String $session -- Session ID returned by a previous call to login.
1146 * @param Array $set_relationship_value --
1147 * 'module1' -- The name of the module that the primary record is from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
1148 * 'module1_id' -- The ID of the bean in the specified module
1149 * 'module2' -- The name of the module that the related record is from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
1150 * 'module2_id' -- The ID of the bean in the specified module
1151 * @return Empty error on success, Error on failure
1153 function set_relationship($session, $set_relationship_value){
1154 $error = new SoapError();
1155 if(!validate_authenticated($session)){
1156 $error->set_error('invalid_login');
1157 return $error->get_soap_array();
1159 return handle_set_relationship($set_relationship_value);
1163 'set_relationships',
1164 array('session'=>'xsd:string','set_relationship_list'=>'tns:set_relationship_list'),
1165 array('return'=>'tns:set_relationship_list_result'),
1169 * Setup several relationships between pairs of beans. The items are related by module name and id.
1171 * @param String $session -- Session ID returned by a previous call to login.
1172 * @param Array $set_relationship_list -- One for each relationship to setup. Each entry is itself an array.
1173 * 'module1' -- The name of the module that the primary record is from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
1174 * 'module1_id' -- The ID of the bean in the specified module
1175 * 'module2' -- The name of the module that the related record is from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
1176 * 'module2_id' -- The ID of the bean in the specified module
1177 * @return Empty error on success, Error on failure
1179 function set_relationships($session, $set_relationship_list){
1180 $error = new SoapError();
1181 if(!validate_authenticated($session)){
1182 $error->set_error('invalid_login');
1187 foreach($set_relationship_list as $set_relationship_value){
1188 $reter = handle_set_relationship($set_relationship_value);
1189 if($reter['number'] == 0){
1195 return array('created'=>$count , 'failed'=>$failed, 'error'=>$error);
1200 //INTERNAL FUNCTION NOT EXPOSED THROUGH SOAP
1202 * (Internal) Create a relationship between two beans.
1204 * @param Array $set_relationship_value --
1205 * 'module1' -- The name of the module that the primary record is from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
1206 * 'module1_id' -- The ID of the bean in the specified module
1207 * 'module2' -- The name of the module that the related record is from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
1208 * 'module2_id' -- The ID of the bean in the specified module
1209 * @return Empty error on success, Error on failure
1211 function handle_set_relationship($set_relationship_value)
1213 global $beanList, $beanFiles;
1214 $error = new SoapError();
1216 $module1 = $set_relationship_value['module1'];
1217 $module1_id = $set_relationship_value['module1_id'];
1218 $module2 = $set_relationship_value['module2'];
1219 $module2_id = $set_relationship_value['module2_id'];
1221 if(empty($beanList[$module1]) || empty($beanList[$module2]) )
1223 $error->set_error('no_module');
1224 return $error->get_soap_array();
1226 $class_name = $beanList[$module1];
1227 require_once($beanFiles[$class_name]);
1228 $mod = new $class_name();
1229 $mod->retrieve($module1_id);
1230 if(!$mod->ACLAccess('DetailView')){
1231 $error->set_error('no_access');
1232 return $error->get_soap_array();
1234 if($module1 == "Contacts" && $module2 == "Users"){
1235 $key = 'contacts_users_id';
1238 $key = array_search(strtolower($module2),$mod->relationship_fields);
1240 $key = Relationship::retrieve_by_modules($module1, $module2, $GLOBALS['db']);
1242 // BEGIN SnapLogic fix for bug 32064
1243 if ($module1 == "Quotes" && $module2 == "ProductBundles") {
1244 // Alternative solution is perhaps to
1245 // do whatever Sugar does when the same
1246 // request is received from the web:
1247 $pb_cls = $beanList[$module2];
1248 $pb = new $pb_cls();
1249 $pb->retrieve($module2_id);
1251 // Check if this relationship already exists
1252 $query = "SELECT count(*) AS count FROM product_bundle_quote WHERE quote_id = '{$module1_id}' AND bundle_id = '{$module2_id}' AND deleted = '0'";
1253 $result = $GLOBALS['db']->query($query, true, "Error checking for previously existing relationship between quote and product_bundle");
1254 $row = $GLOBALS['db']->fetchByAssoc($result);
1255 if(isset($row['count']) && $row['count'] > 0){
1256 return $error->get_soap_array();
1259 $query = "SELECT MAX(bundle_index)+1 AS idx FROM product_bundle_quote WHERE quote_id = '{$module1_id}' AND deleted='0'";
1260 $result = $GLOBALS['db']->query($query, true, "Error getting bundle_index");
1261 $GLOBALS['log']->debug("*********** Getting max bundle_index");
1262 $GLOBALS['log']->debug($query);
1263 $row = $GLOBALS['db']->fetchByAssoc($result);
1270 $pb->set_productbundle_quote_relationship($module1_id,$module2_id,$idx);
1272 return $error->get_soap_array();
1274 } else if ($module1 == "ProductBundles" && $module2 == "Products") {
1275 // And, well, similar things apply in this case
1276 $pb_cls = $beanList[$module1];
1277 $pb = new $pb_cls();
1278 $pb->retrieve($module1_id);
1280 // Check if this relationship already exists
1281 $query = "SELECT count(*) AS count FROM product_bundle_product WHERE bundle_id = '{$module1_id}' AND product_id = '{$module2_id}' AND deleted = '0'";
1282 $result = $GLOBALS['db']->query($query, true, "Error checking for previously existing relationship between quote and product_bundle");
1283 $row = $GLOBALS['db']->fetchByAssoc($result);
1284 if(isset($row['count']) && $row['count'] > 0){
1285 return $error->get_soap_array();
1288 $query = "SELECT MAX(product_index)+1 AS idx FROM product_bundle_product WHERE bundle_id='{$module1_id}'";
1289 $result = $GLOBALS['db']->query($query, true, "Error getting bundle_index");
1290 $GLOBALS['log']->debug("*********** Getting max bundle_index");
1291 $GLOBALS['log']->debug($query);
1292 $row = $GLOBALS['db']->fetchByAssoc($result);
1298 $pb->set_productbundle_product_relationship($module2_id,$idx,$module1_id);
1301 $prod_cls = $beanList[$module2];
1302 $prod = new $prod_cls();
1303 $prod->retrieve($module2_id);
1304 $prod->quote_id = $pb->quote_id;
1306 return $error->get_soap_array();
1308 // END SnapLogic fix for bug 32064
1311 $mod->load_relationship($key);
1312 $mod->$key->add($module2_id);
1313 return $error->get_soap_array();
1320 $error->set_error('no_module');
1321 return $error->get_soap_array();
1324 if(($module1 == 'Meetings' || $module1 == 'Calls') && ($module2 == 'Contacts' || $module2 == 'Users')){
1325 $key = strtolower($module2);
1326 $mod->load_relationship($key);
1327 $mod->$key->add($module2_id);
1329 $mod->$key = $module2_id;
1330 $mod->save_relationship_changes(false);
1333 return $error->get_soap_array();
1338 'set_document_revision',
1339 array('session'=>'xsd:string','note'=>'tns:document_revision'),
1340 array('return'=>'tns:set_entry_result'),
1344 * Enter description here...
1346 * @param String $session -- Session ID returned by a previous call to login.
1347 * @param unknown_type $document_revision
1350 function set_document_revision($session,$document_revision)
1353 $error = new SoapError();
1354 if(!validate_authenticated($session)){
1355 $error->set_error('invalid_login');
1356 return array('id'=>-1, 'error'=>$error->get_soap_array());
1359 require_once('modules/Documents/DocumentSoap.php');
1360 $dr = new DocumentSoap();
1361 return array('id'=>$dr->saveFile($document_revision), 'error'=>$error->get_soap_array());
1367 array('user_name'=>'xsd:string','password'=>'xsd:string','search_string'=>'xsd:string', 'modules'=>'tns:select_fields', 'offset'=>'xsd:int', 'max_results'=>'xsd:int'),
1368 array('return'=>'tns:get_entry_list_result'),
1372 * Given a list of modules to search and a search string, return the id, module_name, along with the fields
1373 * as specified in the $query_array
1375 * @param string $user_name - username of the Sugar User
1376 * @param string $password - password of the Sugar User
1377 * @param string $search_string - string to search
1378 * @param string[] $modules - array of modules to query
1379 * @param int $offset - a specified offset in the query
1380 * @param int $max_results - max number of records to return
1381 * @return get_entry_list_result - id, module_name, and list of fields from each record
1383 function search_by_module($user_name, $password, $search_string, $modules, $offset, $max_results){
1384 global $beanList, $beanFiles;
1386 $error = new SoapError();
1387 if(!validate_user($user_name, $password)){
1388 $error->set_error('invalid_login');
1389 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
1391 global $current_user;
1392 if($max_results > 0){
1393 global $sugar_config;
1394 $sugar_config['list_max_entries_per_page'] = $max_results;
1396 // MRF - BUG:19552 - added a join for accounts' emails below
1397 $query_array = array('Accounts'=>array('where'=>array('Accounts' => array(0 => "accounts.name like '{0}%'"), 'EmailAddresses' => array(0 => "ea.email_address like '{0}%'")),'fields'=>"accounts.id, accounts.name"),
1398 'Bugs'=>array('where'=>array('Bugs' => array(0 => "bugs.name like '{0}%'", 1 => "bugs.bug_number = {0}")),'fields'=>"bugs.id, bugs.name, bugs.bug_number"),
1399 'Cases'=>array('where'=>array('Cases' => array(0 => "cases.name like '{0}%'", 1 => "cases.case_number = {0}")),'fields'=>"cases.id, cases.name, cases.case_number"),
1400 'Leads'=>array('where'=>array('Leads' => array(0 => "leads.first_name like '{0}%'",1 => "leads.last_name like '{0}%'"), 'EmailAddresses' => array(0 => "ea.email_address like '{0}%'")), 'fields'=>"leads.id, leads.first_name, leads.last_name, leads.status"),
1401 'Project'=>array('where'=>array('Project' => array(0 => "project.name like '{0}%'")), 'fields'=>"project.id, project.name"),
1402 'ProjectTask'=>array('where'=>array('ProjectTask' => array(0 => "project.id = '{0}'")), 'fields'=>"project_task.id, project_task.name"),
1403 'Contacts'=>array('where'=>array('Contacts' => array(0 => "contacts.first_name like '{0}%'", 1 => "contacts.last_name like '{0}%'"), 'EmailAddresses' => array(0 => "ea.email_address like '{0}%'")),'fields'=>"contacts.id, contacts.first_name, contacts.last_name"),
1404 'Opportunities'=>array('where'=>array('Opportunities' => array(0 => "opportunities.name like '{0}%'")), 'fields'=>"opportunities.id, opportunities.name"),
1405 'Users'=>array('where'=>array('EmailAddresses' => array(0 => "ea.email_address like '{0}%'")),'fields'=>"users.id, users.user_name, users.first_name, ea.email_address"),
1408 if(!empty($search_string) && isset($search_string)){
1409 foreach($modules as $module_name){
1410 $class_name = $beanList[$module_name];
1411 require_once($beanFiles[$class_name]);
1412 $seed = new $class_name();
1413 if(empty($beanList[$module_name])){
1416 if(!check_modules_access($current_user, $module_name, 'read')){
1419 if(! $seed->ACLAccess('ListView'))
1424 if(isset($query_array[$module_name])){
1427 //split here to do while loop
1428 foreach($query_array[$module_name]['where'] as $key => $value){
1429 foreach($value as $where_clause){
1432 $tmpQuery = ' UNION ';
1433 $tmpQuery .= "SELECT ".$query_array[$module_name]['fields']." FROM $seed->table_name ";
1434 // We need to confirm that the user is a member of the team of the item.
1437 if($module_name == 'ProjectTask'){
1438 $tmpQuery .= "INNER JOIN project ON $seed->table_name.project_id = project.id ";
1441 if(isset($seed->emailAddress) && $key == 'EmailAddresses'){
1442 $tmpQuery .= " INNER JOIN email_addr_bean_rel eabl ON eabl.bean_id = $seed->table_name.id and eabl.deleted=0";
1443 $tmpQuery .= " INNER JOIN email_addresses ea ON (ea.id = eabl.email_address_id) ";
1446 $search_terms = explode(", ", $search_string);
1447 $termCount = count($search_terms);
1449 if($key != 'EmailAddresses'){
1450 foreach($search_terms as $term){
1451 if(!strpos($where_clause, 'number')){
1452 $where .= string_format($where_clause,array($term));
1453 }elseif(is_numeric($term)){
1454 $where .= string_format($where_clause,array($term));
1458 if($count < $termCount){
1464 $where .= 'ea.email_address IN (';
1465 foreach($search_terms as $term){
1466 $where .= "'".$GLOBALS['db']->quote($term)."'";
1467 if($count < $termCount){
1474 $tmpQuery .= $where;
1475 $tmpQuery .= ") AND $seed->table_name.deleted = 0";
1477 $query .= $tmpQuery;
1480 //grab the items from the db
1481 $result = $seed->db->query($query, $offset, $max_results);
1484 if(empty($rows_found)){
1485 $rows_found = $seed->db->getRowCount($result);
1490 while(($row = $seed->db->fetchByAssoc($result)) != null){
1492 $fields = explode(", ", $query_array[$module_name]['fields']);
1493 foreach($fields as $field){
1494 $field_names = explode(".", $field);
1495 $list[$field] = array('name'=>$field_names[1], 'value'=>$row[$field_names[1]]);
1498 $output_list[] = array('id'=>$row['id'],
1499 'module_name'=>$module_name,
1500 'name_value_list'=>$list);
1501 if(empty($field_list)){
1502 $field_list = get_field_list($row);
1509 $next_offset = $offset + sizeof($output_list);
1511 return array('result_count'=>sizeof($output_list), 'next_offset'=>$next_offset,'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
1517 'get_mailmerge_document',
1518 array('session'=>'xsd:string','file_name'=>'xsd:string', 'fields' => 'tns:select_fields'),
1519 array('return'=>'tns:get_sync_result_encoded'),
1523 * Enter description here...
1525 * @param String $session -- Session ID returned by a previous call to login.
1526 * @param unknown_type $file_name
1527 * @param unknown_type $fields
1530 function get_mailmerge_document($session, $file_name, $fields)
1532 global $beanList, $beanFiles, $app_list_strings;
1533 $error = new SoapError();
1534 if(!validate_authenticated($session))
1536 $error->set_error('invalid_login');
1537 return array('result'=>'', 'error'=>$error->get_soap_array());
1540 $file_name = $GLOBALS['sugar_config']['cache_dir'].'MergedDocuments/'.$file_name;
1542 $master_fields = array();
1543 $related_fields = array();
1545 if(file_exists($file_name))
1547 require_once($file_name);
1549 $class1 = $merge_array['master_module'];
1550 $beanL = $beanList[$class1];
1551 $bean1 = $beanFiles[$beanL];
1552 require_once($bean1);
1553 $seed1 = new $beanL();
1555 if(!empty($merge_array['related_module']))
1557 $class2 = $merge_array['related_module'];
1558 $beanR = $beanList[$class2];
1559 $bean2 = $beanFiles[$beanR];
1560 require_once($bean2);
1561 $seed2 = new $beanR();
1565 //$token1 = strtolower($class1);
1566 if($class1 == 'Prospects'){
1567 $class1 = 'CampaignProspects';
1569 foreach($fields as $field)
1571 $pos = strpos(strtolower($field), strtolower($class1));
1572 $pos2 = strpos(strtolower($field), strtolower($class2));
1574 $fieldName = str_replace(strtolower($class1).'_', '', strtolower($field));
1575 array_push($master_fields, $fieldName);
1576 }else if($pos2 !== false){
1577 $fieldName = str_replace(strtolower($class2).'_', '', strtolower($field));
1578 array_push($related_fields, $fieldName);
1582 $html = '<html><body><table border = 1><tr>';
1584 foreach($master_fields as $master_field){
1585 $html .= '<td>'.$class1.'_'.$master_field.'</td>';
1587 foreach($related_fields as $related_field){
1588 $html .= '<td>'.$class2.'_'.$related_field.'</td>';
1592 $ids = $merge_array['ids'];
1593 $is_prospect_merge = ($seed1->object_name == 'Prospect');
1594 foreach($ids as $key=>$value){
1595 if($is_prospect_merge){
1596 $seed1 = $seed1->retrieveTarget($key);
1598 $seed1->retrieve($key);
1601 foreach($master_fields as $master_field){
1602 if(isset($seed1->$master_field)){
1603 if($seed1->field_name_map[$master_field]['type'] == 'enum'){
1604 //pull in the translated dom
1605 $html .='<td>'.$app_list_strings[$seed1->field_name_map[$master_field]['options']][$seed1->$master_field].'</td>';
1607 $html .='<td>'.$seed1->$master_field.'</td>';
1611 $html .= '<td></td>';
1614 if(isset($value) && !empty($value)){
1615 $seed2->retrieve($value);
1616 foreach($related_fields as $related_field){
1617 if(isset($seed2->$related_field)){
1618 if($seed2->field_name_map[$related_field]['type'] == 'enum'){
1619 //pull in the translated dom
1620 $html .='<td>'.$app_list_strings[$seed2->field_name_map[$related_field]['options']][$seed2->$related_field].'</td>';
1622 $html .= '<td>'.$seed2->$related_field.'</td>';
1626 $html .= '<td></td>';
1632 $html .= "</table></body></html>";
1635 $result = base64_encode($html);
1636 return array('result' => $result, 'error' => $error);
1640 'get_mailmerge_document2',
1641 array('session'=>'xsd:string','file_name'=>'xsd:string', 'fields' => 'tns:select_fields'),
1642 array('return'=>'tns:get_mailmerge_document_result'),
1646 * Enter description here...
1648 * @param String $session -- Session ID returned by a previous call to login.
1649 * @param unknown_type $file_name
1650 * @param unknown_type $fields
1653 function get_mailmerge_document2($session, $file_name, $fields)
1655 global $beanList, $beanFiles, $app_list_strings;
1656 $error = new SoapError();
1657 if(!validate_authenticated($session))
1659 $error->set_error('invalid_login');
1660 return array('result'=>'', 'error'=>$error->get_soap_array());
1663 $file_name = $GLOBALS['sugar_config']['cache_dir'].'MergedDocuments/'.$file_name;
1665 $master_fields = array();
1666 $related_fields = array();
1668 if(file_exists($file_name))
1670 require_once($file_name);
1672 $class1 = $merge_array['master_module'];
1673 $beanL = $beanList[$class1];
1674 $bean1 = $beanFiles[$beanL];
1675 require_once($bean1);
1676 $seed1 = new $beanL();
1678 if(!empty($merge_array['related_module']))
1680 $class2 = $merge_array['related_module'];
1681 $beanR = $beanList[$class2];
1682 $bean2 = $beanFiles[$beanR];
1683 require_once($bean2);
1684 $seed2 = new $beanR();
1688 //$token1 = strtolower($class1);
1689 if($class1 == 'Prospects'){
1690 $class1 = 'CampaignProspects';
1692 foreach($fields as $field)
1694 $pos = strpos(strtolower($field), strtolower($class1));
1695 $pos2 = strpos(strtolower($field), strtolower($class2));
1697 $fieldName = str_replace(strtolower($class1).'_', '', strtolower($field));
1698 array_push($master_fields, $fieldName);
1699 }else if($pos2 !== false){
1700 $fieldName = str_replace(strtolower($class2).'_', '', strtolower($field));
1701 array_push($related_fields, $fieldName);
1705 $html = '<html><body><table border = 1><tr>';
1707 foreach($master_fields as $master_field){
1708 $html .= '<td>'.$class1.'_'.$master_field.'</td>';
1710 foreach($related_fields as $related_field){
1711 $html .= '<td>'.$class2.'_'.$related_field.'</td>';
1715 $ids = $merge_array['ids'];
1716 $resultIds = array();
1717 $is_prospect_merge = ($seed1->object_name == 'Prospect');
1718 if($is_prospect_merge){
1721 foreach($ids as $key=>$value){
1723 if($is_prospect_merge){
1724 $seed1 = $pSeed->retrieveTarget($key);
1726 $seed1->retrieve($key);
1728 $resultIds[] = array('name' => $seed1->module_name, 'value' => $key);
1730 foreach($master_fields as $master_field){
1731 if(isset($seed1->$master_field)){
1732 if($seed1->field_name_map[$master_field]['type'] == 'enum'){
1733 //pull in the translated dom
1734 $html .='<td>'.$app_list_strings[$seed1->field_name_map[$master_field]['options']][$seed1->$master_field].'</td>';
1736 $html .='<td>'.$seed1->$master_field.'</td>';
1740 $html .= '<td></td>';
1743 if(isset($value) && !empty($value)){
1744 $resultIds[] = array('name' => $seed2->module_name, 'value' => $value);
1745 $seed2->retrieve($value);
1746 foreach($related_fields as $related_field){
1747 if(isset($seed2->$related_field)){
1748 if($seed2->field_name_map[$related_field]['type'] == 'enum'){
1749 //pull in the translated dom
1750 $html .='<td>'.$app_list_strings[$seed2->field_name_map[$related_field]['options']][$seed2->$related_field].'</td>';
1752 $html .= '<td>'.$seed2->$related_field.'</td>';
1756 $html .= '<td></td>';
1762 $html .= "</table></body></html>";
1765 $result = base64_encode($html);
1766 return array('html' => $result, 'name_value_list' => $resultIds, 'error' => $error);
1770 'get_document_revision',
1771 array('session'=>'xsd:string','i'=>'xsd:string'),
1772 array('return'=>'tns:return_document_revision'),
1776 * This method is used as a result of the .htaccess lock down on the cache directory. It will allow a
1777 * properly authenticated user to download a document that they have proper rights to download.
1779 * @param String $session -- Session ID returned by a previous call to login.
1780 * @param String $id -- ID of the document revision to obtain
1781 * @return return_document_revision - this is a complex type as defined in SoapTypes.php
1783 function get_document_revision($session,$id)
1785 global $sugar_config;
1787 $error = new SoapError();
1788 if(!validate_authenticated($session)){
1789 $error->set_error('invalid_login');
1790 return array('id'=>-1, 'error'=>$error->get_soap_array());
1794 $dr = new DocumentRevision();
1796 if(!empty($dr->filename)){
1797 $filename = $sugar_config['upload_dir']."/".$dr->id;
1798 $contents = base64_encode(sugar_file_get_contents($filename));
1799 // $fh = sugar_fopen($sugar_config['upload_dir']."/rogerrsmith.doc", 'w');
1800 // fwrite($fh, base64_decode($contents));
1801 return array('document_revision'=>array('id' => $dr->id, 'document_name' => $dr->document_name, 'revision' => $dr->revision, 'filename' => $dr->filename, 'file' => $contents), 'error'=>$error->get_soap_array());
1803 $error->set_error('no_records');
1804 return array('id'=>-1, 'error'=>$error->get_soap_array());
1809 'set_campaign_merge',
1810 array('session'=>'xsd:string', 'targets'=>'tns:select_fields', 'campaign_id'=>'xsd:string'),
1811 array('return'=>'tns:error_value'),
1814 * Once we have successfuly done a mail merge on a campaign, we need to notify Sugar of the targets
1815 * and the campaign_id for tracking purposes
1817 * @param session the session id of the authenticated user
1818 * @param targets a string array of ids identifying the targets used in the merge
1819 * @param campaign_id the campaign_id used for the merge
1821 * @return error_value
1823 function set_campaign_merge($session,$targets, $campaign_id){
1824 $error = new SoapError();
1825 if(!validate_authenticated($session)){
1826 $error->set_error('invalid_login');
1827 return $error->get_soap_array();
1829 if (empty($campaign_id) or !is_array($targets) or count($targets) == 0) {
1830 $GLOBALS['log']->debug('set_campaign_merge: Merge action status will not be updated, because, campaign_id is null or no targets were selected.');
1832 require_once('modules/Campaigns/utils.php');
1833 campaign_log_mail_merge($campaign_id,$targets);
1836 return $error->get_soap_array();
1839 'get_entries_count',
1840 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'query'=>'xsd:string', 'deleted' => 'xsd:int'),
1841 array('return'=>'tns:get_entries_count_result'),
1845 * Retrieve number of records in a given module
1847 * @param session the session id of the authenticated user
1848 * @param module_name module to retrieve number of records from
1849 * @param query allows webservice user to provide a WHERE clause
1850 * @param deleted specify whether or not to include deleted records
1852 @return get_entries_count_result - this is a complex type as defined in SoapTypes.php
1854 function get_entries_count($session, $module_name, $query, $deleted) {
1855 global $beanList, $beanFiles, $current_user;
1857 $error = new SoapError();
1859 if (!validate_authenticated($session)) {
1860 $error->set_error('invalid_login');
1862 'result_count' => -1,
1863 'error' => $error->get_soap_array()
1867 if (empty($beanList[$module_name])) {
1868 $error->set_error('no_module');
1870 'result_count' => -1,
1871 'error' => $error->get_soap_array()
1875 if(!check_modules_access($current_user, $module_name, 'list')){
1876 $error->set_error('no_access');
1878 'result_count' => -1,
1879 'error' => $error->get_soap_array()
1883 $class_name = $beanList[$module_name];
1884 require_once($beanFiles[$class_name]);
1885 $seed = new $class_name();
1887 if (!$seed->ACLAccess('ListView')) {
1888 $error->set_error('no_access');
1890 'result_count' => -1,
1891 'error' => $error->get_soap_array()
1895 $sql = 'SELECT COUNT(*) result_count FROM ' . $seed->table_name . ' ';
1898 // build WHERE clauses, if any
1899 $where_clauses = array();
1900 if (!empty($query)) {
1901 $where_clauses[] = $query;
1903 if ($deleted == 0) {
1904 $where_clauses[] = $seed->table_name . '.deleted = 0';
1907 // if WHERE clauses exist, add them to query
1908 if (!empty($where_clauses)) {
1909 $sql .= ' WHERE ' . implode(' AND ', $where_clauses);
1912 $res = $GLOBALS['db']->query($sql);
1913 $row = $GLOBALS['db']->fetchByAssoc($res);
1916 'result_count' => $row['result_count'],
1917 'error' => $error->get_soap_array()
1922 'set_entries_details',
1923 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'name_value_lists'=>'tns:name_value_lists', 'select_fields' => 'tns:select_fields'),
1924 array('return'=>'tns:set_entries_detail_result'),
1928 * Update or create a list of SugarBeans, returning details about the records created/updated
1930 * @param String $session -- Session ID returned by a previous call to login.
1931 * @param String $module_name -- The name of the module to return records from. This name should be the name the module was developed under (changing a tab name is studio does not affect the name that should be passed into this method)..
1932 * @param Array $name_value_lists -- Array of Bean specific Arrays where the keys of the array are the SugarBean attributes, the values of the array are the values the attributes should have.
1933 * @param Array $select_fields -- A list of the fields to be included in the results. This optional parameter allows for only needed fields to be retrieved.
1934 * @return Array 'name_value_lists' -- Array of Bean specific Arrays where the keys of the array are the SugarBean attributes, the values of the array are the values the attributes should have.
1935 * 'error' -- The SOAP error if any.
1937 function set_entries_details($session, $module_name, $name_value_lists, $select_fields) {
1938 $error = new SoapError();
1940 if(!validate_authenticated($session)){
1941 $error->set_error('invalid_login');
1945 'error' => $error->get_soap_array()
1949 return handle_set_entries($module_name, $name_value_lists, $select_fields);
1952 // INTERNAL FUNCTION NOT EXPOSED THROUGH API
1953 function handle_set_entries($module_name, $name_value_lists, $select_fields = FALSE) {
1954 global $beanList, $beanFiles, $app_list_strings;
1956 $error = new SoapError();
1957 $ret_values = array();
1959 if(empty($beanList[$module_name])){
1960 $error->set_error('no_module');
1961 return array('ids'=>array(), 'error'=>$error->get_soap_array());
1963 global $current_user;
1964 if(!check_modules_access($current_user, $module_name, 'write')){
1965 $error->set_error('no_access');
1966 return array('ids'=>-1, 'error'=>$error->get_soap_array());
1969 $class_name = $beanList[$module_name];
1970 require_once($beanFiles[$class_name]);
1973 $total = sizeof($name_value_lists);
1974 foreach($name_value_lists as $name_value_list){
1975 $seed = new $class_name();
1977 $seed->update_vcal = false;
1978 foreach($name_value_list as $value){
1979 if($value['name'] == 'id'){
1980 $seed->retrieve($value['value']);
1985 foreach($name_value_list as $value) {
1986 $val = $value['value'];
1987 if($seed->field_name_map[$value['name']]['type'] == 'enum' ||$seed->field_name_map[$value['name']]['type'] == 'radioenum'){
1988 $vardef = $seed->field_name_map[$value['name']];
1989 if(isset($app_list_strings[$vardef['options']]) && !isset($app_list_strings[$vardef['options']][$value]) ) {
1990 if ( in_array($val,$app_list_strings[$vardef['options']]) ){
1991 $val = array_search($val,$app_list_strings[$vardef['options']]);
1994 } else if($seed->field_name_map[$value['name']]['type'] == 'multienum') {
1995 $vardef = $seed->field_name_map[$value['name']];
1996 if(isset($app_list_strings[$vardef['options']]) && !isset($app_list_strings[$vardef['options']][$value]) ) {
1997 $items = explode(",", $val);
1998 $parsedItems = array();
1999 foreach ($items as $item) {
2000 if ( in_array($item, $app_list_strings[$vardef['options']]) ){
2001 $keyVal = array_search($item,$app_list_strings[$vardef['options']]);
2002 array_push($parsedItems, $keyVal);
2005 if (!empty($parsedItems)) {
2006 $val = encodeMultienumValue($parsedItems);
2010 $seed->$value['name'] = $val;
2013 if($count == $total){
2014 $seed->update_vcal = false;
2018 //Add the account to a contact
2019 if($module_name == 'Contacts'){
2020 $GLOBALS['log']->debug('Creating Contact Account');
2021 add_create_account($seed);
2022 $duplicate_id = check_for_duplicate_contacts($seed);
2023 if($duplicate_id == null){
2024 if($seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2026 if($seed->deleted == 1){
2027 $seed->mark_deleted($seed->id);
2033 //since we found a duplicate we should set the sync flag
2034 if( $seed->ACLAccess('Save')){
2035 $seed->id = $duplicate_id;
2036 $seed->contacts_users_id = $current_user->id;
2038 $ids[] = $duplicate_id;//we have a conflict
2042 else if($module_name == 'Meetings' || $module_name == 'Calls'){
2043 //we are going to check if we have a meeting in the system
2044 //with the same outlook_id. If we do find one then we will grab that
2046 if( $seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2047 if(empty($seed->id) && !isset($seed->id)){
2048 if(!empty($seed->outlook_id) && isset($seed->outlook_id)){
2049 //at this point we have an object that does not have
2050 //the id set, but does have the outlook_id set
2051 //so we need to query the db to find if we already
2052 //have an object with this outlook_id, if we do
2053 //then we can set the id, otherwise this is a new object
2055 $query = $seed->table_name.".outlook_id = '".$seed->outlook_id."'";
2056 $response = $seed->get_list($order_by, $query, 0,-1,-1,0);
2057 $list = $response['list'];
2058 if(count($list) > 0){
2059 foreach($list as $value)
2061 $seed->id = $value->id;
2067 if (empty($seed->reminder_time)) {
2068 $seed->reminder_time = -1;
2070 if($seed->reminder_time == -1){
2071 $defaultRemindrTime = $current_user->getPreference('reminder_time');
2072 if ($defaultRemindrTime != -1){
2073 $seed->reminder_checked = '1';
2074 $seed->reminder_time = $defaultRemindrTime;
2083 if( $seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2089 // if somebody is calling set_entries_detail() and wants fields returned...
2090 if ($select_fields !== FALSE) {
2091 $ret_values[$count] = array();
2093 foreach ($select_fields as $select_field) {
2094 if (isset($seed->$select_field)) {
2095 $ret_values[$count][] = get_name_value($select_field, $seed->$select_field);
2101 // handle returns for set_entries_detail() and set_entries()
2102 if ($select_fields !== FALSE) {
2104 'name_value_lists' => $ret_values,
2105 'error' => $error->get_soap_array()
2111 'error' => $error->get_soap_array()