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-2013 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.
47 class ExternalAPIFactory
50 * Filter the list of APIs, removing disabled ones
51 * @param array $apiFullList
52 * @return array Filtered list
54 public static function filterAPIList($apiFullList)
56 $filteredList = array();
57 foreach($apiFullList as $name => $data) {
58 if(isset($data['connector'])) {
59 if(ConnectorUtils::eapmEnabled($data['connector'])) {
60 if(isset($data['authMethod']) && $data['authMethod'] == 'oauth'){
61 $connector = SourceFactory::getSource($data['connector'], false);
62 if(!empty($connector) && $connector->propertyExists('oauth_consumer_key')
63 && $connector->propertyExists('oauth_consumer_secret')) {
64 $filteredList[$name] = $data;
67 $filteredList[$name] = $data;
71 $filteredList[$name] = $data;
78 * Get the list of available APIs
79 * @param bool $forceRebuild
80 * @param bool $ignoreDisabled Should we ignore disabled status?
83 public static function loadFullAPIList($forceRebuild=false, $ignoreDisabled = false) {
84 if (inDeveloperMode()) {
85 static $beenHereBefore = false;
86 if ( !$beenHereBefore ) {
88 $beenHereBefore = true;
91 $cached=sugar_cached('include/externalAPI.cache.php');
92 if (!$forceRebuild && file_exists($cached) ) {
93 // Already have a cache file built, no need to rebuild
96 return $ignoreDisabled?$fullAPIList:self::filterAPIList($fullAPIList);
99 $apiFullList = array();
100 $meetingPasswordList = array();
101 $needUrlList = array();
103 $baseDirList = array('include/externalAPI/','custom/include/externalAPI/');
104 foreach ( $baseDirList as $baseDir ) {
105 $dirList = glob($baseDir.'*',GLOB_ONLYDIR);
106 foreach($dirList as $dir) {
107 if ( $dir == $baseDir.'.' || $dir == $baseDir.'..' || $dir == $baseDir.'Base' ) {
111 $apiName = str_replace($baseDir,'',$dir);
112 if ( file_exists($dir.'/ExtAPI'.$apiName.'.php') ) {
113 $apiFullList[$apiName]['className'] = 'ExtAPI'.$apiName;
114 $apiFullList[$apiName]['file'] = $dir.'/'.$apiFullList[$apiName]['className'].'.php';
116 if ( file_exists($dir.'/ExtAPI'.$apiName.'_cstm.php') ) {
117 $apiFullList[$apiName]['className'] = 'ExtAPI'.$apiName.'_cstm';
118 $apiFullList[$apiName]['file_cstm'] = $dir.'/'.$apiFullList[$apiName]['className'].'.php';
123 $optionList = array('supportedModules','useAuth','requireAuth','supportMeetingPassword','docSearch', 'authMethod', 'oauthFixed','needsUrl','canInvite','sendsInvites','sharingOptions','connector', 'oauthParams','restrictUploadsByExtension');
124 foreach ( $apiFullList as $apiName => $apiOpts ) {
125 require_once($apiOpts['file']);
126 if ( !empty($apiOpts['file_cstm']) ) {
127 require_once($apiOpts['file_cstm']);
129 $className = $apiOpts['className'];
130 $apiClass = new $className();
131 foreach ( $optionList as $opt ) {
132 if ( isset($apiClass->$opt) ) {
133 $apiFullList[$apiName][$opt] = $apiClass->$opt;
137 // Special handling for the show/hide of the Meeting Password field, we need to create a dropdown for the Sugar Logic code.
138 if ( isset($apiClass->supportMeetingPassword) && $apiClass->supportMeetingPassword == true ) {
139 $meetingPasswordList[$apiName] = $apiName;
144 create_cache_directory('/include/');
145 $cached_tmp = sugar_cached('include/externalAPI.cache-tmp.php');
146 $fd = fopen($cached_tmp,'w');
147 fwrite($fd,"<"."?php\n//This file is auto generated by ".basename(__FILE__)."\n\$fullAPIList = ".var_export($apiFullList,true).";\n\n");
149 rename($cached_tmp, $cached);
151 $fd = fopen(sugar_cached('include/externalAPI.cache-tmp.js'),'w');
152 fwrite($fd,"//This file is auto generated by ".basename(__FILE__)."\nSUGAR.eapm = ".json_encode($apiFullList).";\n\n");
154 rename(sugar_cached('include/externalAPI.cache-tmp.js'),sugar_cached('include/externalAPI.cache.js'));
157 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 )) {
158 // Our meeting password list is different... we need to do something about this.
159 require_once('modules/Administration/Common.php');
160 $languages = get_languages();
161 foreach( $languages as $lang => $langLabel ) {
162 $contents = return_custom_app_list_strings_file_contents($lang);
163 $new_contents = replace_or_add_dropdown_type('extapi_meeting_password', $meetingPasswordList, $contents);
164 save_custom_app_list_strings_contents($new_contents, $lang);
168 return $ignoreDisabled?$apiFullList:self::filterAPIList($apiFullList);
172 * Clear API cache file
174 public static function clearCache() {
175 $cached=sugar_cached('include/externalAPI.cache.php');
176 if ( file_exists($cached) ) {
179 $cached=sugar_cached('include/externalAPI.cache.js');
180 if ( file_exists($cached) ) {
187 * 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.
188 * @param string $apiName The name of the requested API ( known API's can be listed by the listAPI() call )
189 * @param bool $apiName Ignore authentication requirements (optional)
190 * @return ExternalAPIBase API plugin
192 public static function loadAPI($apiName, $ignoreAuth=false)
194 $apiList = self::loadFullAPIList();
195 if ( ! isset($apiList[$apiName]) ) {
199 $myApi = $apiList[$apiName];
200 require_once($myApi['file']);
201 if ( !empty($myApi['file_cstm']) ) {
202 require_once($myApi['file_cstm']);
205 $apiClassName = $myApi['className'];
207 $apiClass = new $apiClassName();
212 if ($myApi['useAuth']) {
213 $eapmBean = EAPM::getLoginInfo($apiName);
215 if (!isset($eapmBean->application) && $myApi['requireAuth']) {
216 // We need authentication, and they don't have it, don't load the API
221 if ( $myApi['useAuth'] && isset($eapmBean->application) ) {
222 $apiClass->loadEAPM($eapmBean);
229 * 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
230 * @param string $module Which module name you are searching for, leave blank to find all API's
231 * @param bool $ignoreAuth Ignore API's demands for authentication (used to get a complete list of modules
234 public static function listAPI($module = '', $ignoreAuth = false) {
235 $apiList = self::loadFullAPIList();
237 if ( $module == '' && $ignoreAuth == true ) {
238 // Simplest case, return everything.
242 $apiFinalList = array();
244 // Not such an easy case, we need to limit to specific modules and see if we have authentication (or not)
245 foreach ( $apiList as $apiName => $apiOpts ) {
246 if ( $module == '' || in_array($module,$apiOpts['supportedModules']) ) {
247 // This matches the module criteria
248 if ( $ignoreAuth || !$apiOpts['useAuth'] || !$apiOpts['requireAuth'] ) {
249 // Don't need to worry about authentication
250 $apiFinalList[$apiName] = $apiOpts;
252 // We need to worry about authentication
253 $eapmBean = EAPM::getLoginInfo($apiName);
254 if ( isset($eapmBean->application) ) {
255 // We have authentication
256 $apiFinalList[$apiName] = $apiOpts;
262 return $apiFinalList;
266 * Get the array of API names available for cetain module
267 * @param string $moduleName
268 * @param bool $ignoreAuth Ignore if we have authentication details or not
269 * @param bool $addEmptyEntry Add empty entry?
271 public static function getModuleDropDown($moduleName, $ignoreAuth = false, $addEmptyEntry = false) {
272 global $app_list_strings;
274 $apiList = self::listAPI($moduleName,$ignoreAuth);
276 $apiDropdown = array();
278 $apiDropdown[''] = '';
281 foreach ( $apiList as $apiName => $ignore ) {
282 $appStringTranslKey = 'eapm_list_' .strtolower($moduleName);
283 if ( isset($app_list_strings[$appStringTranslKey]) && !empty($app_list_strings[$appStringTranslKey][$apiName]) ) {
284 $apiDropdown[$apiName] = $app_list_strings[$appStringTranslKey][$apiName];
286 else if ( !empty($app_list_strings['eapm_list'][$apiName]) ) {
287 $apiDropdown[$apiName] = $app_list_strings['eapm_list'][$apiName];
290 $apiDropdown[$apiName] = $apiName;