PageRenderTime 62ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/soap/SoapSugarUsers.php

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