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