]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/parsers/views/History.php
Release 6.4.0
[Github/sugarcrm.git] / modules / ModuleBuilder / parsers / views / History.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 require_once 'modules/ModuleBuilder/parsers/constants.php' ;
40 require_once 'modules/ModuleBuilder/parsers/views/HistoryInterface.php' ;
41
42 class History implements HistoryInterface
43 {
44
45     private $_dirname ; // base directory for the history files
46     private $_basename ; // base name for a history file, for example, listviewdef.php
47     private $_list ; // the history - a list of history files
48
49     private $_previewFilename ; // the location of a file for preview
50
51     /*
52      * Constructor
53      * @param string $previewFilename The filename which the caller expects for a preview file
54      */
55     public function __construct ( $previewFilename )
56     {
57         $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->__construct( {$previewFilename} )" ) ;
58         $this->_previewFilename = $previewFilename ;
59         $this->_list = array ( ) ;
60
61         $this->_basename = basename ( $this->_previewFilename ) ;
62         $this->_dirname = dirname ( $this->_previewFilename );
63             $this->_historyLimit = isset ( $GLOBALS [ 'sugar_config' ] [ 'studio_max_history' ] ) ? $GLOBALS [ 'sugar_config' ] [ 'studio_max_history' ] : 50 ;
64
65         // create the history directory if it does not already exist
66         if (!is_dir($this->_dirname)) {
67            mkdir_recursive($this->_dirname);
68          }
69         else
70         {
71             // Reconstruct the history from the saved files
72             $filenameList = glob($this->getFileByTimestamp('*'));
73             if (!empty($filenameList))
74             {
75                 foreach ($filenameList as $filename)
76                 {
77                     if(preg_match('/(\d+)$/', $filename, $match))
78                     {
79                         $this->_list [] = $match[1];
80                     }
81                 }
82             }
83         }
84         // now sort the files, oldest first
85         if (count ( $this->_list ) > 0)
86         {
87             sort ( $this->_list ) ;
88         }
89     }
90
91
92  /*
93      * Get the most recent item in the history
94      * @return timestamp of the first item
95      */
96     public function getCount ()
97     {
98         return count ( $this->_list ) ;
99     }
100
101     /*
102      * Get the most recent item in the history
103      * @return timestamp of the first item
104      */
105     public function getFirst ()
106     {
107         return end ( $this->_list ) ;
108     }
109
110 /*
111      * Get the oldest item in the history (the default layout)
112      * @return timestamp of the last item
113      */
114     public function getLast ()
115     {
116         return reset ( $this->_list ) ;
117     }
118
119     /*
120      * Get the next oldest item in the history
121      * @return timestamp of the next item
122      */
123     public function getNext ()
124     {
125         return prev ( $this->_list ) ;
126     }
127
128     /*
129      * Get the nth item in the history (where the zeroeth record is the most recent)
130      * @return timestamp of the nth item
131      */
132      public function getNth ( $index )
133     {
134         $value = end ( $this->_list ) ;
135         $i = 0 ;
136         while ( $i < $index )
137         {
138             $value = prev ( $this->_list ) ;
139             $i ++ ;
140         }
141         return $value ;
142     }
143
144     /*
145      * Add an item to the history
146      * @return String   A GMT Unix timestamp for this newly added item
147      */
148     public function append ($path)
149     {
150         // make sure we don't have a duplicate filename - highly unusual as two people should not be using Studio/MB concurrently, but when testing quite possible to do two appends within one second...
151         // because so unlikely in normal use we handle this the naive way by waiting a second so our naming scheme doesn't get overelaborated
152         $retries = 0 ;
153
154         $now = TimeDate::getInstance()->getNow();
155         $new_file = null;
156         for($retries = 0; !file_exists($new_file) && $retries < 5; $retries ++)
157         {
158             $now->modify("+1 second");
159             $time = $now->__get('ts');
160             $new_file = $this->getFileByTimestamp( $time );
161         }
162         // now we have a unique filename, copy the file into the history
163         copy ( $path, $new_file ) ;
164             $this->_list [ ] = $time ;
165
166         // finally, trim the number of files we're holding in the history to that specified in the configuration
167        // truncate the oldest files, keeping only the most recent $GLOBALS['sugar_config']['studio_max_history'] files (zero=keep them all)
168         $to_delete = $this->getCount() - $this->_historyLimit;
169         if ($this->_historyLimit != 0 && $to_delete)
170         {
171             // most recent files are at the end of the list, so we strip out the first count-max_history records
172             // can't just use array_shift because it renumbers numeric keys (our timestamp keys) to start from zero...
173             for ( $i = 0 ; $i < $to_delete ; $i ++ )
174             {
175                $timestamp = array_shift( $this->_list ) ;
176                if (! unlink ( $this->getFileByTimestamp( $timestamp ) ))
177                 {
178                     $GLOBALS [ 'log' ]->warn ( "History.php: unable to remove history file {$timestamp} from directory {$this->_dirname} - permissions problem?" ) ;                }
179             }
180         }
181
182         // finally, remove any history preview file that might be lurking around - as soon as we append a new record it supercedes any old preview, so that must be removed (bug 20130)
183         if (file_exists($this->_previewFilename))
184         {
185             $GLOBALS [ 'log' ]->debug( get_class($this)."->append(): removing old history file at {$this->_previewFilename}");
186             unlink ( $this->_previewFilename);
187         }
188
189         return $time ;
190     }
191
192     /*
193      * Restore the historical layout identified by timestamp
194      * @param Unix timestamp $timestamp GMT Timestamp of the layout to recover
195      * @return GMT Timestamp if successful, null if failure (if the file could not be copied for some reason)
196      */
197    public function restoreByTimestamp ($timestamp)
198     {
199          $filename = $this->getFileByTimestamp( $timestamp );
200         $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": restoring from $filename to {$this->_previewFilename}" ) ;
201
202         if (file_exists ( $filename ))
203         {
204             copy ( $filename, $this->_previewFilename ) ;
205             return $timestamp ;
206         }
207         return null ;
208     }
209
210     /*
211      * Undo the restore - revert back to the layout before the restore
212      */
213     public function undoRestore ()
214     {
215         if (file_exists ( $this->_previewFilename ))
216         {
217             unlink ( $this->_previewFilename ) ;
218         }
219     }
220
221     /**
222      * Returns full path to history file by timestamp. This function returns file path even if file doesn't exist
223      * @param  $timestamp
224      * @return string
225      */
226     public function getFileByTimestamp($timestamp)
227     {
228         return $this->_dirname . DIRECTORY_SEPARATOR . $this->_basename . '_' . $timestamp ;
229     }
230
231
232 }