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