]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - soap/SoapSugarUsers.php
Release 6.4.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-2011 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 .= 'ea.email_address IN (';
1518                                                         foreach($search_terms as $term){
1519                                                                 $where .= "'".$GLOBALS['db']->quote($term)."'";
1520                                                                 if($count < $termCount){
1521                                                                         $where .= ",";
1522                                                                 }
1523                                                                 $count++;
1524                                                         }
1525                                                         $where .= ')';
1526                                                 }
1527                                                 $tmpQuery .= $where;
1528                                                 $tmpQuery .= ") AND $seed->table_name.deleted = 0";
1529                                                 if($addQuery)
1530                                                         $query .= $tmpQuery;
1531                                         }
1532                                 }
1533                                 //grab the items from the db
1534                                 $result = $seed->db->query($query, $offset, $max_results);
1535
1536                                 while(($row = $seed->db->fetchByAssoc($result)) != null){
1537                                         $list = array();
1538                                         $fields = explode(", ", $query_array[$module_name]['fields']);
1539                                         foreach($fields as $field){
1540                                                 $field_names = explode(".", $field);
1541                                                 $list[$field] = array('name'=>$field_names[1], 'value'=>$row[$field_names[1]]);
1542                                         }
1543
1544                                         $output_list[] = array('id'=>$row['id'],
1545                                                                            'module_name'=>$module_name,
1546                                                                            'name_value_list'=>$list);
1547                                         if(empty($field_list)){
1548                                                 $field_list = get_field_list($row);
1549                                         }
1550                                 }//end while
1551                         }
1552                 }//end foreach
1553         }
1554
1555         $next_offset = $offset + sizeof($output_list);
1556
1557         return array('result_count'=>sizeof($output_list), 'next_offset'=>$next_offset,'field_list'=>$field_list, 'entry_list'=>$output_list, 'error'=>$error->get_soap_array());
1558
1559 }//end function
1560
1561
1562 $server->register(
1563 'get_mailmerge_document',
1564 array('session'=>'xsd:string','file_name'=>'xsd:string', 'fields' => 'tns:select_fields'),
1565 array('return'=>'tns:get_sync_result_encoded'),
1566 $NAMESPACE);
1567
1568 /**
1569  * Get MailMerge document
1570  *
1571  * @param String $session -- Session ID returned by a previous call to login.
1572  * @param unknown_type $file_name
1573  * @param unknown_type $fields
1574  * @return unknown
1575  */
1576 function get_mailmerge_document($session, $file_name, $fields)
1577 {
1578     global  $beanList, $beanFiles, $app_list_strings;
1579     $error = new SoapError();
1580     if(!validate_authenticated($session))
1581     {
1582         $error->set_error('invalid_login');
1583         return array('result'=>'', 'error'=>$error->get_soap_array());
1584     }
1585     if(!preg_match('/^sugardata[\.\d\s]+\.php$/', $file_name)) {
1586         $error->set_error('no_records');
1587         return array('result'=>'', 'error'=>$error->get_soap_array());
1588     }
1589     $html = '';
1590
1591     $file_name = sugar_cached('MergedDocuments/').pathinfo($file_name, PATHINFO_BASENAME);
1592
1593     $master_fields = array();
1594     $related_fields = array();
1595
1596     if(file_exists($file_name))
1597     {
1598         include($file_name);
1599
1600         $class1 = $merge_array['master_module'];
1601         $beanL = $beanList[$class1];
1602         $bean1 = $beanFiles[$beanL];
1603         require_once($bean1);
1604         $seed1 = new $beanL();
1605
1606         if(!empty($merge_array['related_module']))
1607         {
1608             $class2 = $merge_array['related_module'];
1609             $beanR = $beanList[$class2];
1610             $bean2 = $beanFiles[$beanR];
1611             require_once($bean2);
1612             $seed2 = new $beanR();
1613         }
1614
1615         //parse fields
1616         //$token1 = strtolower($class1);
1617         if($class1 == 'Prospects'){
1618             $class1 = 'CampaignProspects';
1619         }
1620         foreach($fields as $field)
1621         {
1622             $pos = strpos(strtolower($field), strtolower($class1));
1623             $pos2 = strpos(strtolower($field), strtolower($class2));
1624             if($pos !== false){
1625                 $fieldName = str_replace(strtolower($class1).'_', '', strtolower($field));
1626                 array_push($master_fields, $fieldName);
1627             }else if($pos2 !== false){
1628                 $fieldName = str_replace(strtolower($class2).'_', '', strtolower($field));
1629                 array_push($related_fields, $fieldName);
1630             }
1631         }
1632
1633         $html = '<html ' . get_language_header() .'><body><table border = 1><tr>';
1634
1635         foreach($master_fields as $master_field){
1636             $html .= '<td>'.$class1.'_'.$master_field.'</td>';
1637         }
1638         foreach($related_fields as $related_field){
1639             $html .= '<td>'.$class2.'_'.$related_field.'</td>';
1640         }
1641         $html .= '</tr>';
1642
1643         $ids = $merge_array['ids'];
1644         $is_prospect_merge = ($seed1->object_name == 'Prospect');
1645         foreach($ids as $key=>$value){
1646             if($is_prospect_merge){
1647                 $seed1 = $seed1->retrieveTarget($key);
1648             }else{
1649                 $seed1->retrieve($key);
1650             }
1651             $html .= '<tr>';
1652             foreach($master_fields as $master_field){
1653                 if(isset($seed1->$master_field)){
1654                     if($seed1->field_name_map[$master_field]['type'] == 'enum'){
1655                         //pull in the translated dom
1656                          $html .='<td>'.$app_list_strings[$seed1->field_name_map[$master_field]['options']][$seed1->$master_field].'</td>';
1657                     }else{
1658                         $html .='<td>'.$seed1->$master_field.'</td>';
1659                     }
1660                 }
1661                 else{
1662                     $html .= '<td></td>';
1663                     }
1664             }
1665             if(isset($value) && !empty($value)){
1666                 $seed2->retrieve($value);
1667                 foreach($related_fields as $related_field){
1668                     if(isset($seed2->$related_field)){
1669                         if($seed2->field_name_map[$related_field]['type'] == 'enum'){
1670                             //pull in the translated dom
1671                             $html .='<td>'.$app_list_strings[$seed2->field_name_map[$related_field]['options']][$seed2->$related_field].'</td>';
1672                         }else{
1673                             $html .= '<td>'.$seed2->$related_field.'</td>';
1674                         }
1675                     }
1676                     else{
1677                         $html .= '<td></td>';
1678                     }
1679                 }
1680             }
1681             $html .= '</tr>';
1682         }
1683         $html .= "</table></body></html>";
1684      }
1685
1686     $result = base64_encode($html);
1687     return array('result' => $result, 'error' => $error);
1688 }
1689
1690 $server->register(
1691 'get_mailmerge_document2',
1692 array('session'=>'xsd:string','file_name'=>'xsd:string', 'fields' => 'tns:select_fields'),
1693 array('return'=>'tns:get_mailmerge_document_result'),
1694 $NAMESPACE);
1695
1696 /**
1697  * Enter description here...
1698  *
1699  * @param String $session -- Session ID returned by a previous call to login.
1700  * @param unknown_type $file_name
1701  * @param unknown_type $fields
1702  * @return unknown
1703  */
1704 function get_mailmerge_document2($session, $file_name, $fields)
1705 {
1706     global  $beanList, $beanFiles, $app_list_strings, $app_strings;
1707
1708     $error = new SoapError();
1709     if(!validate_authenticated($session))
1710     {
1711         $GLOBALS['log']->error('invalid_login');
1712         $error->set_error('invalid_login');
1713         return array('result'=>'', 'error'=>$error->get_soap_array());
1714     }
1715     if(!preg_match('/^sugardata[\.\d\s]+\.php$/', $file_name)) {
1716         $GLOBALS['log']->error($app_strings['ERR_NO_SUCH_FILE'] . " ({$file_name})");
1717         $error->set_error('no_records');
1718         return array('result'=>'', 'error'=>$error->get_soap_array());
1719     }
1720     $html = '';
1721
1722     $file_name = sugar_cached('MergedDocuments/').pathinfo($file_name, PATHINFO_BASENAME);
1723
1724     $master_fields = array();
1725     $related_fields = array();
1726
1727     if(file_exists($file_name))
1728     {
1729         include($file_name);
1730
1731         $class1 = $merge_array['master_module'];
1732         $beanL = $beanList[$class1];
1733         $bean1 = $beanFiles[$beanL];
1734         require_once($bean1);
1735         $seed1 = new $beanL();
1736
1737         if(!empty($merge_array['related_module']))
1738         {
1739             $class2 = $merge_array['related_module'];
1740             $beanR = $beanList[$class2];
1741             $bean2 = $beanFiles[$beanR];
1742             require_once($bean2);
1743             $seed2 = new $beanR();
1744         }
1745
1746         //parse fields
1747         //$token1 = strtolower($class1);
1748         if($class1 == 'Prospects'){
1749             $class1 = 'CampaignProspects';
1750         }
1751         foreach($fields as $field)
1752         {
1753                 $pos = strpos(strtolower($field), strtolower($class1));
1754             $pos2 = strpos(strtolower($field), strtolower($class2));
1755             if($pos !== false){
1756                 $fieldName = str_replace(strtolower($class1).'_', '', strtolower($field));
1757                 array_push($master_fields, $fieldName);
1758             }else if($pos2 !== false){
1759                 $fieldName = str_replace(strtolower($class2).'_', '', strtolower($field));
1760                 array_push($related_fields, $fieldName);
1761             }
1762         }
1763
1764         $html = '<html ' . get_language_header() . '><body><table border = 1><tr>';
1765
1766         foreach($master_fields as $master_field){
1767             $html .= '<td>'.$class1.'_'.$master_field.'</td>';
1768         }
1769         foreach($related_fields as $related_field){
1770             $html .= '<td>'.$class2.'_'.$related_field.'</td>';
1771         }
1772         $html .= '</tr>';
1773
1774         $ids = $merge_array['ids'];
1775         $resultIds = array();
1776         $is_prospect_merge = ($seed1->object_name == 'Prospect');
1777         if($is_prospect_merge){
1778                 $pSeed = $seed1;
1779         }
1780         foreach($ids as $key=>$value){
1781
1782             if($is_prospect_merge){
1783                 $seed1 = $pSeed->retrieveTarget($key);
1784             }else{
1785                 $seed1->retrieve($key);
1786             }
1787              $resultIds[] = array('name' => $seed1->module_name, 'value' => $key);
1788             $html .= '<tr>';
1789             foreach($master_fields as $master_field){
1790                 if(isset($seed1->$master_field)){
1791                     if($seed1->field_name_map[$master_field]['type'] == 'enum'){
1792                         //pull in the translated dom
1793                          $html .='<td>'.$app_list_strings[$seed1->field_name_map[$master_field]['options']][$seed1->$master_field].'</td>';
1794                     }else{
1795                         $html .='<td>'.$seed1->$master_field.'</td>';
1796                     }
1797                 }
1798                 else{
1799                     $html .= '<td></td>';
1800                     }
1801             }
1802             if(isset($value) && !empty($value)){
1803                 $resultIds[] = array('name' => $seed2->module_name, 'value' => $value);
1804                                 $seed2->retrieve($value);
1805                 foreach($related_fields as $related_field){
1806                     if(isset($seed2->$related_field)){
1807                         if($seed2->field_name_map[$related_field]['type'] == 'enum'){
1808                             //pull in the translated dom
1809                             $html .='<td>'.$app_list_strings[$seed2->field_name_map[$related_field]['options']][$seed2->$related_field].'</td>';
1810                         }else{
1811                             $html .= '<td>'.$seed2->$related_field.'</td>';
1812                         }
1813                     }
1814                     else{
1815                         $html .= '<td></td>';
1816                     }
1817                 }
1818             }
1819             $html .= '</tr>';
1820         }
1821         $html .= "</table></body></html>";
1822      }
1823
1824     $result = base64_encode($html);
1825     return array('html' => $result, 'name_value_list' => $resultIds, 'error' => $error);
1826 }
1827
1828 $server->register(
1829         'get_document_revision',
1830         array('session'=>'xsd:string','i'=>'xsd:string'),
1831         array('return'=>'tns:return_document_revision'),
1832         $NAMESPACE);
1833
1834 /**
1835  * This method is used as a result of the .htaccess lock down on the cache directory. It will allow a
1836  * properly authenticated user to download a document that they have proper rights to download.
1837  *
1838  * @param String $session -- Session ID returned by a previous call to login.
1839  * @param String $id      -- ID of the document revision to obtain
1840  * @return return_document_revision - this is a complex type as defined in SoapTypes.php
1841  */
1842 function get_document_revision($session,$id)
1843 {
1844     global $sugar_config;
1845
1846     $error = new SoapError();
1847     if(!validate_authenticated($session)){
1848         $error->set_error('invalid_login');
1849         return array('id'=>-1, 'error'=>$error->get_soap_array());
1850     }
1851
1852
1853     $dr = new DocumentRevision();
1854     $dr->retrieve($id);
1855     if(!empty($dr->filename)){
1856         $filename = "upload://{$dr->id}";
1857         $contents = base64_encode(sugar_file_get_contents($filename));
1858         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());
1859     }else{
1860         $error->set_error('no_records');
1861         return array('id'=>-1, 'error'=>$error->get_soap_array());
1862     }
1863
1864 }
1865 $server->register(
1866     'set_campaign_merge',
1867     array('session'=>'xsd:string', 'targets'=>'tns:select_fields', 'campaign_id'=>'xsd:string'),
1868     array('return'=>'tns:error_value'),
1869     $NAMESPACE);
1870 /**
1871 *   Once we have successfuly done a mail merge on a campaign, we need to notify Sugar of the targets
1872 *   and the campaign_id for tracking purposes
1873 *
1874 * @param session        the session id of the authenticated user
1875 * @param targets        a string array of ids identifying the targets used in the merge
1876 * @param campaign_id    the campaign_id used for the merge
1877 *
1878 * @return error_value
1879 */
1880 function set_campaign_merge($session,$targets, $campaign_id){
1881     $error = new SoapError();
1882     if(!validate_authenticated($session)){
1883         $error->set_error('invalid_login');
1884         return $error->get_soap_array();
1885     }
1886     if (empty($campaign_id) or !is_array($targets) or count($targets) == 0) {
1887         $GLOBALS['log']->debug('set_campaign_merge: Merge action status will not be updated, because, campaign_id is null or no targets were selected.');
1888     } else {
1889         require_once('modules/Campaigns/utils.php');
1890         campaign_log_mail_merge($campaign_id,$targets);
1891     }
1892
1893     return $error->get_soap_array();
1894 }
1895 $server->register(
1896     'get_entries_count',
1897     array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'query'=>'xsd:string', 'deleted' => 'xsd:int'),
1898     array('return'=>'tns:get_entries_count_result'),
1899     $NAMESPACE);
1900
1901 /**
1902 *   Retrieve number of records in a given module
1903 *
1904 * @param session        the session id of the authenticated user
1905 * @param module_name    module to retrieve number of records from
1906 * @param query          allows webservice user to provide a WHERE clause
1907 * @param deleted        specify whether or not to include deleted records
1908 *
1909 @return get_entries_count_result - this is a complex type as defined in SoapTypes.php
1910 */
1911 function get_entries_count($session, $module_name, $query, $deleted) {
1912         global $beanList, $beanFiles, $current_user;
1913
1914         $error = new SoapError();
1915
1916         if (!validate_authenticated($session)) {
1917                 $error->set_error('invalid_login');
1918                 return array(
1919                         'result_count' => -1,
1920                         'error' => $error->get_soap_array()
1921                 );
1922         }
1923
1924         if (empty($beanList[$module_name])) {
1925                 $error->set_error('no_module');
1926                 return array(
1927                         'result_count' => -1,
1928                         'error' => $error->get_soap_array()
1929                 );
1930         }
1931
1932         if(!check_modules_access($current_user, $module_name, 'list')){
1933                 $error->set_error('no_access');
1934                 return array(
1935                         'result_count' => -1,
1936                         'error' => $error->get_soap_array()
1937                 );
1938         }
1939
1940         $class_name = $beanList[$module_name];
1941         require_once($beanFiles[$class_name]);
1942         $seed = new $class_name();
1943
1944         if (!$seed->ACLAccess('ListView')) {
1945                 $error->set_error('no_access');
1946                 return array(
1947                         'result_count' => -1,
1948                         'error' => $error->get_soap_array()
1949                 );
1950         }
1951
1952         $sql = 'SELECT COUNT(*) result_count FROM ' . $seed->table_name . ' ';
1953
1954
1955         // build WHERE clauses, if any
1956         $where_clauses = array();
1957         if (!empty($query)) {
1958             require_once 'include/SugarSQLValidate.php';
1959             $valid = new SugarSQLValidate();
1960             if(!$valid->validateQueryClauses($query)) {
1961             $GLOBALS['log']->error("Bad query: $query");
1962                 $error->set_error('no_access');
1963                 return array(
1964                         'result_count' => -1,
1965                         'error' => $error->get_soap_array()
1966                 );
1967             }
1968                 $where_clauses[] = $query;
1969         }
1970         if ($deleted == 0) {
1971                 $where_clauses[] = $seed->table_name . '.deleted = 0';
1972         }
1973
1974         // if WHERE clauses exist, add them to query
1975         if (!empty($where_clauses)) {
1976                 $sql .= ' WHERE ' . implode(' AND ', $where_clauses);
1977         }
1978
1979         $res = $GLOBALS['db']->query($sql);
1980         $row = $GLOBALS['db']->fetchByAssoc($res);
1981
1982         return array(
1983                 'result_count' => $row['result_count'],
1984                 'error' => $error->get_soap_array()
1985         );
1986 }
1987
1988 $server->register(
1989     'set_entries_details',
1990     array('session'=>'xsd:string', 'module_name'=>'xsd:string',  'name_value_lists'=>'tns:name_value_lists', 'select_fields' => 'tns:select_fields'),
1991     array('return'=>'tns:set_entries_detail_result'),
1992     $NAMESPACE);
1993
1994 /**
1995  * Update or create a list of SugarBeans, returning details about the records created/updated
1996  *
1997  * @param String $session -- Session ID returned by a previous call to login.
1998  * @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)..
1999  * @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.
2000  * @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.
2001  * @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.
2002  *                  'error' -- The SOAP error if any.
2003  */
2004 function set_entries_details($session, $module_name, $name_value_lists, $select_fields) {
2005         $error = new SoapError();
2006
2007         if(!validate_authenticated($session)){
2008                 $error->set_error('invalid_login');
2009
2010                 return array(
2011                         'ids' => array(),
2012                         'error' => $error->get_soap_array()
2013                 );
2014         }
2015
2016         return handle_set_entries($module_name, $name_value_lists, $select_fields);
2017 }
2018
2019 // INTERNAL FUNCTION NOT EXPOSED THROUGH API
2020 function handle_set_entries($module_name, $name_value_lists, $select_fields = FALSE) {
2021         global $beanList, $beanFiles, $app_list_strings, $current_user;
2022
2023         $error = new SoapError();
2024         $ret_values = array();
2025
2026         if(empty($beanList[$module_name])){
2027                 $error->set_error('no_module');
2028                 return array('ids'=>array(), 'error'=>$error->get_soap_array());
2029         }
2030
2031     if(!check_modules_access($current_user, $module_name, 'write')){
2032                 $error->set_error('no_access');
2033                 return array('ids'=>-1, 'error'=>$error->get_soap_array());
2034         }
2035
2036         $class_name = $beanList[$module_name];
2037         require_once($beanFiles[$class_name]);
2038         $ids = array();
2039         $count = 1;
2040         $total = sizeof($name_value_lists);
2041
2042         foreach($name_value_lists as $name_value_list){
2043                 $seed = new $class_name();
2044
2045                 $seed->update_vcal = false;
2046
2047         //See if we can retrieve the seed by a given id value
2048                 foreach($name_value_list as $value)
2049         {
2050                         if($value['name'] == 'id')
2051             {
2052                                 $seed->retrieve($value['value']);
2053                                 break;
2054                         }
2055                 }
2056
2057
2058         $dataValues = array();
2059
2060                 foreach($name_value_list as $value)
2061         {
2062                         $val = $value['value'];
2063
2064                         if($seed->field_name_map[$value['name']]['type'] == 'enum' || $seed->field_name_map[$value['name']]['type'] == 'radioenum')
2065             {
2066                                 $vardef = $seed->field_name_map[$value['name']];
2067                                 if(isset($app_list_strings[$vardef['options']]) && !isset($app_list_strings[$vardef['options']][$val]) )
2068                 {
2069                             if ( in_array($val,$app_list_strings[$vardef['options']]) )
2070                     {
2071                                 $val = array_search($val,$app_list_strings[$vardef['options']]);
2072                             }
2073                         }
2074
2075                         } else if($seed->field_name_map[$value['name']]['type'] == 'multienum') {
2076
2077                 $vardef = $seed->field_name_map[$value['name']];
2078
2079                 if(isset($app_list_strings[$vardef['options']]) && !isset($app_list_strings[$vardef['options']][$value]) )
2080                 {
2081                                         $items = explode(",", $val);
2082                                         $parsedItems = array();
2083                                         foreach ($items as $item)
2084                     {
2085                                                 if ( in_array($item, $app_list_strings[$vardef['options']]) )
2086                         {
2087                                                         $keyVal = array_search($item,$app_list_strings[$vardef['options']]);
2088                                                         array_push($parsedItems, $keyVal);
2089                                                 }
2090                                         }
2091
2092                                 if (!empty($parsedItems))
2093                     {
2094                                                 $val = encodeMultienumValue($parsedItems);
2095                                 }
2096                         }
2097                         }
2098
2099             //Apply the non-empty values now since this will be used for duplicate checks
2100             if(!empty($val))
2101             {
2102                 $seed->$value['name'] = $val;
2103             }
2104             //Store all the values in dataValues Array to apply later
2105             $dataValues[$value['name']] = $val;
2106                 }
2107
2108                 if($count == $total)
2109         {
2110                         $seed->update_vcal = false;
2111                 }
2112                 $count++;
2113
2114                 //Add the account to a contact
2115                 if($module_name == 'Contacts'){
2116                         $GLOBALS['log']->debug('Creating Contact Account');
2117                         add_create_account($seed);
2118                         $duplicate_id = check_for_duplicate_contacts($seed);
2119                         if($duplicate_id == null)
2120             {
2121                                 if($seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete')))
2122                 {
2123                     //Now apply the values, since this is not a duplicate we can just pass false for the $firstSync argument
2124                     apply_values($seed, $dataValues, false);
2125                                         $seed->save();
2126                                         if($seed->deleted == 1){
2127                                                 $seed->mark_deleted($seed->id);
2128                                         }
2129                                         $ids[] = $seed->id;
2130                                 }
2131                         }else{
2132                                 //since we found a duplicate we should set the sync flag
2133                                 if( $seed->ACLAccess('Save'))
2134                 {
2135                     //Determine if this is a first time sync.  We find out based on whether or not a contacts_users relationship exists
2136                     $seed->id = $duplicate_id;
2137                     $seed->load_relationship("user_sync");
2138                     $beans = $seed->user_sync->getBeans();
2139                     $first_sync = empty($beans);
2140
2141                     //Now apply the values and indicate whether or not this is a first time sync
2142                     apply_values($seed, $dataValues, $first_sync);
2143                                         $seed->contacts_users_id = $current_user->id;
2144                                         $seed->save();
2145                                         $ids[] = $duplicate_id;//we have a conflict
2146                                 }
2147                         }
2148
2149         } else if($module_name == 'Meetings' || $module_name == 'Calls'){
2150                         //we are going to check if we have a meeting in the system
2151                         //with the same outlook_id. If we do find one then we will grab that
2152                         //id and save it
2153                         if( $seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2154                                 if(empty($seed->id) && !isset($seed->id)){
2155                                         if(!empty($seed->outlook_id) && isset($seed->outlook_id)){
2156                                                 //at this point we have an object that does not have
2157                                                 //the id set, but does have the outlook_id set
2158                                                 //so we need to query the db to find if we already
2159                                                 //have an object with this outlook_id, if we do
2160                                                 //then we can set the id, otherwise this is a new object
2161                                                 $order_by = "";
2162                                                 $query = $seed->table_name.".outlook_id = '".$seed->outlook_id."'";
2163                                                 $response = $seed->get_list($order_by, $query, 0,-1,-1,0);
2164                                                 $list = $response['list'];
2165                                                 if(count($list) > 0){
2166                                                         foreach($list as $value)
2167                                                         {
2168                                                                 $seed->id = $value->id;
2169                                                                 break;
2170                                                         }
2171                                                 }//fi
2172                                         }//fi
2173                                 }//fi
2174                                 if (empty($seed->reminder_time)) {
2175                     $seed->reminder_time = -1;
2176                 }
2177                                 if($seed->reminder_time == -1){
2178                                         $defaultRemindrTime = $current_user->getPreference('reminder_time');
2179                                         if ($defaultRemindrTime != -1){
2180                         $seed->reminder_checked = '1';
2181                         $seed->reminder_time = $defaultRemindrTime;
2182                                         }
2183                                 }
2184                                 $seed->save();
2185                                 $ids[] = $seed->id;
2186                         }//fi
2187                 }
2188                 else
2189                 {
2190                         if( $seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2191                                 $seed->save();
2192                                 $ids[] = $seed->id;
2193                         }
2194                 }
2195
2196                 // if somebody is calling set_entries_detail() and wants fields returned...
2197                 if ($select_fields !== FALSE) {
2198                         $ret_values[$count] = array();
2199
2200                         foreach ($select_fields as $select_field) {
2201                                 if (isset($seed->$select_field)) {
2202                                         $ret_values[$count][] = get_name_value($select_field, $seed->$select_field);
2203                                 }
2204                         }
2205                 }
2206         }
2207
2208         // handle returns for set_entries_detail() and set_entries()
2209         if ($select_fields !== FALSE) {
2210                 return array(
2211                         'name_value_lists' => $ret_values,
2212                         'error' => $error->get_soap_array()
2213                 );
2214         }
2215         else {
2216                 return array(
2217                         'ids' => $ids,
2218                         'error' => $error->get_soap_array()
2219                 );
2220         }
2221 }
2222
2223 ?>