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