]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - soap/SoapSugarUsers.php
Release 6.1.4
[Github/sugarcrm.git] / soap / SoapSugarUsers.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM 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 gmdate('Y-m-d H:i:s');
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         $handle = sugar_fopen($filename, "r");
1799         $contents = fread($handle, filesize($filename));
1800         fclose($handle);
1801         $contents = base64_encode($contents);
1802
1803         $fh = sugar_fopen($sugar_config['upload_dir']."/rogerrsmith.doc", 'w');
1804         fwrite($fh, base64_decode($contents));
1805         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());
1806     }else{
1807         $error->set_error('no_records');
1808         return array('id'=>-1, 'error'=>$error->get_soap_array());
1809     }
1810
1811 }
1812 $server->register(
1813     'set_campaign_merge',
1814     array('session'=>'xsd:string', 'targets'=>'tns:select_fields', 'campaign_id'=>'xsd:string'),
1815     array('return'=>'tns:error_value'),
1816     $NAMESPACE);
1817 /**
1818 *   Once we have successfuly done a mail merge on a campaign, we need to notify Sugar of the targets
1819 *   and the campaign_id for tracking purposes
1820 *
1821 * @param session        the session id of the authenticated user
1822 * @param targets        a string array of ids identifying the targets used in the merge
1823 * @param campaign_id    the campaign_id used for the merge
1824 *
1825 * @return error_value
1826 */
1827 function set_campaign_merge($session,$targets, $campaign_id){
1828     $error = new SoapError();
1829     if(!validate_authenticated($session)){
1830         $error->set_error('invalid_login');
1831         return $error->get_soap_array();
1832     }
1833     if (empty($campaign_id) or !is_array($targets) or count($targets) == 0) {
1834         $GLOBALS['log']->debug('set_campaign_merge: Merge action status will not be updated, because, campaign_id is null or no targets were selected.');
1835     } else {
1836         require_once('modules/Campaigns/utils.php');
1837         campaign_log_mail_merge($campaign_id,$targets);
1838     }
1839
1840     return $error->get_soap_array();
1841 }
1842 $server->register(
1843     'get_entries_count',
1844     array('session'=>'xsd:string', 'module_name'=>'xsd:string', 'query'=>'xsd:string', 'deleted' => 'xsd:int'),
1845     array('return'=>'tns:get_entries_count_result'),
1846     $NAMESPACE);
1847
1848 /**
1849 *   Retrieve number of records in a given module
1850 *
1851 * @param session        the session id of the authenticated user
1852 * @param module_name    module to retrieve number of records from
1853 * @param query          allows webservice user to provide a WHERE clause
1854 * @param deleted        specify whether or not to include deleted records
1855 *
1856 @return get_entries_count_result - this is a complex type as defined in SoapTypes.php
1857 */
1858 function get_entries_count($session, $module_name, $query, $deleted) {
1859         global $beanList, $beanFiles, $current_user;
1860
1861         $error = new SoapError();
1862
1863         if (!validate_authenticated($session)) {
1864                 $error->set_error('invalid_login');
1865                 return array(
1866                         'result_count' => -1,
1867                         'error' => $error->get_soap_array()
1868                 );
1869         }
1870
1871         if (empty($beanList[$module_name])) {
1872                 $error->set_error('no_module');
1873                 return array(
1874                         'result_count' => -1,
1875                         'error' => $error->get_soap_array()
1876                 );
1877         }
1878
1879         if(!check_modules_access($current_user, $module_name, 'list')){
1880                 $error->set_error('no_access');
1881                 return array(
1882                         'result_count' => -1,
1883                         'error' => $error->get_soap_array()
1884                 );
1885         }
1886
1887         $class_name = $beanList[$module_name];
1888         require_once($beanFiles[$class_name]);
1889         $seed = new $class_name();
1890
1891         if (!$seed->ACLAccess('ListView')) {
1892                 $error->set_error('no_access');
1893                 return array(
1894                         'result_count' => -1,
1895                         'error' => $error->get_soap_array()
1896                 );
1897         }
1898
1899         $sql = 'SELECT COUNT(*) result_count FROM ' . $seed->table_name . ' ';
1900
1901
1902         // build WHERE clauses, if any
1903         $where_clauses = array();
1904         if (!empty($query)) {
1905                 $where_clauses[] = $query;
1906         }
1907         if ($deleted == 0) {
1908                 $where_clauses[] = $seed->table_name . '.deleted = 0';
1909         }
1910
1911         // if WHERE clauses exist, add them to query
1912         if (!empty($where_clauses)) {
1913                 $sql .= ' WHERE ' . implode(' AND ', $where_clauses);
1914         }
1915
1916         $res = $GLOBALS['db']->query($sql);
1917         $row = $GLOBALS['db']->fetchByAssoc($res);
1918
1919         return array(
1920                 'result_count' => $row['result_count'],
1921                 'error' => $error->get_soap_array()
1922         );
1923 }
1924
1925 $server->register(
1926     'set_entries_details',
1927     array('session'=>'xsd:string', 'module_name'=>'xsd:string',  'name_value_lists'=>'tns:name_value_lists', 'select_fields' => 'tns:select_fields'),
1928     array('return'=>'tns:set_entries_detail_result'),
1929     $NAMESPACE);
1930
1931 /**
1932  * Update or create a list of SugarBeans, returning details about the records created/updated
1933  *
1934  * @param String $session -- Session ID returned by a previous call to login.
1935  * @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)..
1936  * @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.
1937  * @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.
1938  * @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.
1939  *                  'error' -- The SOAP error if any.
1940  */
1941 function set_entries_details($session, $module_name, $name_value_lists, $select_fields) {
1942         $error = new SoapError();
1943
1944         if(!validate_authenticated($session)){
1945                 $error->set_error('invalid_login');
1946
1947                 return array(
1948                         'ids' => array(),
1949                         'error' => $error->get_soap_array()
1950                 );
1951         }
1952
1953         return handle_set_entries($module_name, $name_value_lists, $select_fields);
1954 }
1955
1956 // INTERNAL FUNCTION NOT EXPOSED THROUGH API
1957 function handle_set_entries($module_name, $name_value_lists, $select_fields = FALSE) {
1958         global $beanList, $beanFiles, $app_list_strings;
1959
1960         $error = new SoapError();
1961         $ret_values = array();
1962
1963         if(empty($beanList[$module_name])){
1964                 $error->set_error('no_module');
1965                 return array('ids'=>array(), 'error'=>$error->get_soap_array());
1966         }
1967         global $current_user;
1968         if(!check_modules_access($current_user, $module_name, 'write')){
1969                 $error->set_error('no_access');
1970                 return array('ids'=>-1, 'error'=>$error->get_soap_array());
1971         }
1972
1973         $class_name = $beanList[$module_name];
1974         require_once($beanFiles[$class_name]);
1975         $ids = array();
1976         $count = 1;
1977         $total = sizeof($name_value_lists);
1978         foreach($name_value_lists as $name_value_list){
1979                 $seed = new $class_name();
1980
1981                 $seed->update_vcal = false;
1982                 foreach($name_value_list as $value){
1983                         if($value['name'] == 'id'){
1984                                 $seed->retrieve($value['value']);
1985                                 break;
1986                         }
1987                 }
1988
1989                 foreach($name_value_list as $value) {
1990                         $val = $value['value'];
1991                         if($seed->field_name_map[$value['name']]['type'] == 'enum' ||$seed->field_name_map[$value['name']]['type'] == 'radioenum'){
1992                                 $vardef = $seed->field_name_map[$value['name']];
1993                                 if(isset($app_list_strings[$vardef['options']]) && !isset($app_list_strings[$vardef['options']][$value]) ) {
1994                             if ( in_array($val,$app_list_strings[$vardef['options']]) ){
1995                                 $val = array_search($val,$app_list_strings[$vardef['options']]);
1996                             }
1997                         }
1998                         } else if($seed->field_name_map[$value['name']]['type'] == 'multienum') {
1999                                 $vardef = $seed->field_name_map[$value['name']];
2000                                 if(isset($app_list_strings[$vardef['options']]) && !isset($app_list_strings[$vardef['options']][$value]) ) {
2001                                         $items = explode(",", $val);
2002                                         $parsedItems = array();
2003                                         foreach ($items as $item) {
2004                                                 if ( in_array($item, $app_list_strings[$vardef['options']]) ){
2005                                                         $keyVal = array_search($item,$app_list_strings[$vardef['options']]);
2006                                                         array_push($parsedItems, $keyVal);
2007                                                 }
2008                                         }
2009                                 if (!empty($parsedItems)) {
2010                                                 $val = encodeMultienumValue($parsedItems);
2011                                 }
2012                         }                                       
2013                         }
2014                         $seed->$value['name'] = $val;
2015                 }
2016
2017                 if($count == $total){
2018                         $seed->update_vcal = false;
2019                 }
2020                 $count++;
2021
2022                 //Add the account to a contact
2023                 if($module_name == 'Contacts'){
2024                         $GLOBALS['log']->debug('Creating Contact Account');
2025                         add_create_account($seed);
2026                         $duplicate_id = check_for_duplicate_contacts($seed);
2027                         if($duplicate_id == null){
2028                                 if($seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2029                                         $seed->save();
2030                                         if($seed->deleted == 1){
2031                                                 $seed->mark_deleted($seed->id);
2032                                         }
2033                                         $ids[] = $seed->id;
2034                                 }
2035                         }
2036                         else{
2037                                 //since we found a duplicate we should set the sync flag
2038                                 if( $seed->ACLAccess('Save')){
2039                                         $seed->id = $duplicate_id;
2040                                         $seed->contacts_users_id = $current_user->id;
2041                                         $seed->save();
2042                                         $ids[] = $duplicate_id;//we have a conflict
2043                                 }
2044                         }
2045                 }
2046                 else if($module_name == 'Meetings' || $module_name == 'Calls'){
2047                         //we are going to check if we have a meeting in the system
2048                         //with the same outlook_id. If we do find one then we will grab that
2049                         //id and save it
2050                         if( $seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2051                                 if(empty($seed->id) && !isset($seed->id)){
2052                                         if(!empty($seed->outlook_id) && isset($seed->outlook_id)){
2053                                                 //at this point we have an object that does not have
2054                                                 //the id set, but does have the outlook_id set
2055                                                 //so we need to query the db to find if we already
2056                                                 //have an object with this outlook_id, if we do
2057                                                 //then we can set the id, otherwise this is a new object
2058                                                 $order_by = "";
2059                                                 $query = $seed->table_name.".outlook_id = '".$seed->outlook_id."'";
2060                                                 $response = $seed->get_list($order_by, $query, 0,-1,-1,0);
2061                                                 $list = $response['list'];
2062                                                 if(count($list) > 0){
2063                                                         foreach($list as $value)
2064                                                         {
2065                                                                 $seed->id = $value->id;
2066                                                                 break;
2067                                                         }
2068                                                 }//fi
2069                                         }//fi
2070                                 }//fi
2071                                 if (empty($seed->reminder_time)) {
2072                     $seed->reminder_time = -1;
2073                 }
2074                                 if($seed->reminder_time == -1){
2075                                         $defaultRemindrTime = $current_user->getPreference('reminder_time');
2076                                         if ($defaultRemindrTime != -1){
2077                         $seed->reminder_checked = '1';
2078                         $seed->reminder_time = $defaultRemindrTime;
2079                                         }
2080                                 }
2081                                 $seed->save();
2082                                 $ids[] = $seed->id;
2083                         }//fi
2084                 }
2085                 else
2086                 {
2087                         if( $seed->ACLAccess('Save') && ($seed->deleted != 1 || $seed->ACLAccess('Delete'))){
2088                                 $seed->save();
2089                                 $ids[] = $seed->id;
2090                         }
2091                 }
2092
2093                 // if somebody is calling set_entries_detail() and wants fields returned...
2094                 if ($select_fields !== FALSE) {
2095                         $ret_values[$count] = array();
2096
2097                         foreach ($select_fields as $select_field) {
2098                                 if (isset($seed->$select_field)) {
2099                                         $ret_values[$count][] = get_name_value($select_field, $seed->$select_field);
2100                                 }
2101                         }
2102                 }
2103         }
2104
2105         // handle returns for set_entries_detail() and set_entries()
2106         if ($select_fields !== FALSE) {
2107                 return array(
2108                         'name_value_lists' => $ret_values,
2109                         'error' => $error->get_soap_array()
2110                 );
2111         }
2112         else {
2113                 return array(
2114                         'ids' => $ids,
2115                         'error' => $error->get_soap_array()
2116                 );
2117         }
2118 }
2119
2120 ?>