2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
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.
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
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
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.
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.
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 ********************************************************************************/
39 require_once('include/connectors/utils/ConnectorUtils.php');
40 require_once('include/connectors/sources/SourceFactory.php');
42 * Provides a factory to list, discover and create external API calls
44 * Main features are to list available external API's by supported features, modules and which ones have access for the user.
46 class ExternalAPIFactory
49 * Filter the list of APIs, removing disabled ones
50 * @param array $apiFullList
51 * @return array Filtered list
53 public static function filterAPIList($apiFullList)
55 $filteredList = array();
56 foreach($apiFullList as $name => $data) {
57 if(isset($data['connector'])) {
58 if(ConnectorUtils::eapmEnabled($data['connector'])) {
59 if(isset($data['authMethod']) && $data['authMethod'] == 'oauth'){
60 $connector = SourceFactory::getSource($data['connector'], false);
61 if(!empty($connector) && $connector->propertyExists('oauth_consumer_key')
62 && $connector->propertyExists('oauth_consumer_secret')) {
63 $filteredList[$name] = $data;
66 $filteredList[$name] = $data;
70 $filteredList[$name] = $data;
77 * Get the list of available APIs
78 * @param bool $forceRebuild
79 * @param bool $ignoreDisabled Should we ignore disabled status?
82 public static function loadFullAPIList($forceRebuild=false, $ignoreDisabled = false) {
83 if ( isset($GLOBALS['sugar_config']['developer_mode']) && $GLOBALS['sugar_config']['developer_mode'] ) {
84 static $beenHereBefore = false;
85 if ( !$beenHereBefore ) {
87 $beenHereBefore = true;
90 if (!$forceRebuild && file_exists('cache/include/externalAPI.cache.php') ) {
91 // Already have a cache file built, no need to rebuild
92 require('cache/include/externalAPI.cache.php');
94 return $ignoreDisabled?$fullAPIList:self::filterAPIList($fullAPIList);
97 $apiFullList = array();
98 $meetingPasswordList = array();
99 $needUrlList = array();
101 $baseDirList = array('include/externalAPI/','custom/include/externalAPI/');
102 foreach ( $baseDirList as $baseDir ) {
103 $dirList = glob($baseDir.'*',GLOB_ONLYDIR);
104 foreach($dirList as $dir) {
105 if ( $dir == $baseDir.'.' || $dir == $baseDir.'..' || $dir == $baseDir.'Base' ) {
109 $apiName = str_replace($baseDir,'',$dir);
110 if ( file_exists($dir.'/ExtAPI'.$apiName.'.php') ) {
111 $apiFullList[$apiName]['className'] = 'ExtAPI'.$apiName;
112 $apiFullList[$apiName]['file'] = $dir.'/'.$apiFullList[$apiName]['className'].'.php';
114 if ( file_exists($dir.'/ExtAPI'.$apiName.'_cstm.php') ) {
115 $apiFullList[$apiName]['className'] = 'ExtAPI'.$apiName.'_cstm';
116 $apiFullList[$apiName]['file_cstm'] = $dir.'/'.$apiFullList[$apiName]['className'].'.php';
121 $optionList = array('supportedModules','useAuth','requireAuth','supportMeetingPassword','docSearch', 'authMethod', 'oauthFixed','needsUrl','canInvite','sendsInvites','sharingOptions','connector', 'oauthParams','restrictUploadsByExtension');
122 foreach ( $apiFullList as $apiName => $apiOpts ) {
123 require_once($apiOpts['file']);
124 if ( !empty($apiOpts['file_cstm']) ) {
125 require_once($apiOpts['file_cstm']);
127 $className = $apiOpts['className'];
128 $apiClass = new $className();
129 foreach ( $optionList as $opt ) {
130 if ( isset($apiClass->$opt) ) {
131 $apiFullList[$apiName][$opt] = $apiClass->$opt;
135 // Special handling for the show/hide of the Meeting Password field, we need to create a dropdown for the Sugar Logic code.
136 if ( isset($apiClass->supportMeetingPassword) && $apiClass->supportMeetingPassword == true ) {
137 $meetingPasswordList[$apiName] = $apiName;
142 create_cache_directory('/include/');
143 $fd = fopen('cache/include/externalAPI.cache-tmp.php','w');
144 fwrite($fd,"<"."?php\n//This file is auto generated by ".__FILE__."\n\$fullAPIList = ".var_export($apiFullList,true).";\n\n");
146 rename('cache/include/externalAPI.cache-tmp.php','cache/include/externalAPI.cache.php');
148 $fd = fopen('cache/include/externalAPI.cache-tmp.js','w');
149 fwrite($fd,"//This file is auto generated by ".__FILE__."\nSUGAR.eapm = ".json_encode($apiFullList).";\n\n");
151 rename('cache/include/externalAPI.cache-tmp.js','cache/include/externalAPI.cache.js');
154 if (!isset($GLOBALS['app_list_strings']['extapi_meeting_password']) || (is_array($GLOBALS['app_list_strings']['extapi_meeting_password']) && count(array_diff($meetingPasswordList,$GLOBALS['app_list_strings']['extapi_meeting_password'])) != 0 )) {
155 // Our meeting password list is different... we need to do something about this.
156 require_once('modules/Administration/Common.php');
157 $languages = get_languages();
158 foreach( $languages as $lang => $langLabel ) {
159 $contents = return_custom_app_list_strings_file_contents($lang);
160 $new_contents = replace_or_add_dropdown_type('extapi_meeting_password', $meetingPasswordList, $contents);
161 save_custom_app_list_strings_contents($new_contents, $lang);
165 return $ignoreDisabled?$apiFullList:self::filterAPIList($apiFullList);
169 * Clear API cache file
171 public static function clearCache() {
172 if ( file_exists('cache/include/externalAPI.cache.php') ) {
173 unlink('cache/include/externalAPI.cache.php');
175 if ( file_exists('cache/include/externalAPI.cache.js') ) {
176 unlink('cache/include/externalAPI.cache.js');
182 * This will hand back an initialized class for the requested external API, it will also load in the external API password information into the bean.
183 * @param string $apiName The name of the requested API ( known API's can be listed by the listAPI() call )
184 * @param bool $apiName Ignore authentication requirements (optional)
185 * @return ExternalAPIBase API plugin
187 public static function loadAPI($apiName, $ignoreAuth=false)
189 $apiList = self::loadFullAPIList();
190 if ( ! isset($apiList[$apiName]) ) {
194 $myApi = $apiList[$apiName];
195 require_once($myApi['file']);
196 if ( !empty($myApi['file_cstm']) ) {
197 require_once($myApi['file_cstm']);
200 $apiClassName = $myApi['className'];
202 $apiClass = new $apiClassName();
207 if ($myApi['useAuth']) {
208 $eapmBean = EAPM::getLoginInfo($apiName);
210 if (!isset($eapmBean->application) && $myApi['requireAuth']) {
211 // We need authentication, and they don't have it, don't load the API
216 if ( $myApi['useAuth'] && isset($eapmBean->application) ) {
217 $apiClass->loadEAPM($eapmBean);
224 * Lists the available API's for a module or all modules, and possibly ignoring if the user has auth information for that API even if it is required
225 * @param string $module Which module name you are searching for, leave blank to find all API's
226 * @param bool $ignoreAuth Ignore API's demands for authentication (used to get a complete list of modules
229 public static function listAPI($module = '', $ignoreAuth = false) {
230 $apiList = self::loadFullAPIList();
232 if ( $module == '' && $ignoreAuth == true ) {
233 // Simplest case, return everything.
237 $apiFinalList = array();
239 // Not such an easy case, we need to limit to specific modules and see if we have authentication (or not)
240 foreach ( $apiList as $apiName => $apiOpts ) {
241 if ( $module == '' || in_array($module,$apiOpts['supportedModules']) ) {
242 // This matches the module criteria
243 if ( $ignoreAuth || !$apiOpts['useAuth'] || !$apiOpts['requireAuth'] ) {
244 // Don't need to worry about authentication
245 $apiFinalList[$apiName] = $apiOpts;
247 // We need to worry about authentication
248 $eapmBean = EAPM::getLoginInfo($apiName);
249 if ( isset($eapmBean->application) ) {
250 // We have authentication
251 $apiFinalList[$apiName] = $apiOpts;
257 return $apiFinalList;
261 * Get the array of API names available for cetain module
262 * @param string $moduleName
263 * @param bool $ignoreAuth Ignore if we have authentication details or not
264 * @param bool $addEmptyEntry Add empty entry?
266 public static function getModuleDropDown($moduleName, $ignoreAuth = false, $addEmptyEntry = false) {
267 global $app_list_strings;
269 $apiList = self::listAPI($moduleName,$ignoreAuth);
271 $apiDropdown = array();
273 $apiDropdown[''] = '';
276 foreach ( $apiList as $apiName => $ignore ) {
277 $appStringTranslKey = 'eapm_list_' .strtolower($moduleName);
278 if ( isset($app_list_strings[$appStringTranslKey]) && !empty($app_list_strings[$appStringTranslKey][$apiName]) ) {
279 $apiDropdown[$apiName] = $app_list_strings[$appStringTranslKey][$apiName];
281 else if ( !empty($app_list_strings['eapm_list'][$apiName]) ) {
282 $apiDropdown[$apiName] = $app_list_strings['eapm_list'][$apiName];
285 $apiDropdown[$apiName] = $apiName;