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 $isEmailModule = false;
354 if($module_name == 'Emails'){
355 $isEmailModule = true;
357 // retrieve the vardef information on the bean's fields.
358 $field_list = array();
359 foreach($list as $value)
361 if(isset($value->emailAddress)){
362 $value->emailAddress->handleLegacyRetrieve($value);
365 $value->retrieveEmailText();
367 $value->fill_in_additional_detail_fields();
368 $output_list[] = get_return_value($value, $module_name);
369 if(empty($field_list)){
370 $field_list = get_field_list($value);
374 // Filter the search results to only include the requested fields.
375 $output_list = filter_return_list($output_list, $select_fields, $module_name);
377 // Filter the list of fields to only include information on the requested fields.
378 $field_list = filter_return_list($field_list,$select_fields, $module_name);
380 // Calculate the offset for the start of the next page
381 $next_offset = $offset + sizeof($output_list);
383 return array('result_count'=>sizeof($output_list), 'next_offset'=>$next_offset,'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
388 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'id'=>'xsd:string', 'select_fields'=>'tns:select_fields'),
389 array('return'=>'tns:get_entry_result'),
393 * Retrieve a single SugarBean based on ID.
395 * @param String $session -- Session ID returned by a previous call to login.
396 * @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)..
397 * @param String $id -- The SugarBean's ID value.
398 * @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.
401 function get_entry($session, $module_name, $id,$select_fields ){
402 return get_entries($session, $module_name, array($id), $select_fields);
407 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'ids'=>'tns:select_fields', 'select_fields'=>'tns:select_fields'),
408 array('return'=>'tns:get_entry_result'),
412 * Retrieve a list of SugarBean's based on provided IDs.
414 * @param String $session -- Session ID returned by a previous call to login.
415 * @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)..
416 * @param Array $ids -- An array of SugarBean IDs.
417 * @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.
418 * @return Array 'field_list' -- Var def information about the returned fields
419 * 'entry_list' -- The records that were retrieved
420 * 'error' -- The SOAP error, if any
422 function get_entries($session, $module_name, $ids,$select_fields ){
423 global $beanList, $beanFiles;
424 $error = new SoapError();
425 $field_list = array();
426 $output_list = array();
427 if(!validate_authenticated($session)){
428 $error->set_error('invalid_login');
429 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
432 if($module_name == 'CampaignProspects'){
433 $module_name = 'Prospects';
436 if(empty($beanList[$module_name])){
437 $error->set_error('no_module');
438 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
440 global $current_user;
441 if(!check_modules_access($current_user, $module_name, 'read')){
442 $error->set_error('no_access');
443 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
446 $class_name = $beanList[$module_name];
447 require_once($beanFiles[$class_name]);
449 //todo can modify in there to call bean->get_list($order_by, $where, 0, -1, -1, $deleted);
450 //that way we do not have to call retrieve for each bean
451 //perhaps also add a select_fields to this, so we only get the fields we need
452 //and not do a select *
453 foreach($ids as $id){
454 $seed = new $class_name();
457 $seed = $seed->retrieveTarget($id);
459 if ($seed->retrieve($id) == null)
463 if ($seed->deleted == 1) {
465 $list[] = array('name'=>'warning', 'value'=>'Access to this object is denied since it has been deleted or does not exist');
466 $list[] = array('name'=>'deleted', 'value'=>'1');
467 $output_list[] = Array('id'=>$id,
468 'module_name'=> $module_name,
469 'name_value_list'=>$list,
473 if(! $seed->ACLAccess('DetailView')){
474 $error->set_error('no_access');
475 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
477 $output_list[] = get_return_value($seed, $module_name);
479 if(empty($field_list)){
480 $field_list = get_field_list($seed);
485 $output_list = filter_return_list($output_list, $select_fields, $module_name);
486 $field_list = filter_field_list($field_list,$select_fields, $module_name);
488 return array( 'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
493 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'name_value_list'=>'tns:name_value_list'),
494 array('return'=>'tns:set_entry_result'),
498 * Update or create a single SugarBean.
500 * @param String $session -- Session ID returned by a previous call to login.
501 * @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)..
502 * @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.
503 * @return Array 'id' -- the ID of the bean that was written to (-1 on error)
504 * 'error' -- The SOAP error if any.
506 function set_entry($session,$module_name, $name_value_list){
507 global $beanList, $beanFiles;
509 $error = new SoapError();
510 if(!validate_authenticated($session)){
511 $error->set_error('invalid_login');
512 return array('id'=>-1, 'error'=>$error->get_soap_array());
514 if(empty($beanList[$module_name])){
515 $error->set_error('no_module');
516 return array('id'=>-1, 'error'=>$error->get_soap_array());
518 global $current_user;
519 if(!check_modules_access($current_user, $module_name, 'write')){
520 $error->set_error('no_access');
521 return array('id'=>-1, 'error'=>$error->get_soap_array());
524 $class_name = $beanList[$module_name];
525 require_once($beanFiles[$class_name]);
526 $seed = new $class_name();
528 foreach($name_value_list as $value){
529 if($value['name'] == 'id' && isset($value['value']) && strlen($value['value']) > 0){
530 $result = $seed->retrieve($value['value']);
531 //bug: 44680 - check to ensure the user has access before proceeding.
534 $error->set_error('no_access');
535 return array('id'=>-1, 'error'=>$error->get_soap_array());
544 foreach($name_value_list as $value){
545 $GLOBALS['log']->debug($value['name']." : ".$value['value']);
546 $seed->$value['name'] = $value['value'];
548 if(! $seed->ACLAccess('Save') || ($seed->deleted == 1 && !$seed->ACLAccess('Delete')))
550 $error->set_error('no_access');
551 return array('id'=>-1, 'error'=>$error->get_soap_array());
554 if($seed->deleted == 1){
555 $seed->mark_deleted($seed->id);
557 return array('id'=>$seed->id, 'error'=>$error->get_soap_array());
563 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'name_value_lists'=>'tns:name_value_lists'),
564 array('return'=>'tns:set_entries_result'),
568 * Update or create a list of SugarBeans
570 * @param String $session -- Session ID returned by a previous call to login.
571 * @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)..
572 * @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.
573 * @return Array 'ids' -- Array of the IDs of the beans that was written to (-1 on error)
574 * 'error' -- The SOAP error if any.
576 function set_entries($session,$module_name, $name_value_lists){
577 $error = new SoapError();
579 if(!validate_authenticated($session)){
580 $error->set_error('invalid_login');
584 'error' => $error->get_soap_array()
588 return handle_set_entries($module_name, $name_value_lists, FALSE);
595 'set_note_attachment',
596 array('session'=>'xsd:string','note'=>'tns:note_attachment'),
597 array('return'=>'tns:set_entry_result'),
601 * Add or replace the attachment on a Note.
603 * @param String $session -- Session ID returned by a previous call to login.
604 * @param Binary $note -- The flie contents of the attachment.
605 * @return Array 'id' -- The ID of the new note or -1 on error
606 * 'error' -- The SOAP error if any.
608 function set_note_attachment($session,$note)
611 $error = new SoapError();
612 if(!validate_authenticated($session)){
613 $error->set_error('invalid_login');
614 return array('id'=>-1, 'error'=>$error->get_soap_array());
617 require_once('modules/Notes/NoteSoap.php');
618 $ns = new NoteSoap();
619 return array('id'=>$ns->saveFile($note), 'error'=>$error->get_soap_array());
624 'get_note_attachment',
625 array('session'=>'xsd:string', 'id'=>'xsd:string'),
626 array('return'=>'tns:return_note_attachment'),
630 * Retrieve an attachment from a note
631 * @param String $session -- Session ID returned by a previous call to login.
632 * @param Binary $note -- The flie contents of the attachment.
633 * @return Array 'id' -- The ID of the new note or -1 on error
634 * 'error' -- The SOAP error if any.
636 * @param String $session -- Session ID returned by a previous call to login.
637 * @param String $id -- The ID of the appropriate Note.
638 * @return Array 'note_attachment' -- Array String 'id' -- The ID of the Note containing the attachment
639 * String 'filename' -- The file name of the attachment
640 * Binary 'file' -- The binary contents of the file.
641 * 'error' -- The SOAP error if any.
643 function get_note_attachment($session,$id)
645 $error = new SoapError();
646 if(!validate_authenticated($session)){
647 $error->set_error('invalid_login');
648 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
653 $note->retrieve($id);
654 if(!$note->ACLAccess('DetailView')){
655 $error->set_error('no_access');
656 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
658 require_once('modules/Notes/NoteSoap.php');
659 $ns = new NoteSoap();
660 if(!isset($note->filename)){
661 $note->filename = '';
663 $file= $ns->retrieveFile($id,$note->filename);
665 $error->set_error('no_file');
669 return array('note_attachment'=>array('id'=>$id, 'filename'=>$note->filename, 'file'=>$file), 'error'=>$error->get_soap_array());
673 'relate_note_to_module',
674 array('session'=>'xsd:string', 'note_id'=>'xsd:string', 'module_name'=>'xsd:string', 'module_id'=>'xsd:string'),
675 array('return'=>'tns:error_value'),
679 * Attach a note to another bean. Once you have created a note to store an
680 * attachment, the note needs to be related to the bean.
682 * @param String $session -- Session ID returned by a previous call to login.
683 * @param String $note_id -- The ID of the note that you want to associate with a bean
684 * @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)..
685 * @param String $module_id -- The ID of the bean that you want to associate the note with
686 * @return no error for success, error for failure
688 function relate_note_to_module($session,$note_id, $module_name, $module_id){
689 global $beanList, $beanFiles;
690 $error = new SoapError();
691 if(!validate_authenticated($session)){
692 $error->set_error('invalid_login');
693 return $error->get_soap_array();
695 if(empty($beanList[$module_name])){
696 $error->set_error('no_module');
697 return $error->get_soap_array();
699 global $current_user;
700 if(!check_modules_access($current_user, $module_name, 'read')){
701 $error->set_error('no_access');
702 return $error->get_soap_array();
704 $class_name = $beanList['Notes'];
705 require_once($beanFiles[$class_name]);
706 $seed = new $class_name();
707 $seed->retrieve($note_id);
708 if(!$seed->ACLAccess('ListView')){
709 $error->set_error('no_access');
710 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
713 if($module_name != 'Contacts'){
714 $seed->parent_type=$module_name;
715 $seed->parent_id = $module_id;
719 $seed->contact_id=$module_id;
725 return $error->get_soap_array();
730 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'module_id'=>'xsd:string', 'select_fields'=>'tns:select_fields'),
731 array('return'=>'tns:get_entry_result'),
735 * Retrieve the collection of notes that are related to a bean.
737 * @param String $session -- Session ID returned by a previous call to login.
738 * @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)..
739 * @param String $module_id -- The ID of the bean that you want to associate the note with
740 * @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.
741 * @return Array 'result_count' -- The number of records returned (-1 on error)
742 * '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.
743 * 'field_list' -- The vardef information on the selected fields.
744 * 'entry_list' -- The records that were retrieved
745 * 'error' -- The SOAP error, if any
747 function get_related_notes($session,$module_name, $module_id, $select_fields){
748 global $beanList, $beanFiles;
749 $error = new SoapError();
750 if(!validate_authenticated($session)){
751 $error->set_error('invalid_login');
752 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
754 if(empty($beanList[$module_name])){
755 $error->set_error('no_module');
756 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
758 global $current_user;
759 if(!check_modules_access($current_user, $module_name, 'read')){
760 $error->set_error('no_access');
761 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
764 $class_name = $beanList[$module_name];
765 require_once($beanFiles[$class_name]);
766 $seed = new $class_name();
767 $seed->retrieve($module_id);
768 if(!$seed->ACLAccess('DetailView')){
769 $error->set_error('no_access');
770 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
772 $list = $seed->get_linked_beans('notes','Note', array(), 0, -1, 0);
774 $output_list = Array();
775 $field_list = Array();
776 foreach($list as $value)
778 $output_list[] = get_return_value($value, 'Notes');
779 if(empty($field_list))
781 $field_list = get_field_list($value);
784 $output_list = filter_return_list($output_list, $select_fields, $module_name);
785 $field_list = filter_field_list($field_list,$select_fields, $module_name);
787 return array('result_count'=>sizeof($output_list), 'next_offset'=>0,'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
792 array('session'=>'xsd:string'),
793 array('return'=>'tns:error_value'),
797 * Log out of the session. This will destroy the session and prevent other's from using it.
799 * @param String $session -- Session ID returned by a previous call to login.
800 * @return Empty error on success, Error on failure
802 function logout($session){
803 global $current_user;
805 $error = new SoapError();
806 LogicHook::initialize();
807 if(validate_authenticated($session)){
808 $current_user->call_custom_logic('before_logout');
810 $GLOBALS['logic_hook']->call_custom_logic('Users', 'after_logout');
811 return $error->get_soap_array();
813 $error->set_error('no_session');
814 $GLOBALS['logic_hook']->call_custom_logic('Users', 'after_logout');
815 return $error->get_soap_array();
820 array('session'=>'xsd:string', 'module_name'=>'xsd:string'),
821 array('return'=>'tns:module_fields'),
825 * Retrieve vardef information on the fields of the specified bean.
827 * @param String $session -- Session ID returned by a previous call to login.
828 * @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)..
829 * @return Array 'module_fields' -- The vardef information on the selected fields.
830 * 'error' -- The SOAP error, if any
832 function get_module_fields($session, $module_name){
833 global $beanList, $beanFiles;
834 $error = new SoapError();
835 $module_fields = array();
836 if(! validate_authenticated($session)){
837 $error->set_error('invalid_session');
838 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
840 if(empty($beanList[$module_name])){
841 $error->set_error('no_module');
842 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
844 global $current_user;
845 if(!check_modules_access($current_user, $module_name, 'read')){
846 $error->set_error('no_access');
847 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
849 $class_name = $beanList[$module_name];
851 if(empty($beanFiles[$class_name]))
853 $error->set_error('no_file');
854 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
857 require_once($beanFiles[$class_name]);
858 $seed = new $class_name();
859 if($seed->ACLAccess('ListView', true) || $seed->ACLAccess('DetailView', true) || $seed->ACLAccess('EditView', true) )
861 return get_return_module_fields($seed, $module_name, $error);
865 $error->set_error('no_access');
866 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
871 'get_available_modules',
872 array('session'=>'xsd:string'),
873 array('return'=>'tns:module_list'),
877 * Retrieve the list of available modules on the system available to the currently logged in user.
879 * @param String $session -- Session ID returned by a previous call to login.
880 * @return Array 'modules' -- An array of module names
881 * 'error' -- The SOAP error, if any
883 function get_available_modules($session){
884 $error = new SoapError();
886 if(! validate_authenticated($session)){
887 $error->set_error('invalid_session');
888 return array('modules'=> $modules, 'error'=>$error->get_soap_array());
890 $modules = array_keys($_SESSION['avail_modules']);
892 return array('modules'=> $modules, 'error'=>$error->get_soap_array());
897 'update_portal_user',
898 array('session'=>'xsd:string', 'portal_name'=>'xsd:string', 'name_value_list'=>'tns:name_value_list'),
899 array('return'=>'tns:error_value'),
903 * Update the properties of a contact that is portal user. Add the portal user name to the user's properties.
905 * @param String $session -- Session ID returned by a previous call to login.
906 * @param String $portal_name -- The portal user_name of the contact
907 * @param Array $name_value_list -- collection of 'name'=>'value' pairs for finding the contact
908 * @return Empty error on success, Error on failure
910 function update_portal_user($session,$portal_name, $name_value_list){
911 global $beanList, $beanFiles;
912 $error = new SoapError();
913 if(! validate_authenticated($session)){
914 $error->set_error('invalid_session');
915 return $error->get_soap_array();
917 $contact = new Contact();
919 $searchBy = array('deleted'=>0);
920 foreach($name_value_list as $name_value){
921 $searchBy[$name_value['name']] = $name_value['value'];
923 if($contact->retrieve_by_string_fields($searchBy) != null){
924 if(!$contact->duplicates_found){
925 $contact->portal_name = $portal_name;
926 $contact->portal_active = 1;
927 if($contact->ACLAccess('Save')){
930 $error->set_error('no_access');
932 return $error->get_soap_array();
934 $error->set_error('duplicates');
935 return $error->get_soap_array();
937 $error->set_error('no_records');
938 return $error->get_soap_array();
943 array('session'=>'xsd:string'),
944 array('return'=>'xsd:string'),
948 * Return the user_id of the user that is logged into the current session.
950 * @param String $session -- Session ID returned by a previous call to login.
951 * @return String -- the User ID of the current session
954 function get_user_id($session){
955 if(validate_authenticated($session)){
956 global $current_user;
957 return $current_user->id;
966 array('session'=>'xsd:string'),
967 array('return'=>'xsd:string'),
971 * Return the ID of the default team for the user that is logged into the current session.
973 * @param String $session -- Session ID returned by a previous call to login.
974 * @return String -- the Team ID of the current user's default team
975 * 1 for Community Edition
978 function get_user_team_id($session){
979 if(validate_authenticated($session))
990 array('return'=>'xsd:string'),
994 * 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.
996 * @return String -- The current date/time 'Y-m-d H:i:s'
998 function get_server_time(){
999 return date('Y-m-d H:i:s');
1005 array('return'=>'xsd:string'),
1009 * Return the current time on the server in the format 'Y-m-d H:i:s'. This time is in GMT.
1011 * @return String -- The current date/time 'Y-m-d H:i:s'
1013 function get_gmt_time(){
1014 return TimeDate::getInstance()->nowDb();
1020 array('return'=>'xsd:string'),
1024 * Retrieve the specific flavor of sugar.
1026 * @return String 'CE' -- For Community Edition
1027 * 'PRO' -- For Professional
1028 * 'ENT' -- For Enterprise
1030 function get_sugar_flavor(){
1031 global $sugar_flavor;
1033 return $sugar_flavor;
1038 'get_server_version',
1040 array('return'=>'xsd:string'),
1044 * Retrieve the version number of Sugar that the server is running.
1046 * @return String -- The current sugar version number.
1049 function get_server_version(){
1051 $admin = new Administration();
1052 $admin->retrieveSettings('info');
1053 if(isset($admin->settings['info_sugar_version'])){
1054 return $admin->settings['info_sugar_version'];
1062 'get_relationships',
1063 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'module_id'=>'xsd:string', 'related_module'=>'xsd:string', 'related_module_query'=>'xsd:string', 'deleted'=>'xsd:int'),
1064 array('return'=>'tns:get_relationships_result'),
1068 * Retrieve a collection of beans tha are related to the specified bean.
1069 * As of 4.5.1c, all combinations of related modules are supported
1071 * @param String $session -- Session ID returned by a previous call to login.
1072 * @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)..
1073 * @param String $module_id -- The ID of the bean in the specified module
1074 * @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)..
1075 * @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.
1076 * @param Number $deleted -- false if deleted records should not be include, true if deleted records should be included.
1079 function get_relationships($session, $module_name, $module_id, $related_module, $related_module_query, $deleted){
1080 $error = new SoapError();
1082 if(!validate_authenticated($session)){
1083 $error->set_error('invalid_login');
1084 return array('ids'=>$ids,'error'=> $error->get_soap_array());
1086 global $beanList, $beanFiles;
1087 $error = new SoapError();
1089 if(empty($beanList[$module_name]) || empty($beanList[$related_module])){
1090 $error->set_error('no_module');
1091 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1093 $class_name = $beanList[$module_name];
1094 require_once($beanFiles[$class_name]);
1095 $mod = new $class_name();
1096 $mod->retrieve($module_id);
1097 if(!$mod->ACLAccess('DetailView')){
1098 $error->set_error('no_access');
1099 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1102 $id_list = get_linked_records($related_module, $module_name, $module_id);
1104 if ($id_list === FALSE) {
1105 $error->set_error('no_relationship_support');
1106 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1108 elseif (count($id_list) == 0) {
1109 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1114 $id_list_quoted = array_map(create_function('$str','return "\'" . $str . "\'";'), $id_list);
1115 $in = implode(", ", $id_list_quoted);
1117 $related_class_name = $beanList[$related_module];
1118 require_once($beanFiles[$related_class_name]);
1119 $related_mod = new $related_class_name();
1121 $sql = "SELECT {$related_mod->table_name}.id FROM {$related_mod->table_name} ";
1124 $sql .= " WHERE {$related_mod->table_name}.id IN ({$in}) ";
1126 if (!empty($related_module_query)) {
1127 $sql .= " AND ( {$related_module_query} )";
1130 $result = $related_mod->db->query($sql);
1131 while ($row = $related_mod->db->fetchByAssoc($result)) {
1132 $list[] = $row['id'];
1135 $return_list = array();
1137 foreach($list as $id) {
1138 $related_class_name = $beanList[$related_module];
1139 $related_mod = new $related_class_name();
1140 $related_mod->retrieve($id);
1142 $return_list[] = array(
1144 'date_modified' => $related_mod->date_modified,
1145 'deleted' => $related_mod->deleted
1149 return array('ids' => $return_list, 'error' => $error->get_soap_array());
1155 array('session'=>'xsd:string','set_relationship_value'=>'tns:set_relationship_value'),
1156 array('return'=>'tns:error_value'),
1160 * Set a single relationship between two beans. The items are related by module name and id.
1162 * @param String $session -- Session ID returned by a previous call to login.
1163 * @param Array $set_relationship_value --
1164 * '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)..
1165 * 'module1_id' -- The ID of the bean in the specified module
1166 * '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)..
1167 * 'module2_id' -- The ID of the bean in the specified module
1168 * @return Empty error on success, Error on failure
1170 function set_relationship($session, $set_relationship_value){
1171 $error = new SoapError();
1172 if(!validate_authenticated($session)){
1173 $error->set_error('invalid_login');
1174 return $error->get_soap_array();
1176 return handle_set_relationship($set_relationship_value);
1180 'set_relationships',
1181 array('session'=>'xsd:string','set_relationship_list'=>'tns:set_relationship_list'),
1182 array('return'=>'tns:set_relationship_list_result'),
1186 * Setup several relationships between pairs of beans. The items are related by module name and id.
1188 * @param String $session -- Session ID returned by a previous call to login.
1189 * @param Array $set_relationship_list -- One for each relationship to setup. Each entry is itself an array.
1190 * '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)..
1191 * 'module1_id' -- The ID of the bean in the specified module
1192 * '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)..
1193 * 'module2_id' -- The ID of the bean in the specified module
1194 * @return Empty error on success, Error on failure
1196 function set_relationships($session, $set_relationship_list){
1197 $error = new SoapError();
1198 if(!validate_authenticated($session)){
1199 $error->set_error('invalid_login');
1204 foreach($set_relationship_list as $set_relationship_value){
1205 $reter = handle_set_relationship($set_relationship_value);
1206 if($reter['number'] == 0){
1212 return array('created'=>$count , 'failed'=>$failed, 'error'=>$error);
1217 //INTERNAL FUNCTION NOT EXPOSED THROUGH SOAP
1219 * (Internal) Create a relationship between two beans.
1221 * @param Array $set_relationship_value --
1222 * '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)..
1223 * 'module1_id' -- The ID of the bean in the specified module
1224 * '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)..
1225 * 'module2_id' -- The ID of the bean in the specified module
1226 * @return Empty error on success, Error on failure
1228 function handle_set_relationship($set_relationship_value)
1230 global $beanList, $beanFiles;
1231 $error = new SoapError();
1233 $module1 = $set_relationship_value['module1'];
1234 $module1_id = $set_relationship_value['module1_id'];
1235 $module2 = $set_relationship_value['module2'];
1236 $module2_id = $set_relationship_value['module2_id'];
1238 if(empty($beanList[$module1]) || empty($beanList[$module2]) )
1240 $error->set_error('no_module');
1241 return $error->get_soap_array();
1243 $class_name = $beanList[$module1];
1244 require_once($beanFiles[$class_name]);
1245 $mod = new $class_name();
1246 $mod->retrieve($module1_id);
1247 if(!$mod->ACLAccess('DetailView')){
1248 $error->set_error('no_access');
1249 return $error->get_soap_array();
1251 if($module1 == "Contacts" && $module2 == "Users"){
1252 $key = 'contacts_users_id';
1255 $key = array_search(strtolower($module2),$mod->relationship_fields);
1257 $key = Relationship::retrieve_by_modules($module1, $module2, $GLOBALS['db']);
1259 // BEGIN SnapLogic fix for bug 32064
1260 if ($module1 == "Quotes" && $module2 == "ProductBundles") {
1261 // Alternative solution is perhaps to
1262 // do whatever Sugar does when the same
1263 // request is received from the web:
1264 $pb_cls = $beanList[$module2];
1265 $pb = new $pb_cls();
1266 $pb->retrieve($module2_id);
1268 // Check if this relationship already exists
1269 $query = "SELECT count(*) AS count FROM product_bundle_quote WHERE quote_id = '{$module1_id}' AND bundle_id = '{$module2_id}' AND deleted = '0'";
1270 $result = $GLOBALS['db']->query($query, true, "Error checking for previously existing relationship between quote and product_bundle");
1271 $row = $GLOBALS['db']->fetchByAssoc($result);
1272 if(isset($row['count']) && $row['count'] > 0){
1273 return $error->get_soap_array();
1276 $query = "SELECT MAX(bundle_index)+1 AS idx FROM product_bundle_quote WHERE quote_id = '{$module1_id}' AND deleted='0'";
1277 $result = $GLOBALS['db']->query($query, true, "Error getting bundle_index");
1278 $GLOBALS['log']->debug("*********** Getting max bundle_index");
1279 $GLOBALS['log']->debug($query);
1280 $row = $GLOBALS['db']->fetchByAssoc($result);
1287 $pb->set_productbundle_quote_relationship($module1_id,$module2_id,$idx);
1289 return $error->get_soap_array();
1291 } else if ($module1 == "ProductBundles" && $module2 == "Products") {
1292 // And, well, similar things apply in this case
1293 $pb_cls = $beanList[$module1];
1294 $pb = new $pb_cls();
1295 $pb->retrieve($module1_id);
1297 // Check if this relationship already exists
1298 $query = "SELECT count(*) AS count FROM product_bundle_product WHERE bundle_id = '{$module1_id}' AND product_id = '{$module2_id}' AND deleted = '0'";
1299 $result = $GLOBALS['db']->query($query, true, "Error checking for previously existing relationship between quote and product_bundle");
1300 $row = $GLOBALS['db']->fetchByAssoc($result);
1301 if(isset($row['count']) && $row['count'] > 0){
1302 return $error->get_soap_array();
1305 $query = "SELECT MAX(product_index)+1 AS idx FROM product_bundle_product WHERE bundle_id='{$module1_id}'";
1306 $result = $GLOBALS['db']->query($query, true, "Error getting bundle_index");
1307 $GLOBALS['log']->debug("*********** Getting max bundle_index");
1308 $GLOBALS['log']->debug($query);
1309 $row = $GLOBALS['db']->fetchByAssoc($result);
1315 $pb->set_productbundle_product_relationship($module2_id,$idx,$module1_id);
1318 $prod_cls = $beanList[$module2];
1319 $prod = new $prod_cls();
1320 $prod->retrieve($module2_id);
1321 $prod->quote_id = $pb->quote_id;
1323 return $error->get_soap_array();
1325 // END SnapLogic fix for bug 32064
1328 $mod->load_relationship($key);
1329 $mod->$key->add($module2_id);
1330 return $error->get_soap_array();
1337 $error->set_error('no_module');
1338 return $error->get_soap_array();
1341 if(($module1 == 'Meetings' || $module1 == 'Calls') && ($module2 == 'Contacts' || $module2 == 'Users')){
1342 $key = strtolower($module2);
1343 $mod->load_relationship($key);
1344 $mod->$key->add($module2_id);
1346 $mod->$key = $module2_id;
1347 $mod->save_relationship_changes(false);
1350 return $error->get_soap_array();
1355 'set_document_revision',
1356 array('session'=>'xsd:string','note'=>'tns:document_revision'),
1357 array('return'=>'tns:set_entry_result'),
1361 * Enter description here...
1363 * @param String $session -- Session ID returned by a previous call to login.
1364 * @param unknown_type $document_revision
1367 function set_document_revision($session,$document_revision)
1370 $error = new SoapError();
1371 if(!validate_authenticated($session)){
1372 $error->set_error('invalid_login');
1373 return array('id'=>-1, 'error'=>$error->get_soap_array());
1376 require_once('modules/Documents/DocumentSoap.php');
1377 $dr = new DocumentSoap();
1378 return array('id'=>$dr->saveFile($document_revision), 'error'=>$error->get_soap_array());
1384 array('user_name'=>'xsd:string','password'=>'xsd:string','search_string'=>'xsd:string', 'modules'=>'tns:select_fields', 'offset'=>'xsd:int', 'max_results'=>'xsd:int'),
1385 array('return'=>'tns:get_entry_list_result'),
1389 * Given a list of modules to search and a search string, return the id, module_name, along with the fields
1390 * as specified in the $query_array
1392 * @param string $user_name - username of the Sugar User
1393 * @param string $password - password of the Sugar User
1394 * @param string $search_string - string to search
1395 * @param string[] $modules - array of modules to query
1396 * @param int $offset - a specified offset in the query
1397 * @param int $max_results - max number of records to return
1398 * @return get_entry_list_result - id, module_name, and list of fields from each record
1400 function search_by_module($user_name, $password, $search_string, $modules, $offset, $max_results){
1401 global $beanList, $beanFiles;
1403 $error = new SoapError();
1404 if(!validate_user($user_name, $password)){
1405 $error->set_error('invalid_login');
1406 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
1408 global $current_user;
1409 if($max_results > 0){
1410 global $sugar_config;
1411 $sugar_config['list_max_entries_per_page'] = $max_results;
1413 // MRF - BUG:19552 - added a join for accounts' emails below
1414 $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"),
1415 'Bugs'=>array('where'=>array('Bugs' => array(0 => "bugs.name like '{0}%'", 1 => "bugs.bug_number = {0}")),'fields'=>"bugs.id, bugs.name, bugs.bug_number"),
1416 'Cases'=>array('where'=>array('Cases' => array(0 => "cases.name like '{0}%'", 1 => "cases.case_number = {0}")),'fields'=>"cases.id, cases.name, cases.case_number"),
1417 '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"),
1418 'Project'=>array('where'=>array('Project' => array(0 => "project.name like '{0}%'")), 'fields'=>"project.id, project.name"),
1419 'ProjectTask'=>array('where'=>array('ProjectTask' => array(0 => "project.id = '{0}'")), 'fields'=>"project_task.id, project_task.name"),
1420 '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"),
1421 'Opportunities'=>array('where'=>array('Opportunities' => array(0 => "opportunities.name like '{0}%'")), 'fields'=>"opportunities.id, opportunities.name"),
1422 'Users'=>array('where'=>array('EmailAddresses' => array(0 => "ea.email_address like '{0}%'")),'fields'=>"users.id, users.user_name, users.first_name, ea.email_address"),
1425 if(!empty($search_string) && isset($search_string)){
1426 foreach($modules as $module_name){
1427 $class_name = $beanList[$module_name];
1428 require_once($beanFiles[$class_name]);
1429 $seed = new $class_name();
1430 if(empty($beanList[$module_name])){
1433 if(!check_modules_access($current_user, $module_name, 'read')){
1436 if(! $seed->ACLAccess('ListView'))
1441 if(isset($query_array[$module_name])){
1444 //split here to do while loop
1445 foreach($query_array[$module_name]['where'] as $key => $value){
1446 foreach($value as $where_clause){
1449 $tmpQuery = ' UNION ';
1450 $tmpQuery .= "SELECT ".$query_array[$module_name]['fields']." FROM $seed->table_name ";
1451 // We need to confirm that the user is a member of the team of the item.
1454 if($module_name == 'ProjectTask'){
1455 $tmpQuery .= "INNER JOIN project ON $seed->table_name.project_id = project.id ";
1458 if(isset($seed->emailAddress) && $key == 'EmailAddresses'){
1459 $tmpQuery .= " INNER JOIN email_addr_bean_rel eabl ON eabl.bean_id = $seed->table_name.id and eabl.deleted=0";
1460 $tmpQuery .= " INNER JOIN email_addresses ea ON (ea.id = eabl.email_address_id) ";
1463 $search_terms = explode(", ", $search_string);
1464 $termCount = count($search_terms);
1466 if($key != 'EmailAddresses'){
1467 foreach($search_terms as $term){
1468 if(!strpos($where_clause, 'number')){
1469 $where .= string_format($where_clause,array($term));
1470 }elseif(is_numeric($term)){
1471 $where .= string_format($where_clause,array($term));
1475 if($count < $termCount){
1481 $where .= 'ea.email_address IN (';
1482 foreach($search_terms as $term){
1483 $where .= "'".$GLOBALS['db']->quote($term)."'";
1484 if($count < $termCount){
1491 $tmpQuery .= $where;
1492 $tmpQuery .= ") AND $seed->table_name.deleted = 0";
1494 $query .= $tmpQuery;
1497 //grab the items from the db
1498 $result = $seed->db->query($query, $offset, $max_results);
1501 if(empty($rows_found)){
1502 $rows_found = $seed->db->getRowCount($result);
1507 while(($row = $seed->db->fetchByAssoc($result)) != null){
1509 $fields = explode(", ", $query_array[$module_name]['fields']);
1510 foreach($fields as $field){
1511 $field_names = explode(".", $field);
1512 $list[$field] = array('name'=>$field_names[1], 'value'=>$row[$field_names[1]]);
1515 $output_list[] = array('id'=>$row['id'],
1516 'module_name'=>$module_name,
1517 'name_value_list'=>$list);
1518 if(empty($field_list)){
1519 $field_list = get_field_list($row);
1526 $next_offset = $offset + sizeof($output_list);
1528 return array('result_count'=>sizeof($output_list), 'next_offset'=>$next_offset,'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
1534 'get_mailmerge_document',
1535 array('session'=>'xsd:string','file_name'=>'xsd:string', 'fields' => 'tns:select_fields'),
1536 array('return'=>'tns:get_sync_result_encoded'),
1540 * Enter description here...
1542 * @param String $session -- Session ID returned by a previous call to login.
1543 * @param unknown_type $file_name
1544 * @param unknown_type $fields
1547 function get_mailmerge_document($session, $file_name, $fields)
1549 global $beanList, $beanFiles, $app_list_strings;
1550 $error = new SoapError();
1551 if(!validate_authenticated($session))
1553 $error->set_error('invalid_login');
1554 return array('result'=>'', 'error'=>$error->get_soap_array());
1557 $file_name = $GLOBALS['sugar_config']['cache_dir'].'MergedDocuments/'.$file_name;
1559 $master_fields = array();
1560 $related_fields = array();
1562 if(file_exists($file_name))
1564 require_once($file_name);
1566 $class1 = $merge_array['master_module'];
1567 $beanL = $beanList[$class1];
1568 $bean1 = $beanFiles[$beanL];
1569 require_once($bean1);
1570 $seed1 = new $beanL();
1572 if(!empty($merge_array['related_module']))
1574 $class2 = $merge_array['related_module'];
1575 $beanR = $beanList[$class2];
1576 $bean2 = $beanFiles[$beanR];
1577 require_once($bean2);
1578 $seed2 = new $beanR();
1582 //$token1 = strtolower($class1);
1583 if($class1 == 'Prospects'){
1584 $class1 = 'CampaignProspects';
1586 foreach($fields as $field)
1588 $pos = strpos(strtolower($field), strtolower($class1));
1589 $pos2 = strpos(strtolower($field), strtolower($class2));
1591 $fieldName = str_replace(strtolower($class1).'_', '', strtolower($field));
1592 array_push($master_fields, $fieldName);
1593 }else if($pos2 !== false){
1594 $fieldName = str_replace(strtolower($class2).'_', '', strtolower($field));
1595 array_push($related_fields, $fieldName);
1599 $html = '<html><body><table border = 1><tr>';
1601 foreach($master_fields as $master_field){
1602 $html .= '<td>'.$class1.'_'.$master_field.'</td>';
1604 foreach($related_fields as $related_field){
1605 $html .= '<td>'.$class2.'_'.$related_field.'</td>';
1609 $ids = $merge_array['ids'];
1610 $is_prospect_merge = ($seed1->object_name == 'Prospect');
1611 foreach($ids as $key=>$value){
1612 if($is_prospect_merge){
1613 $seed1 = $seed1->retrieveTarget($key);
1615 $seed1->retrieve($key);
1618 foreach($master_fields as $master_field){
1619 if(isset($seed1->$master_field)){
1620 if($seed1->field_name_map[$master_field]['type'] == 'enum'){
1621 //pull in the translated dom
1622 $html .='<td>'.$app_list_strings[$seed1->field_name_map[$master_field]['options']][$seed1->$master_field].'</td>';
1624 $html .='<td>'.$seed1->$master_field.'</td>';
1628 $html .= '<td></td>';
1631 if(isset($value) && !empty($value)){
1632 $seed2->retrieve($value);
1633 foreach($related_fields as $related_field){
1634 if(isset($seed2->$related_field)){
1635 if($seed2->field_name_map[$related_field]['type'] == 'enum'){
1636 //pull in the translated dom
1637 $html .='<td>'.$app_list_strings[$seed2->field_name_map[$related_field]['options']][$seed2->$related_field].'</td>';
1639 $html .= '<td>'.$seed2->$related_field.'</td>';
1643 $html .= '<td></td>';
1649 $html .= "</table></body></html>";
1652 $result = base64_encode($html);
1653 return array('result' => $result, 'error' => $error);
1657 'get_mailmerge_document2',
1658 array('session'=>'xsd:string','file_name'=>'xsd:string', 'fields' => 'tns:select_fields'),
1659 array('return'=>'tns:get_mailmerge_document_result'),
1663 * Enter description here...
1665 * @param String $session -- Session ID returned by a previous call to login.
1666 * @param unknown_type $file_name
1667 * @param unknown_type $fields
1670 function get_mailmerge_document2($session, $file_name, $fields)
1672 global $beanList, $beanFiles, $app_list_strings;
1673 $error = new SoapError();
1674 if(!validate_authenticated($session))
1676 $error->set_error('invalid_login');
1677 return array('result'=>'', 'error'=>$error->get_soap_array());
1680 $file_name = $GLOBALS['sugar_config']['cache_dir'].'MergedDocuments/'.$file_name;
1682 $master_fields = array();
1683 $related_fields = array();
1685 if(file_exists($file_name))
1687 require_once($file_name);
1689 $class1 = $merge_array['master_module'];
1690 $beanL = $beanList[$class1];
1691 $bean1 = $beanFiles[$beanL];
1692 require_once($bean1);
1693 $seed1 = new $beanL();
1695 if(!empty($merge_array['related_module']))
1697 $class2 = $merge_array['related_module'];
1698 $beanR = $beanList[$class2];
1699 $bean2 = $beanFiles[$beanR];
1700 require_once($bean2);
1701 $seed2 = new $beanR();
1705 //$token1 = strtolower($class1);
1706 if($class1 == 'Prospects'){
1707 $class1 = 'CampaignProspects';
1709 foreach($fields as $field)
1711 $pos = strpos(strtolower($field), strtolower($class1));
1712 $pos2 = strpos(strtolower($field), strtolower($class2));
1714 $fieldName = str_replace(strtolower($class1).'_', '', strtolower($field));
1715 array_push($master_fields, $fieldName);
1716 }else if($pos2 !== false){
1717 $fieldName = str_replace(strtolower($class2).'_', '', strtolower($field));
1718 array_push($related_fields, $fieldName);
1722 $html = '<html><body><table border = 1><tr>';
1724 foreach($master_fields as $master_field){
1725 $html .= '<td>'.$class1.'_'.$master_field.'</td>';
1727 foreach($related_fields as $related_field){
1728 $html .= '<td>'.$class2.'_'.$related_field.'</td>';
1732 $ids = $merge_array['ids'];
1733 $resultIds = array();
1734 $is_prospect_merge = ($seed1->object_name == 'Prospect');
1735 if($is_prospect_merge){
1738 foreach($ids as $key=>$value){
1740 if($is_prospect_merge){
1741 $seed1 = $pSeed->retrieveTarget($key);
1743 $seed1->retrieve($key);
1745 $resultIds[] = array('name' => $seed1->module_name, 'value' => $key);
1747 foreach($master_fields as $master_field){
1748 if(isset($seed1->$master_field)){
1749 if($seed1->field_name_map[$master_field]['type'] == 'enum'){
1750 //pull in the translated dom
1751 $html .='<td>'.$app_list_strings[$seed1->field_name_map[$master_field]['options']][$seed1->$master_field].'</td>';
1753 $html .='<td>'.$seed1->$master_field.'</td>';
1757 $html .= '<td></td>';
1760 if(isset($value) && !empty($value)){
1761 $resultIds[] = array('name' => $seed2->module_name, 'value' => $value);
1762 $seed2->retrieve($value);
1763 foreach($related_fields as $related_field){
1764 if(isset($seed2->$related_field)){
1765 if($seed2->field_name_map[$related_field]['type'] == 'enum'){
1766 //pull in the translated dom
1767 $html .='<td>'.$app_list_strings[$seed2->field_name_map[$related_field]['options']][$seed2->$related_field].'</td>';
1769 $html .= '<td>'.$seed2->$related_field.'</td>';
1773 $html .= '<td></td>';
1779 $html .= "</table></body></html>";
1782 $result = base64_encode($html);
1783 return array('html' => $result, 'name_value_list' => $resultIds, 'error' => $error);
1787 'get_document_revision',
1788 array('session'=>'xsd:string','i'=>'xsd:string'),
1789 array('return'=>'tns:return_document_revision'),
1793 * This method is used as a result of the .htaccess lock down on the cache directory. It will allow a
1794 * properly authenticated user to download a document that they have proper rights to download.
1796 * @param String $session -- Session ID returned by a previous call to login.
1797 * @param String $id -- ID of the document revision to obtain
1798 * @return return_document_revision - this is a complex type as defined in SoapTypes.php
1800 function get_document_revision($session,$id)
1802 global $sugar_config;
1804 $error = new SoapError();
1805 if(!validate_authenticated($session)){
1806 $error->set_error('invalid_login');
1807 return array('id'=>-1, 'error'=>$error->get_soap_array());
1811 $dr = new DocumentRevision();
1813 if(!empty($dr->filename)){
1814 $filename = $sugar_config['upload_dir']."/".$dr->id;
1815 $contents = base64_encode(sugar_file_get_contents($filename));
1816 // $fh = sugar_fopen($sugar_config['upload_dir']."/rogerrsmith.doc", 'w');
1817 // fwrite($fh, base64_decode($contents));
1818 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());
1820 $error->set_error('no_records');
1821 return array('id'=>-1, 'error'=>$error->get_soap_array());
1826 'set_campaign_merge',
1827 array('session'=>'xsd:string', 'targets'=>'tns:select_fields', 'campaign_id'=>'xsd:string'),
1828 array('return'=>'tns:error_value'),
1831 * Once we have successfuly done a mail merge on a campaign, we need to notify Sugar of the targets
1832 * and the campaign_id for tracking purposes
1834 * @param session the session id of the authenticated user
1835 * @param targets a string array of ids identifying the targets used in the merge
1836 * @param campaign_id the campaign_id used for the merge
1838 * @return error_value
1840 function set_campaign_merge($session,$targets, $campaign_id){
1841 $error = new SoapError();
1842 if(!validate_authenticated($session)){
1843 $error->set_error('invalid_login');
1844 return $error->get_soap_array();
1846 if (empty($campaign_id) or !is_array($targets) or count($targets) == 0) {
1847 $GLOBALS['log']->debug('set_campaign_merge: Merge action status will not be updated, because, campaign_id is null or no targets were selected.');
1849 require_once('modules/Campaigns/utils.php');
1850 campaign_log_mail_merge($campaign_id,$targets);
1853 return $error->get_soap_array();
1856 'get_entries_count',
1857 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'query'=>'xsd:string', 'deleted' => 'xsd:int'),
1858 array('return'=>'tns:get_entries_count_result'),
1862 * Retrieve number of records in a given module
1864 * @param session the session id of the authenticated user
1865 * @param module_name module to retrieve number of records from
1866 * @param query allows webservice user to provide a WHERE clause
1867 * @param deleted specify whether or not to include deleted records
1869 @return get_entries_count_result - this is a complex type as defined in SoapTypes.php
1871 function get_entries_count($session, $module_name, $query, $deleted) {
1872 global $beanList, $beanFiles, $current_user;
1874 $error = new SoapError();
1876 if (!validate_authenticated($session)) {
1877 $error->set_error('invalid_login');
1879 'result_count' => -1,
1880 'error' => $error->get_soap_array()
1884 if (empty($beanList[$module_name])) {
1885 $error->set_error('no_module');
1887 'result_count' => -1,
1888 'error' => $error->get_soap_array()
1892 if(!check_modules_access($current_user, $module_name, 'list')){
1893 $error->set_error('no_access');
1895 'result_count' => -1,
1896 'error' => $error->get_soap_array()
1900 $class_name = $beanList[$module_name];
1901 require_once($beanFiles[$class_name]);
1902 $seed = new $class_name();
1904 if (!$seed->ACLAccess('ListView')) {
1905 $error->set_error('no_access');
1907 'result_count' => -1,
1908 'error' => $error->get_soap_array()
1912 $sql = 'SELECT COUNT(*) result_count FROM ' . $seed->table_name . ' ';
1915 // build WHERE clauses, if any
1916 $where_clauses = array();
1917 if (!empty($query)) {
1918 $where_clauses[] = $query;
1920 if ($deleted == 0) {
1921 $where_clauses[] = $seed->table_name . '.deleted = 0';
1924 // if WHERE clauses exist, add them to query
1925 if (!empty($where_clauses)) {
1926 $sql .= ' WHERE ' . implode(' AND ', $where_clauses);
1929 $res = $GLOBALS['db']->query($sql);
1930 $row = $GLOBALS['db']->fetchByAssoc($res);
1933 'result_count' => $row['result_count'],
1934 'error' => $error->get_soap_array()
1939 'set_entries_details',
1940 array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'name_value_lists'=>'tns:name_value_lists', 'select_fields' => 'tns:select_fields'),
1941 array('return'=>'tns:set_entries_detail_result'),
1945 * Update or create a list of SugarBeans, returning details about the records created/updated
1947 * @param String $session -- Session ID returned by a previous call to login.
1948 * @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)..
1949 * @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.
1950 * @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.
1951 * @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.
1952 * 'error' -- The SOAP error if any.
1954 function set_entries_details($session, $module_name, $name_value_lists, $select_fields) {
1955 $error = new SoapError();
1957 if(!validate_authenticated($session)){
1958 $error->set_error('invalid_login');
1962 'error' => $error->get_soap_array()
1966 return handle_set_entries($module_name, $name_value_lists, $select_fields);
1969 // INTERNAL FUNCTION NOT EXPOSED THROUGH API
1970 function handle_set_entries($module_name, $name_value_lists, $select_fields = FALSE) {
1971 global $beanList, $beanFiles, $app_list_strings, $current_user;
1973 $error = new SoapError();
1974 $ret_values = array();
1976 if(empty($beanList[$module_name])){
1977 $error->set_error('no_module');
1978 return array('ids'=>array(), 'error'=>$error->get_soap_array());
1981 if(!check_modules_access($current_user, $module_name, 'write'))
1983 $error->set_error('no_access');
1984 return array('ids'=>-1, 'error'=>$error->get_soap_array());
1987 $class_name = $beanList[$module_name];
1988 require_once($beanFiles[$class_name]);
1991 $total = sizeof($name_value_lists);
1993 foreach($name_value_lists as $name_value_list){
1994 $seed = new $class_name();
1996 $seed->update_vcal = false;
1998 //See if we can retrieve the seed by a given id value
1999 foreach($name_value_list as $value)
2001 if($value['name'] == 'id')
2003 $seed->retrieve($value['value']);
2008 $dataValues = array();
2009 foreach($name_value_list as $value)
2011 $val = $value['value'];
2012 if($seed->field_name_map[$value['name']]['type'] == 'enum' || $seed->field_name_map[$value['name']]['type'] == 'radioenum')
2014 $vardef = $seed->field_name_map[$value['name']];
2015 if(isset($app_list_strings[$vardef['options']]) && !isset($app_list_strings[$vardef['options']][$val]) )
2017 if ( in_array($val,$app_list_strings[$vardef['options']]) )
2019 $val = array_search($val,$app_list_strings[$vardef['options']]);
2023 } else if($seed->field_name_map[$value['name']]['type'] == 'multienum') {
2024 $vardef = $seed->field_name_map[$value['name']];
2026 if(isset($app_list_strings[$vardef['options']]) && !isset($app_list_strings[$vardef['options']][$value]) )
2028 $items = explode(",", $val);
2029 $parsedItems = array();
2030 foreach ($items as $item)
2032 if ( in_array($item, $app_list_strings[$vardef['options']]) )
2034 $keyVal = array_search($item,$app_list_strings[$vardef['options']]);
2035 array_push($parsedItems, $keyVal);
2038 if (!empty($parsedItems))
2040 $val = encodeMultienumValue($parsedItems);
2045 //Apply the non-empty values now since this will be used for duplicate checks
2048 $seed->$value['name'] = $val;
2050 //Store all the values in dataValues Array to apply later
2051 $dataValues[$value['name']] = $val;
2054 if($count == $total)
2056 $seed->update_vcal = false;
2060 //Add the account to a contact
2061 if($module_name == 'Contacts'){
2063 add_create_account($seed);
2064 $duplicate_id = check_for_duplicate_contacts($seed);
2065 if($duplicate_id == null)
2067 if($seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete')))
2069 //Now apply the values, since this is not a duplicate we can just pass false for the $firstSync argument
2070 apply_values($seed, $dataValues, false);
2072 if($seed->deleted == 1){
2073 $seed->mark_deleted($seed->id);
2078 //since we found a duplicate we should set the sync flag
2079 if( $seed->ACLAccess('Save'))
2081 //Determine if this is a first time sync. We find out based on whether or not a contacts_users relationship exists
2082 $seed->id = $duplicate_id;
2083 $seed->load_relationship("user_sync");
2084 $beans = $seed->user_sync->getBeans();
2085 $first_sync = empty($beans);
2087 //Now apply the values and indicate whether or not this is a first time sync
2088 apply_values($seed, $dataValues, $first_sync);
2089 $seed->contacts_users_id = $current_user->id;
2091 $ids[] = $duplicate_id;//we have a conflict
2094 } else if($module_name == 'Meetings' || $module_name == 'Calls') {
2095 //we are going to check if we have a meeting in the system
2096 //with the same outlook_id. If we do find one then we will grab that
2098 if( $seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2099 if(empty($seed->id) && !isset($seed->id)){
2100 if(!empty($seed->outlook_id) && isset($seed->outlook_id)){
2101 //at this point we have an object that does not have
2102 //the id set, but does have the outlook_id set
2103 //so we need to query the db to find if we already
2104 //have an object with this outlook_id, if we do
2105 //then we can set the id, otherwise this is a new object
2107 $query = $seed->table_name.".outlook_id = '".$seed->outlook_id."'";
2108 $response = $seed->get_list($order_by, $query, 0,-1,-1,0);
2109 $list = $response['list'];
2110 if(count($list) > 0){
2111 foreach($list as $value)
2113 $seed->id = $value->id;
2119 if (empty($seed->reminder_time)) {
2120 $seed->reminder_time = -1;
2122 if($seed->reminder_time == -1){
2123 $defaultRemindrTime = $current_user->getPreference('reminder_time');
2124 if ($defaultRemindrTime != -1){
2125 $seed->reminder_checked = '1';
2126 $seed->reminder_time = $defaultRemindrTime;
2133 if( $seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2139 // if somebody is calling set_entries_detail() and wants fields returned...
2140 if ($select_fields !== FALSE) {
2141 $ret_values[$count] = array();
2143 foreach ($select_fields as $select_field) {
2144 if (isset($seed->$select_field)) {
2145 $ret_values[$count][] = get_name_value($select_field, $seed->$select_field);
2151 // handle returns for set_entries_detail() and set_entries()
2152 if ($select_fields !== FALSE) {
2154 'name_value_lists' => $ret_values,
2155 'error' => $error->get_soap_array()
2161 'error' => $error->get_soap_array()