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