]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - soap/SoapSugarUsers.php
Release 6.5.1
[Github/sugarcrm.git] / soap / SoapSugarUsers.php
1 <?php
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-2012 SugarCRM Inc.
6  * 
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.
13  * 
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
17  * details.
18  * 
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
22  * 02110-1301 USA.
23  * 
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.
26  * 
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.
30  * 
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  ********************************************************************************/
37
38 require_once('soap/SoapHelperFunctions.php');
39 require_once('soap/SoapTypes.php');
40
41 /*************************************************************************************
42
43 THIS IS FOR SUGARCRM USERS
44
45
46 *************************************************************************************/
47 $disable_date_format = true;
48
49 $server->register(
50     'is_user_admin',
51     array('session'=>'xsd:string'),
52     array('return'=>'xsd:int'),
53     $NAMESPACE);
54
55 /**
56  * Return if the user is an admin or not
57  *
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
60  */
61 function is_user_admin($session){
62         if(validate_authenticated($session)){
63                 global $current_user;
64                 return is_admin($current_user);
65
66         }else{
67                 return 0;
68         }
69 }
70
71
72 $server->register(
73         'login',
74         array('user_auth'=>'tns:user_auth', 'application_name'=>'xsd:string'),
75         array('return'=>'tns:set_entry_result'),
76         $NAMESPACE);
77
78 /**
79  * Log the user into the application
80  *
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.
87  */
88 function login($user_auth, $application){
89         global $sugar_config, $system_config;
90
91         $error = new SoapError();
92         $user = new User();
93         $success = false;
94         //rrs
95                 $system_config = new Administration();
96         $system_config->retrieveSettings('system');
97         $authController = new AuthenticationController((!empty($sugar_config['authenticationClass'])? $sugar_config['authenticationClass'] : 'SugarAuthenticate'));
98         //rrs
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']);
101         if($usr_id) {
102                 $user->retrieve($usr_id);
103         }
104
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);
112                 } // if
113                 if(!empty($user) && !empty($user->id) && !$user->is_group) {
114                         $success = true;
115                         global $current_user;
116                         $current_user = $user;
117                 } // if
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'])){
128                         $success = true;
129                 } // if
130         } // else if
131
132         if($success){
133                 session_start();
134                 global $current_user;
135                 //$current_user = $user;
136                 login_success();
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'];
145
146                 $current_user->call_custom_logic('after_login');
147                 return array('id'=>session_id(), 'error'=>$error);
148         }
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);
154
155 }
156
157 //checks if the soap server and client are running on the same machine
158 $server->register(
159         'is_loopback',
160         array(),
161         array('return'=>'xsd:int'),
162         $NAMESPACE);
163
164 /**
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.
167  *
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.
170  */
171 function is_loopback(){
172         if(query_client_ip() == $_SERVER['SERVER_ADDR'])
173                 return 1;
174         return 0;
175 }
176
177 /**
178  * Validate the provided session information is correct and current.  Load the session.
179  *
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.
183  */
184 function validate_authenticated($session_id){
185         if(!empty($session_id)){
186                 session_id($session_id);
187                 session_start();
188
189                 if(!empty($_SESSION['is_valid_session']) && is_valid_ip_address('ip_address') && $_SESSION['type'] == 'user'){
190
191                         global $current_user;
192
193                         $current_user = new User();
194                         $current_user->retrieve($_SESSION['user_id']);
195                         login_success();
196                         return true;
197                 }
198
199                 session_destroy();
200         }
201         LogicHook::initialize();
202         $GLOBALS['log']->fatal('SECURITY: The session ID is invalid');
203         $GLOBALS['logic_hook']->call_custom_logic('Users', 'login_failed');
204         return false;
205 }
206
207 /**
208  * Use the same logic as in SugarAuthenticate to validate the ip address
209  *
210  * @param string $session_var
211  * @return bool - true if the ip address is valid, false otherwise.
212  */
213 function is_valid_ip_address($session_var){
214         global $sugar_config;
215         // grab client ip address
216         $clientIP = query_client_ip();
217         $classCheck = 0;
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) {
226                 $classCheck = 0;
227             }else {
228                         // match class C IP addresses
229                         for ($i = 0; $i < 3; $i ++) {
230                                 if ($session_parts[$i] == $client_parts[$i]) {
231                                         $classCheck = 1;
232                                                 continue;
233                                 } else {
234                                         $classCheck = 0;
235                                         break;
236                                         }
237                                 }
238                 }
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}");
242                                         return false;
243                                 }
244                         } else {
245                                 return false;
246                         }
247         }
248         return true;
249 }
250
251 $server->register(
252     'seamless_login',
253     array('session'=>'xsd:string'),
254     array('return'=>'xsd:int'),
255     $NAMESPACE);
256
257 /**
258  * Perform a seamless login.  This is used internally during the sync process.
259  *
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
263  */
264 function seamless_login($session){
265                 if(!validate_authenticated($session)){
266                         return 0;
267                 }
268                 $_SESSION['seamless_login'] = true;
269                 return 1;
270 }
271
272 $server->register(
273     'get_entry_list',
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'),
276     $NAMESPACE);
277
278 /**
279  * Retrieve a list of beans.  This is the primary method for getting list of SugarBeans from Sugar using the SOAP API.
280  *
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
299  */
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());
306         }
307     $using_cp = false;
308     if($module_name == 'CampaignProspects'){
309         $module_name = 'Prospects';
310         $using_cp = true;
311     }
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());
315         }
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());
320         }
321
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;
326         }
327
328
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')))
333         {
334                 $error->set_error('no_access');
335                 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
336         }
337
338         require_once 'include/SugarSQLValidate.php';
339         $valid = new SugarSQLValidate();
340         if(!$valid->validateQueryClauses($query, $order_by)) {
341         $GLOBALS['log']->error("Bad query: $query $order_by");
342             $error->set_error('no_access');
343             return array(
344                         'result_count' => -1,
345                         'error' => $error->get_soap_array()
346         );
347         }
348         if($query == ''){
349                 $where = '';
350         }
351         if($offset == '' || $offset == -1){
352                 $offset = 0;
353         }
354     if($using_cp){
355         $response = $seed->retrieveTargetList($query, $select_fields, $offset,-1,-1,$deleted);
356     }else{
357         $response = $seed->get_list($order_by, $query, $offset,-1,-1,$deleted,true);
358     }
359         $list = $response['list'];
360
361
362         $output_list = array();
363
364     $isEmailModule = false;
365     if($module_name == 'Emails'){
366         $isEmailModule = true;
367     }
368         // retrieve the vardef information on the bean's fields.
369         $field_list = array();
370         foreach($list as $value)
371         {
372                 if(isset($value->emailAddress)){
373                         $value->emailAddress->handleLegacyRetrieve($value);
374                 }
375         if($isEmailModule){
376             $value->retrieveEmailText();
377         }
378                 $value->fill_in_additional_detail_fields();
379                 $output_list[] = get_return_value($value, $module_name);
380                 if(empty($field_list)){
381                         $field_list = get_field_list($value);
382                 }
383         }
384
385         // Filter the search results to only include the requested fields.
386         $output_list = filter_return_list($output_list, $select_fields, $module_name);
387
388         // Filter the list of fields to only include information on the requested fields.
389         $field_list = filter_return_list($field_list,$select_fields, $module_name);
390
391         // Calculate the offset for the start of the next page
392         $next_offset = $offset + sizeof($output_list);
393
394         return array('result_count'=>sizeof($output_list), 'next_offset'=>$next_offset,'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
395 }
396
397 $server->register(
398     'get_entry',
399     array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'id'=>'xsd:string', 'select_fields'=>'tns:select_fields'),
400     array('return'=>'tns:get_entry_result'),
401     $NAMESPACE);
402
403 /**
404  * Retrieve a single SugarBean based on ID.
405  *
406  * @param String $session -- Session ID returned by a previous call to login.
407  * @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)..
408  * @param String $id -- The SugarBean's ID value.
409  * @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.
410  * @return unknown
411  */
412 function get_entry($session, $module_name, $id,$select_fields ){
413         return get_entries($session, $module_name, array($id), $select_fields);
414 }
415
416 $server->register(
417     'get_entries',
418     array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'ids'=>'tns:select_fields', 'select_fields'=>'tns:select_fields'),
419     array('return'=>'tns:get_entry_result'),
420     $NAMESPACE);
421
422 /**
423  * Retrieve a list of SugarBean's based on provided IDs.
424  *
425  * @param String $session -- Session ID returned by a previous call to login.
426  * @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)..
427  * @param Array $ids -- An array of SugarBean IDs.
428  * @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.
429  * @return Array 'field_list' -- Var def information about the returned fields
430  *               'entry_list' -- The records that were retrieved
431  *               'error' -- The SOAP error, if any
432  */
433 function get_entries($session, $module_name, $ids,$select_fields ){
434         global  $beanList, $beanFiles;
435         $error = new SoapError();
436         $field_list = array();
437         $output_list = array();
438         if(!validate_authenticated($session)){
439                 $error->set_error('invalid_login');
440                 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
441         }
442     $using_cp = false;
443     if($module_name == 'CampaignProspects'){
444         $module_name = 'Prospects';
445         $using_cp = true;
446     }
447         if(empty($beanList[$module_name])){
448                 $error->set_error('no_module');
449                 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
450         }
451         global $current_user;
452         if(!check_modules_access($current_user, $module_name, 'read')){
453                 $error->set_error('no_access');
454                 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
455         }
456
457         $class_name = $beanList[$module_name];
458         require_once($beanFiles[$class_name]);
459
460         //todo can modify in there to call bean->get_list($order_by, $where, 0, -1, -1, $deleted);
461         //that way we do not have to call retrieve for each bean
462         //perhaps also add a select_fields to this, so we only get the fields we need
463         //and not do a select *
464         foreach($ids as $id){
465                 $seed = new $class_name();
466
467     if($using_cp){
468         $seed = $seed->retrieveTarget($id);
469     }else{
470                 if ($seed->retrieve($id) == null)
471                         $seed->deleted = 1;
472     }
473
474     if ($seed->deleted == 1) {
475         $list = array();
476         $list[] = array('name'=>'warning', 'value'=>'Access to this object is denied since it has been deleted or does not exist');
477                 $list[] = array('name'=>'deleted', 'value'=>'1');
478         $output_list[] = Array('id'=>$id,
479                                                                 'module_name'=> $module_name,
480                                                         'name_value_list'=>$list,
481                                                         );
482                 continue;
483     }
484         if(! $seed->ACLAccess('DetailView')){
485                 $error->set_error('no_access');
486                 return array('field_list'=>$field_list, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
487         }
488                 $output_list[] = get_return_value($seed, $module_name);
489
490                 if(empty($field_list)){
491                                 $field_list = get_field_list($seed);
492
493                 }
494         }
495
496                 $output_list = filter_return_list($output_list, $select_fields, $module_name);
497                 $field_list = filter_field_list($field_list,$select_fields, $module_name);
498
499         return array( 'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
500 }
501
502 $server->register(
503     'set_entry',
504     array('session'=>'xsd:string', 'module_name'=>'xsd:string',  'name_value_list'=>'tns:name_value_list'),
505     array('return'=>'tns:set_entry_result'),
506     $NAMESPACE);
507
508 /**
509  * Update or create a single SugarBean.
510  *
511  * @param String $session -- Session ID returned by a previous call to login.
512  * @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)..
513  * @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.
514  * @return Array    'id' -- the ID of the bean that was written to (-1 on error)
515  *                  'error' -- The SOAP error if any.
516  */
517 function set_entry($session,$module_name, $name_value_list){
518         global  $beanList, $beanFiles;
519
520         $error = new SoapError();
521         if(!validate_authenticated($session)){
522                 $error->set_error('invalid_login');
523                 return array('id'=>-1, 'error'=>$error->get_soap_array());
524         }
525         if(empty($beanList[$module_name])){
526                 $error->set_error('no_module');
527                 return array('id'=>-1, 'error'=>$error->get_soap_array());
528         }
529         global $current_user;
530         if(!check_modules_access($current_user, $module_name, 'write')){
531                 $error->set_error('no_access');
532                 return array('id'=>-1, 'error'=>$error->get_soap_array());
533         }
534
535         $class_name = $beanList[$module_name];
536         require_once($beanFiles[$class_name]);
537         $seed = new $class_name();
538
539         foreach($name_value_list as $value){
540         if($value['name'] == 'id' && isset($value['value']) && strlen($value['value']) > 0){
541                         $result = $seed->retrieve($value['value']);
542             //bug: 44680 - check to ensure the user has access before proceeding.
543             if(is_null($result))
544             {
545                 $error->set_error('no_access');
546                         return array('id'=>-1, 'error'=>$error->get_soap_array());
547             }
548             else
549             {
550                 break;
551             }
552
553                 }
554         }
555         foreach($name_value_list as $value){
556         $GLOBALS['log']->debug($value['name']." : ".$value['value']);
557                 $seed->$value['name'] = $value['value'];
558         }
559         if(! $seed->ACLAccess('Save') || ($seed->deleted == 1  &&  !$seed->ACLAccess('Delete')))
560         {
561                 $error->set_error('no_access');
562                 return array('id'=>-1, 'error'=>$error->get_soap_array());
563         }
564         $seed->save();
565         if($seed->deleted == 1){
566                         $seed->mark_deleted($seed->id);
567         }
568         return array('id'=>$seed->id, 'error'=>$error->get_soap_array());
569
570 }
571
572 $server->register(
573     'set_entries',
574     array('session'=>'xsd:string', 'module_name'=>'xsd:string',  'name_value_lists'=>'tns:name_value_lists'),
575     array('return'=>'tns:set_entries_result'),
576     $NAMESPACE);
577
578 /**
579  * Update or create a list of SugarBeans
580  *
581  * @param String $session -- Session ID returned by a previous call to login.
582  * @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)..
583  * @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.
584  * @return Array    'ids' -- Array of the IDs of the beans that was written to (-1 on error)
585  *                  'error' -- The SOAP error if any.
586  */
587 function set_entries($session,$module_name, $name_value_lists){
588         $error = new SoapError();
589
590         if(!validate_authenticated($session)){
591                 $error->set_error('invalid_login');
592
593                 return array(
594                         'ids' => array(),
595                         'error' => $error->get_soap_array()
596                 );
597         }
598
599         return handle_set_entries($module_name, $name_value_lists, FALSE);
600 }
601
602 /*
603 NOTE SPECIFIC CODE
604 */
605 $server->register(
606         'set_note_attachment',
607         array('session'=>'xsd:string','note'=>'tns:note_attachment'),
608         array('return'=>'tns:set_entry_result'),
609         $NAMESPACE);
610
611 /**
612  * Add or replace the attachment on a Note.
613  *
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.
618  */
619 function set_note_attachment($session,$note)
620 {
621
622         $error = new SoapError();
623         if(!validate_authenticated($session)){
624                 $error->set_error('invalid_login');
625                 return array('id'=>-1, 'error'=>$error->get_soap_array());
626         }
627
628         require_once('modules/Notes/NoteSoap.php');
629         $ns = new NoteSoap();
630         return array('id'=>$ns->saveFile($note), 'error'=>$error->get_soap_array());
631
632 }
633
634 $server->register(
635     'get_note_attachment',
636     array('session'=>'xsd:string', 'id'=>'xsd:string'),
637     array('return'=>'tns:return_note_attachment'),
638     $NAMESPACE);
639
640 /**
641  * Retrieve an attachment from a note
642  * @param String $session -- Session ID returned by a previous call to login.
643  * @param Binary $note -- The flie contents of the attachment.
644  * @return Array 'id' -- The ID of the new note or -1 on error
645  *               'error' -- The SOAP error if any.
646  *
647  * @param String $session -- Session ID returned by a previous call to login.
648  * @param String $id -- The ID of the appropriate Note.
649  * @return Array 'note_attachment' -- Array String 'id' -- The ID of the Note containing the attachment
650  *                                          String 'filename' -- The file name of the attachment
651  *                                          Binary 'file' -- The binary contents of the file.
652  *               'error' -- The SOAP error if any.
653  */
654 function get_note_attachment($session,$id)
655 {
656         $error = new SoapError();
657         if(!validate_authenticated($session)){
658                 $error->set_error('invalid_login');
659                 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
660         }
661
662         $note = new Note();
663
664         $note->retrieve($id);
665         if(!$note->ACLAccess('DetailView')){
666                 $error->set_error('no_access');
667                 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
668         }
669         require_once('modules/Notes/NoteSoap.php');
670         $ns = new NoteSoap();
671         if(!isset($note->filename)){
672                 $note->filename = '';
673         }
674         $file= $ns->retrieveFile($id,$note->filename);
675         if($file == -1){
676                 $error->set_error('no_file');
677                 $file = '';
678         }
679
680         return array('note_attachment'=>array('id'=>$id, 'filename'=>$note->filename, 'file'=>$file), 'error'=>$error->get_soap_array());
681
682 }
683 $server->register(
684     'relate_note_to_module',
685     array('session'=>'xsd:string', 'note_id'=>'xsd:string', 'module_name'=>'xsd:string', 'module_id'=>'xsd:string'),
686     array('return'=>'tns:error_value'),
687     $NAMESPACE);
688
689 /**
690  * Attach a note to another bean.  Once you have created a note to store an
691  * attachment, the note needs to be related to the bean.
692  *
693  * @param String $session -- Session ID returned by a previous call to login.
694  * @param String $note_id -- The ID of the note that you want to associate with a bean
695  * @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)..
696  * @param String $module_id -- The ID of the bean that you want to associate the note with
697  * @return no error for success, error for failure
698  */
699 function relate_note_to_module($session,$note_id, $module_name, $module_id){
700         global  $beanList, $beanFiles;
701         $error = new SoapError();
702         if(!validate_authenticated($session)){
703                 $error->set_error('invalid_login');
704                 return $error->get_soap_array();
705         }
706         if(empty($beanList[$module_name])){
707                 $error->set_error('no_module');
708                 return $error->get_soap_array();
709         }
710         global $current_user;
711         if(!check_modules_access($current_user, $module_name, 'read')){
712                 $error->set_error('no_access');
713                 return $error->get_soap_array();
714         }
715         $class_name = $beanList['Notes'];
716         require_once($beanFiles[$class_name]);
717         $seed = new $class_name();
718         $seed->retrieve($note_id);
719         if(!$seed->ACLAccess('ListView')){
720                 $error->set_error('no_access');
721                 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
722         }
723
724         if($module_name != 'Contacts'){
725                 $seed->parent_type=$module_name;
726                 $seed->parent_id = $module_id;
727
728         }else{
729
730                 $seed->contact_id=$module_id;
731
732         }
733
734         $seed->save();
735
736         return $error->get_soap_array();
737
738 }
739 $server->register(
740     'get_related_notes',
741     array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'module_id'=>'xsd:string', 'select_fields'=>'tns:select_fields'),
742     array('return'=>'tns:get_entry_result'),
743     $NAMESPACE);
744
745 /**
746  * Retrieve the collection of notes that are related to a bean.
747  *
748  * @param String $session -- Session ID returned by a previous call to login.
749  * @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)..
750  * @param String $module_id -- The ID of the bean that you want to associate the note with
751  * @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.
752  * @return Array    'result_count' -- The number of records returned (-1 on error)
753  *                  '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.
754  *                  'field_list' -- The vardef information on the selected fields.
755  *                  'entry_list' -- The records that were retrieved
756  *                  'error' -- The SOAP error, if any
757  */
758 function get_related_notes($session,$module_name, $module_id, $select_fields){
759         global  $beanList, $beanFiles;
760         $error = new SoapError();
761         if(!validate_authenticated($session)){
762                 $error->set_error('invalid_login');
763                 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
764         }
765         if(empty($beanList[$module_name])){
766                 $error->set_error('no_module');
767                 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
768         }
769         global $current_user;
770         if(!check_modules_access($current_user, $module_name, 'read')){
771                 $error->set_error('no_access');
772                 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
773         }
774
775         $class_name = $beanList[$module_name];
776         require_once($beanFiles[$class_name]);
777         $seed = new $class_name();
778         $seed->retrieve($module_id);
779         if(!$seed->ACLAccess('DetailView')){
780                 $error->set_error('no_access');
781                 return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
782         }
783         $list = $seed->get_linked_beans('notes','Note', array(), 0, -1, 0);
784
785         $output_list = Array();
786         $field_list = Array();
787         foreach($list as $value)
788         {
789                 $output_list[] = get_return_value($value, 'Notes');
790         if(empty($field_list))
791         {
792                         $field_list = get_field_list($value);
793                 }
794         }
795         $output_list = filter_return_list($output_list, $select_fields, $module_name);
796         $field_list = filter_field_list($field_list,$select_fields, $module_name);
797
798         return array('result_count'=>sizeof($output_list), 'next_offset'=>0,'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
799 }
800
801 $server->register(
802         'logout',
803         array('session'=>'xsd:string'),
804         array('return'=>'tns:error_value'),
805         $NAMESPACE);
806
807 /**
808  * Log out of the session.  This will destroy the session and prevent other's from using it.
809  *
810  * @param String $session -- Session ID returned by a previous call to login.
811  * @return Empty error on success, Error on failure
812  */
813 function logout($session){
814         global $current_user;
815
816         $error = new SoapError();
817         LogicHook::initialize();
818         if(validate_authenticated($session)){
819                 $current_user->call_custom_logic('before_logout');
820                 session_destroy();
821                 $GLOBALS['logic_hook']->call_custom_logic('Users', 'after_logout');
822                 return $error->get_soap_array();
823         }
824         $error->set_error('no_session');
825         $GLOBALS['logic_hook']->call_custom_logic('Users', 'after_logout');
826         return $error->get_soap_array();
827 }
828
829 $server->register(
830     'get_module_fields',
831     array('session'=>'xsd:string', 'module_name'=>'xsd:string'),
832     array('return'=>'tns:module_fields'),
833     $NAMESPACE);
834
835 /**
836  * Retrieve vardef information on the fields of the specified bean.
837  *
838  * @param String $session -- Session ID returned by a previous call to login.
839  * @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)..
840  * @return Array    'module_fields' -- The vardef information on the selected fields.
841  *                  'error' -- The SOAP error, if any
842  */
843 function get_module_fields($session, $module_name){
844         global  $beanList, $beanFiles;
845         $error = new SoapError();
846         $module_fields = array();
847         if(! validate_authenticated($session)){
848                 $error->set_error('invalid_session');
849                 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
850         }
851         if(empty($beanList[$module_name])){
852                 $error->set_error('no_module');
853                 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
854         }
855         global $current_user;
856         if(!check_modules_access($current_user, $module_name, 'read')){
857                 $error->set_error('no_access');
858                 return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
859         }
860         $class_name = $beanList[$module_name];
861
862         if(empty($beanFiles[$class_name]))
863         {
864        $error->set_error('no_file');
865        return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
866         }
867
868         require_once($beanFiles[$class_name]);
869         $seed = new $class_name();
870         if($seed->ACLAccess('ListView', true) || $seed->ACLAccess('DetailView', true) ||        $seed->ACLAccess('EditView', true) )
871     {
872         return get_return_module_fields($seed, $module_name, $error);
873     }
874     else
875     {
876         $error->set_error('no_access');
877         return array('module_fields'=>$module_fields, 'error'=>$error->get_soap_array());
878     }
879 }
880
881 $server->register(
882     'get_available_modules',
883     array('session'=>'xsd:string'),
884     array('return'=>'tns:module_list'),
885     $NAMESPACE);
886
887 /**
888  * Retrieve the list of available modules on the system available to the currently logged in user.
889  *
890  * @param String $session -- Session ID returned by a previous call to login.
891  * @return Array    'modules' -- An array of module names
892  *                  'error' -- The SOAP error, if any
893  */
894 function get_available_modules($session){
895         $error = new SoapError();
896         $modules = array();
897         if(! validate_authenticated($session)){
898                 $error->set_error('invalid_session');
899                 return array('modules'=> $modules, 'error'=>$error->get_soap_array());
900         }
901         $modules = array_keys($_SESSION['avail_modules']);
902
903         return array('modules'=> $modules, 'error'=>$error->get_soap_array());
904 }
905
906
907 $server->register(
908     'update_portal_user',
909     array('session'=>'xsd:string', 'portal_name'=>'xsd:string', 'name_value_list'=>'tns:name_value_list'),
910     array('return'=>'tns:error_value'),
911     $NAMESPACE);
912
913 /**
914  * Update the properties of a contact that is portal user.  Add the portal user name to the user's properties.
915  *
916  * @param String $session -- Session ID returned by a previous call to login.
917  * @param String $portal_name -- The portal user_name of the contact
918  * @param Array $name_value_list -- collection of 'name'=>'value' pairs for finding the contact
919  * @return Empty error on success, Error on failure
920  */
921 function update_portal_user($session,$portal_name, $name_value_list){
922         global  $beanList, $beanFiles;
923         $error = new SoapError();
924         if(! validate_authenticated($session)){
925                 $error->set_error('invalid_session');
926                 return $error->get_soap_array();
927         }
928         $contact = new Contact();
929
930         $searchBy = array('deleted'=>0);
931         foreach($name_value_list as $name_value){
932                         $searchBy[$name_value['name']] = $name_value['value'];
933         }
934         if($contact->retrieve_by_string_fields($searchBy) != null){
935                 if(!$contact->duplicates_found){
936                         $contact->portal_name = $portal_name;
937                         $contact->portal_active = 1;
938                         if($contact->ACLAccess('Save')){
939                                 $contact->save();
940                         }else{
941                                 $error->set_error('no_access');
942                         }
943                         return $error->get_soap_array();
944                 }
945                 $error->set_error('duplicates');
946                 return $error->get_soap_array();
947         }
948         $error->set_error('no_records');
949         return $error->get_soap_array();
950 }
951
952 $server->register(
953     'get_user_id',
954     array('session'=>'xsd:string'),
955     array('return'=>'xsd:string'),
956     $NAMESPACE);
957
958 /**
959  * Return the user_id of the user that is logged into the current session.
960  *
961  * @param String $session -- Session ID returned by a previous call to login.
962  * @return String -- the User ID of the current session
963  *                  -1 on error.
964  */
965 function get_user_id($session){
966         if(validate_authenticated($session)){
967                 global $current_user;
968                 return $current_user->id;
969
970         }else{
971                 return '-1';
972         }
973 }
974
975 $server->register(
976     'get_user_team_id',
977     array('session'=>'xsd:string'),
978     array('return'=>'xsd:string'),
979     $NAMESPACE);
980
981 /**
982  * Return the ID of the default team for the user that is logged into the current session.
983  *
984  * @param String $session -- Session ID returned by a previous call to login.
985  * @return String -- the Team ID of the current user's default team
986  *                  1 for Community Edition
987  *                  -1 on error.
988  */
989 function get_user_team_id($session){
990         if(validate_authenticated($session))
991         {
992                  return 1;
993         }else{
994                 return '-1';
995         }
996 }
997
998 $server->register(
999     'get_server_time',
1000     array(),
1001     array('return'=>'xsd:string'),
1002     $NAMESPACE);
1003
1004 /**
1005  * 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.
1006  *
1007  * @return String -- The current date/time 'Y-m-d H:i:s'
1008  */
1009 function get_server_time(){
1010         return date('Y-m-d H:i:s');
1011 }
1012
1013 $server->register(
1014     'get_gmt_time',
1015     array(),
1016     array('return'=>'xsd:string'),
1017     $NAMESPACE);
1018
1019 /**
1020  * Return the current time on the server in the format 'Y-m-d H:i:s'.  This time is in GMT.
1021  *
1022  * @return String -- The current date/time 'Y-m-d H:i:s'
1023  */
1024 function get_gmt_time(){
1025         return TimeDate::getInstance()->nowDb();
1026 }
1027
1028 $server->register(
1029     'get_sugar_flavor',
1030     array(),
1031     array('return'=>'xsd:string'),
1032     $NAMESPACE);
1033
1034 /**
1035  * Retrieve the specific flavor of sugar.
1036  *
1037  * @return String   'CE' -- For Community Edition
1038  *                  'PRO' -- For Professional
1039  *                  'ENT' -- For Enterprise
1040  */
1041 function get_sugar_flavor(){
1042  global $sugar_flavor;
1043
1044  return $sugar_flavor;
1045 }
1046
1047
1048 $server->register(
1049     'get_server_version',
1050     array(),
1051     array('return'=>'xsd:string'),
1052     $NAMESPACE);
1053
1054 /**
1055  * Retrieve the version number of Sugar that the server is running.
1056  *
1057  * @return String -- The current sugar version number.
1058  *                   '1.0' on error.
1059  */
1060 function get_server_version(){
1061
1062         $admin  = new Administration();
1063         $admin->retrieveSettings('info');
1064         if(isset($admin->settings['info_sugar_version'])){
1065                 return $admin->settings['info_sugar_version'];
1066         }else{
1067                 return '1.0';
1068         }
1069
1070 }
1071
1072 $server->register(
1073     'get_relationships',
1074     array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'module_id'=>'xsd:string', 'related_module'=>'xsd:string', 'related_module_query'=>'xsd:string', 'deleted'=>'xsd:int'),
1075     array('return'=>'tns:get_relationships_result'),
1076     $NAMESPACE);
1077
1078 /**
1079  * Retrieve a collection of beans tha are related to the specified bean.
1080  * As of 4.5.1c, all combinations of related modules are supported
1081  *
1082  * @param String $session -- Session ID returned by a previous call to login.
1083  * @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)..
1084  * @param String $module_id -- The ID of the bean in the specified module
1085  * @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)..
1086  * @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.
1087  * @param Number $deleted -- false if deleted records should not be include, true if deleted records should be included.
1088  * @return unknown
1089  */
1090 function get_relationships($session, $module_name, $module_id, $related_module, $related_module_query, $deleted){
1091                 $error = new SoapError();
1092         $ids = array();
1093         if(!validate_authenticated($session)){
1094                 $error->set_error('invalid_login');
1095                 return array('ids'=>$ids,'error'=> $error->get_soap_array());
1096         }
1097         global  $beanList, $beanFiles;
1098         $error = new SoapError();
1099
1100         if(empty($beanList[$module_name]) || empty($beanList[$related_module])){
1101                 $error->set_error('no_module');
1102                 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1103         }
1104         $class_name = $beanList[$module_name];
1105         require_once($beanFiles[$class_name]);
1106         $mod = new $class_name();
1107         $mod->retrieve($module_id);
1108         if(!$mod->ACLAccess('DetailView')){
1109                 $error->set_error('no_access');
1110                 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1111         }
1112
1113         require_once 'include/SugarSQLValidate.php';
1114         $valid = new SugarSQLValidate();
1115         if(!$valid->validateQueryClauses($related_module_query)) {
1116         $GLOBALS['log']->error("Bad query: $related_module_query");
1117         $error->set_error('no_access');
1118             return array(
1119                         'result_count' => -1,
1120                         'error' => $error->get_soap_array()
1121                 );
1122     }
1123
1124     $id_list = get_linked_records($related_module, $module_name, $module_id);
1125
1126         if ($id_list === FALSE) {
1127                 $error->set_error('no_relationship_support');
1128                 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1129         }
1130         elseif (count($id_list) == 0) {
1131                 return array('ids'=>$ids, 'error'=>$error->get_soap_array());
1132         }
1133
1134         $list = array();
1135
1136         $in = "'".implode("', '", $id_list)."'";
1137
1138         $related_class_name = $beanList[$related_module];
1139         require_once($beanFiles[$related_class_name]);
1140         $related_mod = new $related_class_name();
1141
1142         $sql = "SELECT {$related_mod->table_name}.id FROM {$related_mod->table_name} ";
1143
1144
1145         $sql .= " WHERE {$related_mod->table_name}.id IN ({$in}) ";
1146
1147         if (!empty($related_module_query)) {
1148                 $sql .= " AND ( {$related_module_query} )";
1149         }
1150
1151         $result = $related_mod->db->query($sql);
1152         while ($row = $related_mod->db->fetchByAssoc($result)) {
1153                 $list[] = $row['id'];
1154         }
1155
1156         $return_list = array();
1157
1158         foreach($list as $id) {
1159                 $related_class_name = $beanList[$related_module];
1160                 $related_mod = new $related_class_name();
1161                 $related_mod->retrieve($id);
1162
1163                 $return_list[] = array(
1164                         'id' => $id,
1165                         'date_modified' => $related_mod->date_modified,
1166                         'deleted' => $related_mod->deleted
1167                 );
1168         }
1169
1170         return array('ids' => $return_list, 'error' => $error->get_soap_array());
1171 }
1172
1173
1174 $server->register(
1175     'set_relationship',
1176     array('session'=>'xsd:string','set_relationship_value'=>'tns:set_relationship_value'),
1177     array('return'=>'tns:error_value'),
1178     $NAMESPACE);
1179
1180 /**
1181  * Set a single relationship between two beans.  The items are related by module name and id.
1182  *
1183  * @param String $session -- Session ID returned by a previous call to login.
1184  * @param Array $set_relationship_value --
1185  *      '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)..
1186  *      'module1_id' -- The ID of the bean in the specified module
1187  *      '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)..
1188  *      'module2_id' -- The ID of the bean in the specified module
1189  * @return Empty error on success, Error on failure
1190  */
1191 function set_relationship($session, $set_relationship_value){
1192         $error = new SoapError();
1193         if(!validate_authenticated($session)){
1194                 $error->set_error('invalid_login');
1195                 return $error->get_soap_array();
1196         }
1197         return handle_set_relationship($set_relationship_value, $session);
1198 }
1199
1200 $server->register(
1201     'set_relationships',
1202     array('session'=>'xsd:string','set_relationship_list'=>'tns:set_relationship_list'),
1203     array('return'=>'tns:set_relationship_list_result'),
1204     $NAMESPACE);
1205
1206 /**
1207  * Setup several relationships between pairs of beans.  The items are related by module name and id.
1208  *
1209  * @param String $session -- Session ID returned by a previous call to login.
1210  * @param Array $set_relationship_list -- One for each relationship to setup.  Each entry is itself an array.
1211  *      '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)..
1212  *      'module1_id' -- The ID of the bean in the specified module
1213  *      '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)..
1214  *      'module2_id' -- The ID of the bean in the specified module
1215  * @return Empty error on success, Error on failure
1216  */
1217 function set_relationships($session, $set_relationship_list){
1218         $error = new SoapError();
1219         if(!validate_authenticated($session)){
1220                 $error->set_error('invalid_login');
1221                 return -1;
1222         }
1223         $count = 0;
1224         $failed = 0;
1225         foreach($set_relationship_list as $set_relationship_value){
1226                 $reter = handle_set_relationship($set_relationship_value, $session);
1227                 if($reter['number'] == 0){
1228                         $count++;
1229                 }else{
1230                         $failed++;
1231                 }
1232         }
1233         return array('created'=>$count , 'failed'=>$failed, 'error'=>$error);
1234 }
1235
1236
1237
1238 //INTERNAL FUNCTION NOT EXPOSED THROUGH SOAP
1239 /**
1240  * (Internal) Create a relationship between two beans.
1241  *
1242  * @param Array $set_relationship_value --
1243  *      '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)..
1244  *      'module1_id' -- The ID of the bean in the specified module
1245  *      '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)..
1246  *      'module2_id' -- The ID of the bean in the specified module
1247  * @return Empty error on success, Error on failure
1248  */
1249 function handle_set_relationship($set_relationship_value, $session='')
1250 {
1251     global  $beanList, $beanFiles;
1252     $error = new SoapError();
1253
1254     $module1 = $set_relationship_value['module1'];
1255     $module1_id = $set_relationship_value['module1_id'];
1256     $module2 = $set_relationship_value['module2'];
1257     $module2_id = $set_relationship_value['module2_id'];
1258
1259     if(empty($beanList[$module1]) || empty($beanList[$module2]) )
1260     {
1261         $error->set_error('no_module');
1262         return $error->get_soap_array();
1263     }
1264     $class_name = $beanList[$module1];
1265     require_once($beanFiles[$class_name]);
1266     $mod = new $class_name();
1267     $mod->retrieve($module1_id);
1268         if(!$mod->ACLAccess('DetailView')){
1269                 $error->set_error('no_access');
1270                 return $error->get_soap_array();
1271         }
1272         if($module1 == "Contacts" && $module2 == "Users"){
1273                 $key = 'contacts_users_id';
1274         }
1275         else{
1276         $key = array_search(strtolower($module2),$mod->relationship_fields);
1277         if(!$key) {
1278             $key = Relationship::retrieve_by_modules($module1, $module2, $GLOBALS['db']);
1279
1280             // BEGIN SnapLogic fix for bug 32064
1281             if ($module1 == "Quotes" && $module2 == "ProductBundles") {
1282                 // Alternative solution is perhaps to
1283                 // do whatever Sugar does when the same
1284                 // request is received from the web:
1285                 $pb_cls = $beanList[$module2];
1286                 $pb = new $pb_cls();
1287                 $pb->retrieve($module2_id);
1288
1289                 // Check if this relationship already exists
1290                 $query = "SELECT count(*) AS count FROM product_bundle_quote WHERE quote_id = '{$module1_id}' AND bundle_id = '{$module2_id}' AND deleted = '0'";
1291                 $result = $GLOBALS['db']->query($query, true, "Error checking for previously existing relationship between quote and product_bundle");
1292                 $row = $GLOBALS['db']->fetchByAssoc($result);
1293                 if(isset($row['count']) && $row['count'] > 0){
1294                     return $error->get_soap_array();
1295                 }
1296
1297                 $query = "SELECT MAX(bundle_index)+1 AS idx FROM product_bundle_quote WHERE quote_id = '{$module1_id}' AND deleted='0'";
1298                 $result = $GLOBALS['db']->query($query, true, "Error getting bundle_index");
1299                 $GLOBALS['log']->debug("*********** Getting max bundle_index");
1300                 $GLOBALS['log']->debug($query);
1301                 $row = $GLOBALS['db']->fetchByAssoc($result);
1302
1303                 $idx = 0;
1304                 if ($row) {
1305                     $idx = $row['idx'];
1306                 }
1307
1308                 $pb->set_productbundle_quote_relationship($module1_id,$module2_id,$idx);
1309                 $pb->save();
1310                 return $error->get_soap_array();
1311
1312             } else if ($module1 == "ProductBundles" && $module2 == "Products") {
1313                 // And, well, similar things apply in this case
1314                 $pb_cls = $beanList[$module1];
1315                 $pb = new $pb_cls();
1316                 $pb->retrieve($module1_id);
1317
1318                 // Check if this relationship already exists
1319                 $query = "SELECT count(*) AS count FROM product_bundle_product WHERE bundle_id = '{$module1_id}' AND product_id = '{$module2_id}' AND deleted = '0'";
1320                 $result = $GLOBALS['db']->query($query, true, "Error checking for previously existing relationship between quote and product_bundle");
1321                 $row = $GLOBALS['db']->fetchByAssoc($result);
1322                 if(isset($row['count']) && $row['count'] > 0){
1323                     return $error->get_soap_array();
1324                 }
1325
1326                 $query = "SELECT MAX(product_index)+1 AS idx FROM product_bundle_product WHERE bundle_id='{$module1_id}'";
1327                 $result = $GLOBALS['db']->query($query, true, "Error getting bundle_index");
1328                 $GLOBALS['log']->debug("*********** Getting max bundle_index");
1329                 $GLOBALS['log']->debug($query);
1330                 $row = $GLOBALS['db']->fetchByAssoc($result);
1331
1332                 $idx = 0;
1333                 if ($row) {
1334                     $idx = $row['idx'];
1335                 }
1336                 $pb->set_productbundle_product_relationship($module2_id,$idx,$module1_id);
1337                 $pb->save();
1338
1339                 $prod_cls = $beanList[$module2];
1340                 $prod = new $prod_cls();
1341                 $prod->retrieve($module2_id);
1342                 $prod->quote_id = $pb->quote_id;
1343                 $prod->save();
1344                 return $error->get_soap_array();
1345             }
1346             // END SnapLogic fix for bug 32064
1347
1348                 if (!empty($key)) {
1349                         $mod->load_relationship($key);
1350                         $mod->$key->add($module2_id);
1351                         return $error->get_soap_array();
1352                 } // if
1353         }
1354     }
1355
1356     if(!$key)
1357     {
1358         $error->set_error('no_module');
1359         return $error->get_soap_array();
1360     }
1361
1362     if(($module1 == 'Meetings' || $module1 == 'Calls') && ($module2 == 'Contacts' || $module2 == 'Users')){
1363         $key = strtolower($module2);
1364         $mod->load_relationship($key);
1365         $mod->$key->add($module2_id);
1366     }
1367     else if ($module1 == 'Contacts' && ($module2 == 'Notes' || $module2 == 'Calls' || $module2 == 'Meetings' || $module2 == 'Tasks') && !empty($session)){
1368         $mod->$key = $module2_id;
1369         $mod->save_relationship_changes(false);
1370         if (!empty($mod->account_id)) {
1371             // when setting a relationship from a Contact to these activities, if the Contacts is related to an Account,
1372             // we want to associate that Account to the activity as well
1373             $ret = set_relationship($session, array('module1'=>'Accounts', 'module1_id'=>$mod->account_id, 'module2'=>$module2, 'module2_id'=>$module2_id));
1374         }
1375     }
1376     else{
1377         $mod->$key = $module2_id;
1378         $mod->save_relationship_changes(false);
1379     }
1380
1381     return $error->get_soap_array();
1382 }
1383
1384
1385 $server->register(
1386         'set_document_revision',
1387         array('session'=>'xsd:string','note'=>'tns:document_revision'),
1388         array('return'=>'tns:set_entry_result'),
1389         $NAMESPACE);
1390
1391 /**
1392  * Enter description here...
1393  *
1394  * @param String $session -- Session ID returned by a previous call to login.
1395  * @param unknown_type $document_revision
1396  * @return unknown
1397  */
1398 function set_document_revision($session,$document_revision)
1399 {
1400
1401         $error = new SoapError();
1402         if(!validate_authenticated($session)){
1403                 $error->set_error('invalid_login');
1404                 return array('id'=>-1, 'error'=>$error->get_soap_array());
1405         }
1406
1407         require_once('modules/Documents/DocumentSoap.php');
1408         $dr = new DocumentSoap();
1409         return array('id'=>$dr->saveFile($document_revision), 'error'=>$error->get_soap_array());
1410
1411 }
1412
1413 $server->register(
1414         'search_by_module',
1415         array('user_name'=>'xsd:string','password'=>'xsd:string','search_string'=>'xsd:string', 'modules'=>'tns:select_fields', 'offset'=>'xsd:int', 'max_results'=>'xsd:int'),
1416         array('return'=>'tns:get_entry_list_result'),
1417         $NAMESPACE);
1418
1419 /**
1420  * Given a list of modules to search and a search string, return the id, module_name, along with the fields
1421  * as specified in the $query_array
1422  *
1423  * @param string $user_name             - username of the Sugar User
1424  * @param string $password                      - password of the Sugar User
1425  * @param string $search_string         - string to search
1426  * @param string[] $modules                     - array of modules to query
1427  * @param int $offset                           - a specified offset in the query
1428  * @param int $max_results                      - max number of records to return
1429  * @return get_entry_list_result        - id, module_name, and list of fields from each record
1430  */
1431 function search_by_module($user_name, $password, $search_string, $modules, $offset, $max_results){
1432         global  $beanList, $beanFiles;
1433
1434         $error = new SoapError();
1435     $hasLoginError = false;
1436
1437     if(empty($user_name) && !empty($password))
1438     {
1439         if(!validate_authenticated($password))
1440         {
1441             $hasLoginError = true;
1442         }
1443     } else if(!validate_user($user_name, $password)) {
1444                 $hasLoginError = true;
1445         }
1446
1447     //If there is a login error, then return the error here
1448     if($hasLoginError)
1449     {
1450         $error->set_error('invalid_login');
1451         return array('result_count'=>-1, 'entry_list'=>array(), 'error'=>$error->get_soap_array());
1452     }
1453
1454         global $current_user;
1455         if($max_results > 0){
1456                 global $sugar_config;
1457                 $sugar_config['list_max_entries_per_page'] = $max_results;
1458         }
1459         //  MRF - BUG:19552 - added a join for accounts' emails below
1460         $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"),
1461                                 'Bugs'=>array('where'=>array('Bugs' => array(0 => "bugs.name like '{0}%'", 1 => "bugs.bug_number = {0}")),'fields'=>"bugs.id, bugs.name, bugs.bug_number"),
1462                                                         'Cases'=>array('where'=>array('Cases' => array(0 => "cases.name like '{0}%'", 1 => "cases.case_number = {0}")),'fields'=>"cases.id, cases.name, cases.case_number"),
1463                                                         '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"),
1464                                                         'Project'=>array('where'=>array('Project' => array(0 => "project.name like '{0}%'")), 'fields'=>"project.id, project.name"),
1465                             'ProjectTask'=>array('where'=>array('ProjectTask' => array(0 => "project.id = '{0}'")), 'fields'=>"project_task.id, project_task.name"),
1466                                                         '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"),
1467                                                         'Opportunities'=>array('where'=>array('Opportunities' => array(0 => "opportunities.name like '{0}%'")), 'fields'=>"opportunities.id, opportunities.name"),
1468                                                         'Users'=>array('where'=>array('EmailAddresses' => array(0 => "ea.email_address like '{0}%'")),'fields'=>"users.id, users.user_name, users.first_name, ea.email_address"),
1469                                                 );
1470
1471         if(!empty($search_string) && isset($search_string)){
1472                 foreach($modules as $module_name){
1473                         $class_name = $beanList[$module_name];
1474                         require_once($beanFiles[$class_name]);
1475                         $seed = new $class_name();
1476                         if(empty($beanList[$module_name])){
1477                                 continue;
1478                         }
1479                         if(!check_modules_access($current_user, $module_name, 'read')){
1480                                 continue;
1481                         }
1482                         if(! $seed->ACLAccess('ListView'))
1483                         {
1484                                 continue;
1485                         }
1486
1487                         if(isset($query_array[$module_name])){
1488                                 $query = '';
1489                                 $tmpQuery = '';
1490                                 //split here to do while loop
1491                                 foreach($query_array[$module_name]['where'] as $key => $value){
1492                                         foreach($value as $where_clause){
1493                                                 $addQuery = true;
1494                                                 if(!empty($query))
1495                                                         $tmpQuery = ' UNION ';
1496                                                 $tmpQuery .= "SELECT ".$query_array[$module_name]['fields']." FROM $seed->table_name ";
1497                                                 // We need to confirm that the user is a member of the team of the item.
1498
1499
1500                                 if($module_name == 'ProjectTask'){
1501                                     $tmpQuery .= "INNER JOIN project ON $seed->table_name.project_id = project.id ";
1502                                 }
1503
1504                                 if(isset($seed->emailAddress) && $key == 'EmailAddresses'){
1505                                         $tmpQuery .= " INNER JOIN email_addr_bean_rel eabl  ON eabl.bean_id = $seed->table_name.id and eabl.deleted=0";
1506                                         $tmpQuery .= " INNER JOIN email_addresses ea ON (ea.id = eabl.email_address_id) ";
1507                                 }
1508                                                 $where = "WHERE (";
1509                                                 $search_terms = explode(", ", $search_string);
1510                                                 $termCount = count($search_terms);
1511                                                 $count = 1;
1512                                                 if($key != 'EmailAddresses'){
1513                                                         foreach($search_terms as $term){
1514                                                                 if(!strpos($where_clause, 'number')){
1515                                                                         $where .= string_format($where_clause,array($GLOBALS['db']->quote($term)));
1516                                                                 }elseif(is_numeric($term)){
1517                                                                         $where .= string_format($where_clause,array($GLOBALS['db']->quote($term)));
1518                                                                 }else{
1519                                                                         $addQuery = false;
1520                                                                 }
1521                                                                 if($count < $termCount){
1522                                                                         $where .= " OR ";
1523                                                                 }
1524                                                                 $count++;
1525                                                         }
1526                                                 }else{
1527                             $where .= '(';
1528                             foreach ($search_terms as $term)
1529                             {
1530                                 $where .= "ea.email_address LIKE '".$GLOBALS['db']->quote($term)."'";
1531                                 if ($count < $termCount)
1532                                 {
1533                                     $where .= " OR ";
1534                                 }
1535                                 $count++;
1536                             }
1537                             $where .= ')';
1538                                                 }
1539                                                 $tmpQuery .= $where;
1540                                                 $tmpQuery .= ") AND $seed->table_name.deleted = 0";
1541                                                 if($addQuery)
1542                                                         $query .= $tmpQuery;
1543                                         }
1544                                 }
1545                                 //grab the items from the db
1546                                 $result = $seed->db->query($query, $offset, $max_results);
1547
1548                                 while(($row = $seed->db->fetchByAssoc($result)) != null){
1549                                         $list = array();
1550                                         $fields = explode(", ", $query_array[$module_name]['fields']);
1551                                         foreach($fields as $field){
1552                                                 $field_names = explode(".", $field);
1553                                                 $list[$field] = array('name'=>$field_names[1], 'value'=>$row[$field_names[1]]);
1554                                         }
1555
1556                                         $output_list[] = array('id'=>$row['id'],
1557                                                                            'module_name'=>$module_name,
1558                                                                            'name_value_list'=>$list);
1559                                         if(empty($field_list)){
1560                                                 $field_list = get_field_list($row);
1561                                         }
1562                                 }//end while
1563                         }
1564                 }//end foreach
1565         }
1566
1567         $next_offset = $offset + sizeof($output_list);
1568
1569         return array('result_count'=>sizeof($output_list), 'next_offset'=>$next_offset,'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
1570
1571 }//end function
1572
1573
1574 $server->register(
1575 'get_mailmerge_document',
1576 array('session'=>'xsd:string','file_name'=>'xsd:string', 'fields' => 'tns:select_fields'),
1577 array('return'=>'tns:get_sync_result_encoded'),
1578 $NAMESPACE);
1579
1580 /**
1581  * Get MailMerge document
1582  *
1583  * @param String $session -- Session ID returned by a previous call to login.
1584  * @param unknown_type $file_name
1585  * @param unknown_type $fields
1586  * @return unknown
1587  */
1588 function get_mailmerge_document($session, $file_name, $fields)
1589 {
1590     global  $beanList, $beanFiles, $app_list_strings;
1591     $error = new SoapError();
1592     if(!validate_authenticated($session))
1593     {
1594         $error->set_error('invalid_login');
1595         return array('result'=>'', 'error'=>$error->get_soap_array());
1596     }
1597     if(!preg_match('/^sugardata[\.\d\s]+\.php$/', $file_name)) {
1598         $error->set_error('no_records');
1599         return array('result'=>'', 'error'=>$error->get_soap_array());
1600     }
1601     $html = '';
1602
1603     $file_name = sugar_cached('MergedDocuments/').pathinfo($file_name, PATHINFO_BASENAME);
1604
1605     $master_fields = array();
1606     $related_fields = array();
1607
1608     if(file_exists($file_name))
1609     {
1610         include($file_name);
1611
1612         $class1 = $merge_array['master_module'];
1613         $beanL = $beanList[$class1];
1614         $bean1 = $beanFiles[$beanL];
1615         require_once($bean1);
1616         $seed1 = new $beanL();
1617
1618         if(!empty($merge_array['related_module']))
1619         {
1620             $class2 = $merge_array['related_module'];
1621             $beanR = $beanList[$class2];
1622             $bean2 = $beanFiles[$beanR];
1623             require_once($bean2);
1624             $seed2 = new $beanR();
1625         }
1626
1627         //parse fields
1628         //$token1 = strtolower($class1);
1629         if($class1 == 'Prospects'){
1630             $class1 = 'CampaignProspects';
1631         }
1632         foreach($fields as $field)
1633         {
1634             $pos = strpos(strtolower($field), strtolower($class1));
1635             $pos2 = strpos(strtolower($field), strtolower($class2));
1636             if($pos !== false){
1637                 $fieldName = str_replace(strtolower($class1).'_', '', strtolower($field));
1638                 array_push($master_fields, $fieldName);
1639             }else if($pos2 !== false){
1640                 $fieldName = str_replace(strtolower($class2).'_', '', strtolower($field));
1641                 array_push($related_fields, $fieldName);
1642             }
1643         }
1644
1645         $html = '<html ' . get_language_header() .'><body><table border = 1><tr>';
1646
1647         foreach($master_fields as $master_field){
1648             $html .= '<td>'.$class1.'_'.$master_field.'</td>';
1649         }
1650         foreach($related_fields as $related_field){
1651             $html .= '<td>'.$class2.'_'.$related_field.'</td>';
1652         }
1653         $html .= '</tr>';
1654
1655         $ids = $merge_array['ids'];
1656         $is_prospect_merge = ($seed1->object_name == 'Prospect');
1657         foreach($ids as $key=>$value){
1658             if($is_prospect_merge){
1659                 $seed1 = $seed1->retrieveTarget($key);
1660             }else{
1661                 $seed1->retrieve($key);
1662             }
1663             $html .= '<tr>';
1664             foreach($master_fields as $master_field){
1665                 if(isset($seed1->$master_field)){
1666                     if($seed1->field_name_map[$master_field]['type'] == 'enum'){
1667                         //pull in the translated dom
1668                          $html .='<td>'.$app_list_strings[$seed1->field_name_map[$master_field]['options']][$seed1->$master_field].'</td>';
1669                     }else{
1670                         $html .='<td>'.$seed1->$master_field.'</td>';
1671                     }
1672                 }
1673                 else{
1674                     $html .= '<td></td>';
1675                     }
1676             }
1677             if(isset($value) && !empty($value)){
1678                 $seed2->retrieve($value);
1679                 foreach($related_fields as $related_field){
1680                     if(isset($seed2->$related_field)){
1681                         if($seed2->field_name_map[$related_field]['type'] == 'enum'){
1682                             //pull in the translated dom
1683                             $html .='<td>'.$app_list_strings[$seed2->field_name_map[$related_field]['options']][$seed2->$related_field].'</td>';
1684                         }else{
1685                             $html .= '<td>'.$seed2->$related_field.'</td>';
1686                         }
1687                     }
1688                     else{
1689                         $html .= '<td></td>';
1690                     }
1691                 }
1692             }
1693             $html .= '</tr>';
1694         }
1695         $html .= "</table></body></html>";
1696      }
1697
1698     $result = base64_encode($html);
1699     return array('result' => $result, 'error' => $error);
1700 }
1701
1702 $server->register(
1703 'get_mailmerge_document2',
1704 array('session'=>'xsd:string','file_name'=>'xsd:string', 'fields' => 'tns:select_fields'),
1705 array('return'=>'tns:get_mailmerge_document_result'),
1706 $NAMESPACE);
1707
1708 /**
1709  * Enter description here...
1710  *
1711  * @param String $session -- Session ID returned by a previous call to login.
1712  * @param unknown_type $file_name
1713  * @param unknown_type $fields
1714  * @return unknown
1715  */
1716 function get_mailmerge_document2($session, $file_name, $fields)
1717 {
1718     global  $beanList, $beanFiles, $app_list_strings, $app_strings;
1719
1720     $error = new SoapError();
1721     if(!validate_authenticated($session))
1722     {
1723         $GLOBALS['log']->error('invalid_login');
1724         $error->set_error('invalid_login');
1725         return array('result'=>'', 'error'=>$error->get_soap_array());
1726     }
1727     if(!preg_match('/^sugardata[\.\d\s]+\.php$/', $file_name)) {
1728         $GLOBALS['log']->error($app_strings['ERR_NO_SUCH_FILE'] . " ({$file_name})");
1729         $error->set_error('no_records');
1730         return array('result'=>'', 'error'=>$error->get_soap_array());
1731     }
1732     $html = '';
1733
1734     $file_name = sugar_cached('MergedDocuments/').pathinfo($file_name, PATHINFO_BASENAME);
1735
1736     $master_fields = array();
1737     $related_fields = array();
1738
1739     if(file_exists($file_name))
1740     {
1741         include($file_name);
1742
1743         $class1 = $merge_array['master_module'];
1744         $beanL = $beanList[$class1];
1745         $bean1 = $beanFiles[$beanL];
1746         require_once($bean1);
1747         $seed1 = new $beanL();
1748
1749         if(!empty($merge_array['related_module']))
1750         {
1751             $class2 = $merge_array['related_module'];
1752             $beanR = $beanList[$class2];
1753             $bean2 = $beanFiles[$beanR];
1754             require_once($bean2);
1755             $seed2 = new $beanR();
1756         }
1757
1758         //parse fields
1759         //$token1 = strtolower($class1);
1760         if($class1 == 'Prospects'){
1761             $class1 = 'CampaignProspects';
1762         }
1763         foreach($fields as $field)
1764         {
1765                 $pos = strpos(strtolower($field), strtolower($class1));
1766             $pos2 = strpos(strtolower($field), strtolower($class2));
1767             if($pos !== false){
1768                 $fieldName = str_replace(strtolower($class1).'_', '', strtolower($field));
1769                 array_push($master_fields, $fieldName);
1770             }else if($pos2 !== false){
1771                 $fieldName = str_replace(strtolower($class2).'_', '', strtolower($field));
1772                 array_push($related_fields, $fieldName);
1773             }
1774         }
1775
1776         $html = '<html ' . get_language_header() . '><body><table border = 1><tr>';
1777
1778         foreach($master_fields as $master_field){
1779             $html .= '<td>'.$class1.'_'.$master_field.'</td>';
1780         }
1781         foreach($related_fields as $related_field){
1782             $html .= '<td>'.$class2.'_'.$related_field.'</td>';
1783         }
1784         $html .= '</tr>';
1785
1786         $ids = $merge_array['ids'];
1787         $resultIds = array();
1788         $is_prospect_merge = ($seed1->object_name == 'Prospect');
1789         if($is_prospect_merge){
1790                 $pSeed = $seed1;
1791         }
1792         foreach($ids as $key=>$value){
1793
1794             if($is_prospect_merge){
1795                 $seed1 = $pSeed->retrieveTarget($key);
1796             }else{
1797                 $seed1->retrieve($key);
1798             }
1799              $resultIds[] = array('name' => $seed1->module_name, 'value' => $key);
1800             $html .= '<tr>';
1801             foreach($master_fields as $master_field){
1802                 if(isset($seed1->$master_field)){
1803                     if($seed1->field_name_map[$master_field]['type'] == 'enum'){
1804                         //pull in the translated dom
1805                          $html .='<td>'.$app_list_strings[$seed1->field_name_map[$master_field]['options']][$seed1->$master_field].'</td>';
1806                     } else if ($seed1->field_name_map[$master_field]['type'] == 'multienum') {
1807
1808                         if(isset($app_list_strings[$seed1->field_name_map[$master_field]['options']]) )
1809                         {
1810                             $items = unencodeMultienum($seed1->$master_field);
1811                             $output = array();
1812                             foreach($items as $item) {
1813                                 if ( !empty($app_list_strings[$seed1->field_name_map[$master_field]['options']][$item]) )
1814                                 {
1815                                     array_push($output, $app_list_strings[$seed1->field_name_map[$master_field]['options']][$item]);
1816
1817                                 }
1818
1819                             } // foreach
1820
1821                             $encoded_output = encodeMultienumValue($output);
1822                             $html .= "<td>$encoded_output</td>";
1823
1824                         }
1825                     } else {
1826                        $html .='<td>'.$seed1->$master_field.'</td>';
1827                     }
1828                 }
1829                 else{
1830                     $html .= '<td></td>';
1831                     }
1832             }
1833             if(isset($value) && !empty($value)){
1834                 $resultIds[] = array('name' => $seed2->module_name, 'value' => $value);
1835                                 $seed2->retrieve($value);
1836                 foreach($related_fields as $related_field){
1837                     if(isset($seed2->$related_field)){
1838                         if($seed2->field_name_map[$related_field]['type'] == 'enum'){
1839                             //pull in the translated dom
1840                             $html .='<td>'.$app_list_strings[$seed2->field_name_map[$related_field]['options']][$seed2->$related_field].'</td>';
1841                         }else{
1842                             $html .= '<td>'.$seed2->$related_field.'</td>';
1843                         }
1844                     }
1845                     else{
1846                         $html .= '<td></td>';
1847                     }
1848                 }
1849             }
1850             $html .= '</tr>';
1851         }
1852         $html .= "</table></body></html>";
1853      }
1854     $result = base64_encode($html);
1855
1856     return array('html' => $result, 'name_value_list' => $resultIds, 'error' => $error);
1857 }
1858
1859 $server->register(
1860         'get_document_revision',
1861         array('session'=>'xsd:string','i'=>'xsd:string'),
1862         array('return'=>'tns:return_document_revision'),
1863         $NAMESPACE);
1864
1865 /**
1866  * This method is used as a result of the .htaccess lock down on the cache directory. It will allow a
1867  * properly authenticated user to download a document that they have proper rights to download.
1868  *
1869  * @param String $session -- Session ID returned by a previous call to login.
1870  * @param String $id      -- ID of the document revision to obtain
1871  * @return return_document_revision - this is a complex type as defined in SoapTypes.php
1872  */
1873 function get_document_revision($session,$id)
1874 {
1875     global $sugar_config;
1876
1877     $error = new SoapError();
1878     if(!validate_authenticated($session)){
1879         $error->set_error('invalid_login');
1880         return array('id'=>-1, 'error'=>$error->get_soap_array());
1881     }
1882
1883
1884     $dr = new DocumentRevision();
1885     $dr->retrieve($id);
1886     if(!empty($dr->filename)){
1887         $filename = "upload://{$dr->id}";
1888         $contents = base64_encode(sugar_file_get_contents($filename));
1889         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());
1890     }else{
1891         $error->set_error('no_records');
1892         return array('id'=>-1, 'error'=>$error->get_soap_array());
1893     }
1894
1895 }
1896 $server->register(
1897     'set_campaign_merge',
1898     array('session'=>'xsd:string', 'targets'=>'tns:select_fields', 'campaign_id'=>'xsd:string'),
1899     array('return'=>'tns:error_value'),
1900     $NAMESPACE);
1901 /**
1902 *   Once we have successfuly done a mail merge on a campaign, we need to notify Sugar of the targets
1903 *   and the campaign_id for tracking purposes
1904 *
1905 * @param session        the session id of the authenticated user
1906 * @param targets        a string array of ids identifying the targets used in the merge
1907 * @param campaign_id    the campaign_id used for the merge
1908 *
1909 * @return error_value
1910 */
1911 function set_campaign_merge($session,$targets, $campaign_id){
1912     $error = new SoapError();
1913     if(!validate_authenticated($session)){
1914         $error->set_error('invalid_login');
1915         return $error->get_soap_array();
1916     }
1917     if (empty($campaign_id) or !is_array($targets) or count($targets) == 0) {
1918         $GLOBALS['log']->debug('set_campaign_merge: Merge action status will not be updated, because, campaign_id is null or no targets were selected.');
1919     } else {
1920         require_once('modules/Campaigns/utils.php');
1921         campaign_log_mail_merge($campaign_id,$targets);
1922     }
1923
1924     return $error->get_soap_array();
1925 }
1926 $server->register(
1927     'get_entries_count',
1928     array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'query'=>'xsd:string', 'deleted' => 'xsd:int'),
1929     array('return'=>'tns:get_entries_count_result'),
1930     $NAMESPACE);
1931
1932 /**
1933 *   Retrieve number of records in a given module
1934 *
1935 * @param session        the session id of the authenticated user
1936 * @param module_name    module to retrieve number of records from
1937 * @param query          allows webservice user to provide a WHERE clause
1938 * @param deleted        specify whether or not to include deleted records
1939 *
1940 @return get_entries_count_result - this is a complex type as defined in SoapTypes.php
1941 */
1942 function get_entries_count($session, $module_name, $query, $deleted) {
1943         global $beanList, $beanFiles, $current_user;
1944
1945         $error = new SoapError();
1946
1947         if (!validate_authenticated($session)) {
1948                 $error->set_error('invalid_login');
1949                 return array(
1950                         'result_count' => -1,
1951                         'error' => $error->get_soap_array()
1952                 );
1953         }
1954
1955         if (empty($beanList[$module_name])) {
1956                 $error->set_error('no_module');
1957                 return array(
1958                         'result_count' => -1,
1959                         'error' => $error->get_soap_array()
1960                 );
1961         }
1962
1963         if(!check_modules_access($current_user, $module_name, 'list')){
1964                 $error->set_error('no_access');
1965                 return array(
1966                         'result_count' => -1,
1967                         'error' => $error->get_soap_array()
1968                 );
1969         }
1970
1971         $class_name = $beanList[$module_name];
1972         require_once($beanFiles[$class_name]);
1973         $seed = new $class_name();
1974
1975         if (!$seed->ACLAccess('ListView')) {
1976                 $error->set_error('no_access');
1977                 return array(
1978                         'result_count' => -1,
1979                         'error' => $error->get_soap_array()
1980                 );
1981         }
1982
1983         $sql = 'SELECT COUNT(*) result_count FROM ' . $seed->table_name . ' ';
1984
1985
1986     if (isset($seed->custom_fields)) {
1987         $customJoin = $seed->custom_fields->getJOIN();
1988         $sql .= $customJoin ? $customJoin['join'] : '';
1989     }
1990
1991         // build WHERE clauses, if any
1992         $where_clauses = array();
1993         if (!empty($query)) {
1994             require_once 'include/SugarSQLValidate.php';
1995             $valid = new SugarSQLValidate();
1996             if(!$valid->validateQueryClauses($query)) {
1997             $GLOBALS['log']->error("Bad query: $query");
1998                 $error->set_error('no_access');
1999                 return array(
2000                         'result_count' => -1,
2001                         'error' => $error->get_soap_array()
2002                 );
2003             }
2004                 $where_clauses[] = $query;
2005         }
2006         if ($deleted == 0) {
2007                 $where_clauses[] = $seed->table_name . '.deleted = 0';
2008         }
2009
2010         // if WHERE clauses exist, add them to query
2011         if (!empty($where_clauses)) {
2012                 $sql .= ' WHERE ' . implode(' AND ', $where_clauses);
2013         }
2014
2015         $res = $GLOBALS['db']->query($sql);
2016         $row = $GLOBALS['db']->fetchByAssoc($res);
2017
2018         return array(
2019                 'result_count' => $row['result_count'],
2020                 'error' => $error->get_soap_array()
2021         );
2022 }
2023
2024 $server->register(
2025     'set_entries_details',
2026     array('session'=>'xsd:string', 'module_name'=>'xsd:string',  'name_value_lists'=>'tns:name_value_lists', 'select_fields' => 'tns:select_fields'),
2027     array('return'=>'tns:set_entries_detail_result'),
2028     $NAMESPACE);
2029
2030 /**
2031  * Update or create a list of SugarBeans, returning details about the records created/updated
2032  *
2033  * @param String $session -- Session ID returned by a previous call to login.
2034  * @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)..
2035  * @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.
2036  * @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.
2037  * @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.
2038  *                  'error' -- The SOAP error if any.
2039  */
2040 function set_entries_details($session, $module_name, $name_value_lists, $select_fields) {
2041         $error = new SoapError();
2042
2043         if(!validate_authenticated($session)){
2044                 $error->set_error('invalid_login');
2045
2046                 return array(
2047                         'ids' => array(),
2048                         'error' => $error->get_soap_array()
2049                 );
2050         }
2051
2052         return handle_set_entries($module_name, $name_value_lists, $select_fields);
2053 }
2054
2055 // INTERNAL FUNCTION NOT EXPOSED THROUGH API
2056 function handle_set_entries($module_name, $name_value_lists, $select_fields = FALSE) {
2057         global $beanList, $beanFiles, $app_list_strings, $current_user;
2058
2059         $error = new SoapError();
2060         $ret_values = array();
2061
2062         if(empty($beanList[$module_name])){
2063                 $error->set_error('no_module');
2064                 return array('ids'=>array(), 'error'=>$error->get_soap_array());
2065         }
2066
2067     if(!check_modules_access($current_user, $module_name, 'write')){
2068                 $error->set_error('no_access');
2069                 return array('ids'=>-1, 'error'=>$error->get_soap_array());
2070         }
2071
2072         $class_name = $beanList[$module_name];
2073         require_once($beanFiles[$class_name]);
2074         $ids = array();
2075         $count = 1;
2076         $total = sizeof($name_value_lists);
2077
2078         foreach($name_value_lists as $name_value_list){
2079                 $seed = new $class_name();
2080
2081                 $seed->update_vcal = false;
2082
2083         //See if we can retrieve the seed by a given id value
2084                 foreach($name_value_list as $value)
2085         {
2086                         if($value['name'] == 'id')
2087             {
2088                                 $seed->retrieve($value['value']);
2089                                 break;
2090                         }
2091                 }
2092
2093
2094         $dataValues = array();
2095
2096                 foreach($name_value_list as $value)
2097         {
2098                         $val = $value['value'];
2099
2100                         if($seed->field_name_map[$value['name']]['type'] == 'enum' || $seed->field_name_map[$value['name']]['type'] == 'radioenum')
2101             {
2102                                 $vardef = $seed->field_name_map[$value['name']];
2103                                 if(isset($app_list_strings[$vardef['options']]) && !isset($app_list_strings[$vardef['options']][$val]) )
2104                 {
2105                             if ( in_array($val,$app_list_strings[$vardef['options']]) )
2106                     {
2107                                 $val = array_search($val,$app_list_strings[$vardef['options']]);
2108                             }
2109                         }
2110
2111                         } else if($seed->field_name_map[$value['name']]['type'] == 'multienum') {
2112
2113                 $vardef = $seed->field_name_map[$value['name']];
2114
2115                 if(isset($app_list_strings[$vardef['options']]) && !isset($app_list_strings[$vardef['options']][$value]) )
2116                 {
2117                                         $items = explode(",", $val);
2118                                         $parsedItems = array();
2119                                         foreach ($items as $item)
2120                     {
2121                                                 if ( in_array($item, $app_list_strings[$vardef['options']]) )
2122                         {
2123                                                         $keyVal = array_search($item,$app_list_strings[$vardef['options']]);
2124                                                         array_push($parsedItems, $keyVal);
2125                                                 }
2126                                         }
2127
2128                                 if (!empty($parsedItems))
2129                     {
2130                                                 $val = encodeMultienumValue($parsedItems);
2131                                 }
2132                         }
2133                         }
2134
2135             //Apply the non-empty values now since this will be used for duplicate checks
2136             //allow string or int of 0 to be updated if set.
2137             if(!empty($val) || ($val==='0' || $val===0))
2138             {
2139                 $seed->$value['name'] = $val;
2140             }
2141             //Store all the values in dataValues Array to apply later
2142             $dataValues[$value['name']] = $val;
2143                 }
2144
2145                 if($count == $total)
2146         {
2147                         $seed->update_vcal = false;
2148                 }
2149                 $count++;
2150
2151                 //Add the account to a contact
2152                 if($module_name == 'Contacts'){
2153                         $GLOBALS['log']->debug('Creating Contact Account');
2154                         add_create_account($seed);
2155                         $duplicate_id = check_for_duplicate_contacts($seed);
2156                         if($duplicate_id == null)
2157             {
2158                                 if($seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete')))
2159                 {
2160                     //Now apply the values, since this is not a duplicate we can just pass false for the $firstSync argument
2161                     apply_values($seed, $dataValues, false);
2162                                         $seed->save();
2163                                         if($seed->deleted == 1){
2164                                                 $seed->mark_deleted($seed->id);
2165                                         }
2166                                         $ids[] = $seed->id;
2167                                 }
2168                         }else{
2169                                 //since we found a duplicate we should set the sync flag
2170                                 if( $seed->ACLAccess('Save'))
2171                 {
2172                     //Determine if this is a first time sync.  We find out based on whether or not a contacts_users relationship exists
2173                     $seed->id = $duplicate_id;
2174                     $seed->load_relationship("user_sync");
2175                     $beans = $seed->user_sync->getBeans();
2176                     $first_sync = empty($beans);
2177
2178                     //Now apply the values and indicate whether or not this is a first time sync
2179                     apply_values($seed, $dataValues, $first_sync);
2180                                         $seed->contacts_users_id = $current_user->id;
2181                                         $seed->save();
2182                                         $ids[] = $duplicate_id;//we have a conflict
2183                                 }
2184                         }
2185
2186         } else if($module_name == 'Meetings' || $module_name == 'Calls'){
2187                         //we are going to check if we have a meeting in the system
2188                         //with the same outlook_id. If we do find one then we will grab that
2189                         //id and save it
2190                         if( $seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2191                                 if(empty($seed->id) && !isset($seed->id)){
2192                                         if(!empty($seed->outlook_id) && isset($seed->outlook_id)){
2193                                                 //at this point we have an object that does not have
2194                                                 //the id set, but does have the outlook_id set
2195                                                 //so we need to query the db to find if we already
2196                                                 //have an object with this outlook_id, if we do
2197                                                 //then we can set the id, otherwise this is a new object
2198                                                 $order_by = "";
2199                                                 $query = $seed->table_name.".outlook_id = '".$seed->outlook_id."'";
2200                                                 $response = $seed->get_list($order_by, $query, 0,-1,-1,0);
2201                                                 $list = $response['list'];
2202                                                 if(count($list) > 0){
2203                                                         foreach($list as $value)
2204                                                         {
2205                                                                 $seed->id = $value->id;
2206                                                                 break;
2207                                                         }
2208                                                 }//fi
2209                                         }//fi
2210                                 }//fi
2211                                 if (empty($seed->reminder_time)) {
2212                     $seed->reminder_time = -1;
2213                 }
2214                                 if($seed->reminder_time == -1){
2215                                         $defaultRemindrTime = $current_user->getPreference('reminder_time');
2216                                         if ($defaultRemindrTime != -1){
2217                         $seed->reminder_checked = '1';
2218                         $seed->reminder_time = $defaultRemindrTime;
2219                                         }
2220                                 }
2221                                 $seed->save();
2222                                 if ($seed->deleted == 1) {
2223                                         $seed->mark_deleted($seed->id);
2224                                 }
2225                                 $ids[] = $seed->id;
2226                         }//fi
2227                 }
2228                 else
2229                 {
2230                         if( $seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2231                                 $seed->save();
2232                                 $ids[] = $seed->id;
2233                         }
2234                 }
2235
2236                 // if somebody is calling set_entries_detail() and wants fields returned...
2237                 if ($select_fields !== FALSE) {
2238                         $ret_values[$count] = array();
2239
2240                         foreach ($select_fields as $select_field) {
2241                                 if (isset($seed->$select_field)) {
2242                                         $ret_values[$count][] = get_name_value($select_field, $seed->$select_field);
2243                                 }
2244                         }
2245                 }
2246         }
2247
2248         // handle returns for set_entries_detail() and set_entries()
2249         if ($select_fields !== FALSE) {
2250                 return array(
2251                         'name_value_lists' => $ret_values,
2252                         'error' => $error->get_soap_array()
2253                 );
2254         }
2255         else {
2256                 return array(
2257                         'ids' => $ids,
2258                         'error' => $error->get_soap_array()
2259                 );
2260         }
2261 }
2262