]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/SugarFeed/SugarFeed.php
Release 6.2.0
[Github/sugarcrm.git] / modules / SugarFeed / SugarFeed.php
1 <?PHP
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38
39 class SugarFeed extends Basic {
40         var $new_schema = true;
41         var $module_dir = 'SugarFeed';
42         var $object_name = 'SugarFeed';
43         var $table_name = 'sugarfeed';
44         var $importable = false;
45
46                 var $id;
47                 var $name;
48                 var $date_entered;
49                 var $date_modified;
50                 var $modified_user_id;
51                 var $modified_by_name;
52                 var $created_by;
53                 var $created_by_name;
54                 var $description;
55                 var $deleted;
56                 var $created_by_link;
57                 var $modified_user_link;
58                 var $assigned_user_id;
59                 var $assigned_user_name;
60                 var $assigned_user_link;
61
62         function SugarFeed(){
63                 parent::Basic();
64         }
65
66     static function activateModuleFeed( $module, $updateDB = true ) {
67         if ( $module != 'UserFeed' ) {
68             // UserFeed is a fake module, used for the user postings to the feed
69             // Don't try to load up any classes for it
70             $fileList = SugarFeed::getModuleFeedFiles($module);
71
72             foreach ( $fileList as $fileName ) {
73                 $feedClass = substr(basename($fileName),0,-4);
74
75                 require_once($fileName);
76                 $tmpClass = new $feedClass();
77                 $tmpClass->installHook($fileName,$feedClass);
78             }
79         }
80         if ( $updateDB == true ) {
81
82             $admin = new Administration();
83             $admin->saveSetting('sugarfeed','module_'.$admin->db->quote($module),'1');
84         }
85     }
86
87     static function disableModuleFeed( $module, $updateDB = true ) {
88         if ( $module != 'UserFeed' ) {
89             // UserFeed is a fake module, used for the user postings to the feed
90             // Don't try to load up any classes for it
91             $fileList = SugarFeed::getModuleFeedFiles($module);
92
93             foreach ( $fileList as $fileName ) {
94                 $feedClass = substr(basename($fileName),0,-4);
95
96                 require_once($fileName);
97                 $tmpClass = new $feedClass();
98                 $tmpClass->removeHook($fileName,$feedClass);
99             }
100         }
101
102         if ( $updateDB == true ) {
103
104             $admin = new Administration();
105             $admin->saveSetting('sugarfeed','module_'.$admin->db->quote($module),'0');
106         }
107     }
108
109     static function flushBackendCache( ) {
110         // This function will flush the cache files used for the module list and the link type lists
111         sugar_cache_clear('SugarFeedModules');
112         if ( file_exists($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed/moduleCache.php') ) {
113             unlink($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed/moduleCache.php');
114         }
115
116         sugar_cache_clear('SugarFeedLinkType');
117         if ( file_exists($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed/linkTypeCache.php') ) {
118             unlink($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed/linkTypeCache.php');
119         }
120     }
121
122
123     static function getModuleFeedFiles( $module ) {
124         $baseDirList = array('modules/'.$module.'/SugarFeeds/', 'custom/modules/'.$module.'/SugarFeeds/');
125
126         // We store the files in a list sorted by the filename so you can override a default feed by
127         // putting your replacement feed in the custom directory with the same filename
128         $fileList = array();
129
130         foreach ( $baseDirList as $baseDir ) {
131             if ( ! file_exists($baseDir) ) {
132                 continue;
133             }
134             $d = dir($baseDir);
135             while ( $file = $d->read() ) {
136                 if ( $file{0} == '.' ) { continue; }
137                 if ( substr($file,-4) == '.php' ) {
138                     // We found one
139                     $fileList[$file] = $baseDir.$file;
140                 }
141             }
142         }
143
144         return($fileList);
145     }
146
147     static function getActiveFeedModules( ) {
148         // Stored in a cache somewhere
149         $feedModules = sugar_cache_retrieve('SugarFeedModules');
150         if ( $feedModules != null ) {
151             return($feedModules);
152         }
153
154         // Already stored in a file
155         if ( file_exists($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed/moduleCache.php') ) {
156             require_once($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed/moduleCache.php');
157             sugar_cache_put('SugarFeedModules',$feedModules);
158             return $feedModules;
159         }
160
161         // Gotta go looking for it
162
163         $admin = new Administration();
164         $admin->retrieveSettings();
165
166         $feedModules = array();
167         if ( isset($admin->settings['sugarfeed_enabled']) && $admin->settings['sugarfeed_enabled'] == '1' ) {
168             // Only enable modules if the feed system is enabled
169             foreach ( $admin->settings as $key => $value ) {
170                 if ( strncmp($key,'sugarfeed_module_',17) === 0 ) {
171                     // It's a module setting
172                     if ( $value == '1' ) {
173                         $moduleName = substr($key,17);
174                         $feedModules[$moduleName] = $moduleName;
175                     }
176                 }
177             }
178         }
179
180
181         sugar_cache_put('SugarFeedModules',$feedModules);
182         if ( ! file_exists($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed') ) { mkdir_recursive($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed'); }
183         $fd = fopen($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed/moduleCache.php','w');
184         fwrite($fd,'<'."?php\n\n".'$feedModules = '.var_export($feedModules,true).';');
185         fclose($fd);
186
187         return $feedModules;
188     }
189
190     static function getAllFeedModules( ) {
191         // Uncached, only used from the admin panel and during installation currently
192         $feedModules = array('UserFeed'=>'UserFeed');
193
194         $baseDirList = array('modules/', 'custom/modules/');
195         foreach ( $baseDirList as $baseDir ) {
196             if ( ! file_exists($baseDir) ) {
197                 continue;
198             }
199             $d = dir($baseDir);
200             while ( $module = $d->read() ) {
201                 if ( file_exists($baseDir.$module.'/SugarFeeds/') ) {
202                     $dFeed = dir($baseDir.$module.'/SugarFeeds/');
203                     while ( $file = $dFeed->read() ) {
204                         if ( $file{0} == '.' ) { continue; }
205                         if ( substr($file,-4) == '.php' ) {
206                             // We found one
207                             $feedModules[$module] = $module;
208                         }
209                     }
210                 }
211             }
212         }
213
214         return($feedModules);
215     }
216
217     /**
218      * pushFeed2
219      * This method is a wrapper to pushFeed
220      *
221      * @param $text String value of the feed's description
222      * @param $bean The SugarBean that is triggering the feed
223      * @param $link_type boolean value indicating whether or not feed is a link type
224      * @param $link_url String value of the URL (for link types only)
225      */
226     static function pushFeed2($text, $bean, $link_type=false, $link_url=false) {
227             self::pushFeed($text, $bean->module_dir, $bean->id
228                                                                 ,$bean->assigned_user_id
229                                                                 ,$link_type
230                                                                 ,$link_url
231             );
232     }
233
234         static function pushFeed($text, $module, $id,
235                 $record_assigned_user_id=false,
236                 $link_type=false,
237                 $link_url=false
238                 ) {
239                 $feed = new SugarFeed();
240                 if((empty($text) && empty($link_url)) || !$feed->ACLAccess('save', true) )
241                 {
242                         $GLOBALS['log']->error('Unable to save SugarFeed record (missing data or no ACL access)');
243                         return;
244                 }
245                 
246                 if(!empty($link_url)){
247             $linkClass = SugarFeed::getLinkClass($link_type);
248             if ( $linkClass !== FALSE ) {
249                 $linkClass->handleInput($feed,$link_type,$link_url);
250             }
251         }
252         $text = strip_tags(from_html($text));
253                 $text = '<b>{this.CREATED_BY}</b> ' . $text;
254                 $feed->name = substr($text, 0, 255);
255                 if(strlen($text) > 255){
256                         $feed->description = substr($text, 255, 510);
257                 }
258
259                 if ( $record_assigned_user_id === false ) {
260                         $feed->assigned_user_id = $GLOBALS['current_user']->id;
261                 } else {
262                         $feed->assigned_user_id = $record_assigned_user_id;
263                 }
264                 $feed->related_id = $id;
265                 $feed->related_module = $module;
266                 $feed->save();
267         }
268
269     static function getLinkTypes() {
270         static $linkTypeList = null;
271
272         // Fastest, already stored in the static variable
273         if ( $linkTypeList != null ) {
274             return $linkTypeList;
275         }
276
277         // Second fastest, stored in a cache somewhere
278         $linkTypeList = sugar_cache_retrieve('SugarFeedLinkType');
279         if ( $linkTypeList != null ) {
280             return($linkTypeList);
281         }
282
283         // Third fastest, already stored in a file
284         if ( file_exists($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed/linkTypeCache.php') ) {
285             require_once($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed/linkTypeCache.php');
286             sugar_cache_put('SugarFeedLinkType',$linkTypeList);
287             return $linkTypeList;
288         }
289
290         // Slow, have to actually collect the data
291         $baseDirs = array('custom/modules/SugarFeed/linkHandlers/','modules/SugarFeed/linkHandlers');
292
293         $linkTypeList = array();
294
295         foreach ( $baseDirs as $dirName ) {
296             if ( !file_exists($dirName) ) { continue; }
297             $d = dir($dirName);
298             while ( $file = $d->read() ) {
299                 if ( $file{0} == '.' ) { continue; }
300                 if ( substr($file,-4) == '.php' ) {
301                     // We found one
302                     $typeName = substr($file,0,-4);
303                     $linkTypeList[$typeName] = $typeName;
304                 }
305             }
306         }
307
308         sugar_cache_put('SugarFeedLinkType',$linkTypeList);
309         if ( ! file_exists($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed') ) { mkdir_recursive($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed'); }
310         $fd = fopen($GLOBALS['sugar_config']['cache_dir'].'modules/SugarFeed/linkTypeCache.php','w');
311         fwrite($fd,'<'."?php\n\n".'$linkTypeList = '.var_export($linkTypeList,true).';');
312         fclose($fd);
313
314         return $linkTypeList;
315     }
316
317     static function getLinkClass( $linkName ) {
318         $linkTypeList = SugarFeed::getLinkTypes();
319
320         // Have to make sure the linkName is on the list, so they can't pass in linkName's like ../../config.php ... not that they could get anywhere if they did
321         if ( ! isset($linkTypeList[$linkName]) ) {
322             // No class by this name...
323             return FALSE;
324         }
325
326         if ( file_exists('custom/modules/SugarFeed/linkHandlers/'.$linkName.'.php') ) {
327             require_once('custom/modules/SugarFeed/linkHandlers/'.$linkName.'.php');
328         } else {
329             require_once('modules/SugarFeed/linkHandlers/'.$linkName.'.php');
330         }
331
332         $linkClassName = 'FeedLinkHandler'.$linkName;
333
334         $linkClass = new $linkClassName();
335
336         return($linkClass);
337     }
338
339         function get_list_view_data(){
340                 $data = parent::get_list_view_data();
341                 $delete = '';
342                 if (ACLController::moduleSupportsACL($data['RELATED_MODULE']) && !ACLController::checkAccess($data['RELATED_MODULE'], 'view', $data['CREATED_BY'] == $GLOBALS['current_user']->id) && !ACLController::checkAccess($data['RELATED_MODULE'], 'list', $data['CREATED_BY'] == $GLOBALS['current_user']->id)){
343                         $data['NAME'] = '';
344                         return $data;
345                 }
346         if(is_admin($GLOBALS['current_user']) || (isset($data['CREATED_BY']) && $data['CREATED_BY'] == $GLOBALS['current_user']->id) ) {
347             $delete = ' - <a id="sugarFeedDeleteLink'.$data['ID'].'" href="#" onclick=\'SugarFeed.deleteFeed("'. $data['ID'] . '", "{this.id}"); return false;\'>'. $GLOBALS['app_strings']['LBL_DELETE_BUTTON_LABEL'].'</a>';
348         }
349                 $data['NAME'] .= $data['DESCRIPTION'];
350                 $data['NAME'] =  '<div style="padding:3px">' . html_entity_decode($data['NAME']);
351                 if(!empty($data['LINK_URL'])){
352             $linkClass = SugarFeed::getLinkClass($data['LINK_TYPE']);
353             if ( $linkClass !== FALSE ) {
354                 $data['NAME'] .= $linkClass->getDisplay($data);
355             }
356                 }
357         $data['NAME'] .= '<div class="byLineBox"><span class="byLineLeft">';
358                 $data['NAME'] .= $this->getTimeLapse($data['DATE_ENTERED']) . '&nbsp;</span><div class="byLineRight"><a id="sugarFeedReplyLink'.$data['ID'].'" href="#" onclick=\'SugarFeed.buildReplyForm("'.$data['ID'].'", "{this.id}", this); return false;\'>'.$GLOBALS['app_strings']['LBL_EMAIL_REPLY'].'</a>' .$delete. '</div></div>';
359
360         $data['NAME'] .= $this->fetchReplies($data);
361                 return  $data ;
362         }
363
364     function fetchReplies($data) {
365         $seedBean = new SugarFeed;
366
367         $replies = $seedBean->get_list('date_entered',"related_module = 'SugarFeed' AND related_id = '".$data['ID']."'");
368
369         if ( count($replies['list']) < 1 ) {
370             return '';
371         }
372
373
374         $replyHTML = '<div class="clear"></div><blockquote>';
375
376         foreach ( $replies['list'] as $reply ) {
377             // Setup the delete link
378             $delete = '';
379             if(is_admin($GLOBALS['current_user']) || $data['CREATED_BY'] == $GLOBALS['current_user']->id) {
380                 $delete = '<a id="sugarFieldDeleteLink'.$reply->id.'" href="#" onclick=\'SugarFeed.deleteFeed("'. $reply->id . '", "{this.id}"); return false;\'>'. $GLOBALS['app_strings']['LBL_DELETE_BUTTON_LABEL'].'</a>';
381             }
382
383             $image_url = 'include/images/default_user_feed_picture.png';
384             if ( isset($reply->created_by) ) {
385                 $user = loadBean('Users');
386                 $user->retrieve($reply->created_by);
387                 if ( !empty($user->picture) ) {
388                     $image_url = 'index.php?entryPoint=download&id='.$user->picture.'&type=SugarFieldImage&isTempFile=1';
389                 }
390             }
391             $replyHTML .= '<div style="float: left; margin-right: 3px; width: 50px; height: 50px;"><img src="'.$image_url.'" style="max-width: 50px; max-height: 50px;"></div> ';
392             $replyHTML .= str_replace("{this.CREATED_BY}",get_assigned_user_name($reply->created_by),html_entity_decode($reply->name)).'<br>';
393             $replyHTML .= '<div class="byLineBox"><span class="byLineLeft">'. $this->getTimeLapse($reply->date_entered) . '&nbsp;</span><div class="byLineRight">  &nbsp;' .$delete. '</div></div><div class="clear"></div>';
394         }
395
396         $replyHTML .= '</blockquote>';
397         return $replyHTML;
398
399     }
400
401         static function getTimeLapse($startDate)
402         {
403                 $seconds = $GLOBALS['timedate']->getNow()->ts - $GLOBALS['timedate']->fromUser($startDate)->ts;
404                 $minutes =   $seconds/60;
405                 $seconds = $seconds % 60;
406                 $hours = floor( $minutes / 60);
407                 $minutes = $minutes % 60;
408                 $days = floor( $hours / 24);
409                 $hours = $hours % 24;
410                 $weeks = floor( $days / 7);
411                 $days = $days % 7;
412                 $result = '';
413                 if($weeks == 1){
414                         $result = translate('LBL_TIME_LAST_WEEK','SugarFeed').' ';
415                         return $result;
416                 }else if($weeks > 1){
417                         $result .= $weeks . ' '.translate('LBL_TIME_WEEKS','SugarFeed').' ';
418                         if($days > 0) {
419                 $result .= $days . ' '.translate('LBL_TIME_DAYS','SugarFeed').' ';
420             }
421                 }else{
422                         if($days == 1){
423                                 $result = translate('LBL_TIME_YESTERDAY','SugarFeed').' ';
424                                 return $result;
425                         }else if($days > 1){
426                                 $result .= $days . ' '. translate('LBL_TIME_DAYS','SugarFeed').' ';
427                         }else{
428                                 if($hours == 1) {
429                     $result .= $hours . ' '.translate('LBL_TIME_HOUR','SugarFeed').' ';
430                 } else {
431                     $result .= $hours . ' '.translate('LBL_TIME_HOURS','SugarFeed').' ';
432                 }
433                                 if($hours < 6){
434                                         if($minutes == 1) {
435                         $result .= $minutes . ' ' . translate('LBL_TIME_MINUTE','SugarFeed'). ' ';
436                     } else {
437                         $result .= $minutes . ' ' . translate('LBL_TIME_MINUTES','SugarFeed'). ' ';
438                     }
439                                 }
440                                 if($hours == 0 && $minutes == 0) {
441                     if($seconds == 1 ) {
442                         $result = $seconds . ' ' . translate('LBL_TIME_SECOND','SugarFeed');
443                     } else {
444                         $result = $seconds . ' ' . translate('LBL_TIME_SECONDS','SugarFeed');
445                     }
446                 }
447                         }
448                 }
449                 return $result . ' ' . translate('LBL_TIME_AGO','SugarFeed');
450     }
451
452     /**
453      * Parse a piece of text and replace with proper display tags.
454      * @static
455      * @param  $input
456      * @return void
457      */
458     public static function parseMessage($input){
459         $urls = getUrls($input);
460         foreach($urls as $url){
461                         $output = "<a href='$url' target='_blank'>".$url."</a>";
462                         $input = str_replace($url, $output, $input);
463                 }
464                 return $input;
465     }
466
467
468 }