source: trunk/zpush/lib/core/changesmemorywrapper.php @ 7589

Revision 7589, 12.8 KB checked in by douglas, 11 years ago (diff)

Ticket #3209 - Integrar módulo de sincronização Z-push ao Expresso

Line 
1<?php
2/***********************************************
3* File      :   changesmemorywrapper.php
4* Project   :   Z-Push
5* Descr     :   Class that collect changes in memory
6*
7* Created   :   18.08.2011
8*
9* Copyright 2007 - 2012 Zarafa Deutschland GmbH
10*
11* This program is free software: you can redistribute it and/or modify
12* it under the terms of the GNU Affero General Public License, version 3,
13* as published by the Free Software Foundation with the following additional
14* term according to sec. 7:
15*
16* According to sec. 7 of the GNU Affero General Public License, version 3,
17* the terms of the AGPL are supplemented with the following terms:
18*
19* "Zarafa" is a registered trademark of Zarafa B.V.
20* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
21* The licensing of the Program under the AGPL does not imply a trademark license.
22* Therefore any rights, title and interest in our trademarks remain entirely with us.
23*
24* However, if you propagate an unmodified version of the Program you are
25* allowed to use the term "Z-Push" to indicate that you distribute the Program.
26* Furthermore you may use our trademarks where it is necessary to indicate
27* the intended purpose of a product or service provided you use it in accordance
28* with honest practices in industrial or commercial matters.
29* If you want to propagate modified versions of the Program under the name "Z-Push",
30* you may only do so if you have a written permission by Zarafa Deutschland GmbH
31* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
32*
33* This program is distributed in the hope that it will be useful,
34* but WITHOUT ANY WARRANTY; without even the implied warranty of
35* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36* GNU Affero General Public License for more details.
37*
38* You should have received a copy of the GNU Affero General Public License
39* along with this program.  If not, see <http://www.gnu.org/licenses/>.
40*
41* Consult LICENSE file for details
42************************************************/
43
44
45class ChangesMemoryWrapper extends HierarchyCache implements IImportChanges, IExportChanges {
46    const CHANGE = 1;
47    const DELETION = 2;
48
49    private $changes;
50    private $step;
51    private $destinationImporter;
52    private $exportImporter;
53
54    /**
55     * Constructor
56     *
57     * @access public
58     * @return
59     */
60    public function ChangesMemoryWrapper() {
61        $this->changes = array();
62        $this->step = 0;
63        parent::HierarchyCache();
64    }
65
66    /**
67     * Only used to load additional folder sync information for hierarchy changes
68     *
69     * @param array    $state               current state of additional hierarchy folders
70     *
71     * @access public
72     * @return boolean
73     */
74    public function Config($state, $flags = 0) {
75        // we should never forward this changes to a backend
76        if (!isset($this->destinationImporter)) {
77            foreach($state as $addKey => $addFolder) {
78                ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->Config(AdditionalFolders) : process folder '%s'", $addFolder->displayname));
79                if (isset($addFolder->NoBackendFolder) && $addFolder->NoBackendFolder == true) {
80                    $hasRights = ZPush::GetBackend()->Setup($addFolder->Store, true, $addFolder->serverid);
81                    // delete the folder on the device
82                    if (! $hasRights) {
83                        // delete the folder only if it was an additional folder before, else ignore it
84                        $synchedfolder = $this->GetFolder($addFolder->serverid);
85                        if (isset($synchedfolder->NoBackendFolder) && $synchedfolder->NoBackendFolder == true)
86                            $this->ImportFolderDeletion($addFolder->serverid, $addFolder->parentid);
87                        continue;
88                    }
89                }
90                // add folder to the device - if folder is already on the device, nothing will happen
91                $this->ImportFolderChange($addFolder);
92            }
93
94            // look for folders which are currently on the device if there are now not to be synched anymore
95            $alreadyDeleted = $this->GetDeletedFolders();
96            foreach ($this->ExportFolders(true) as $sid => $folder) {
97                // we are only looking at additional folders
98                if (isset($folder->NoBackendFolder)) {
99                    // look if this folder is still in the list of additional folders and was not already deleted (e.g. missing permissions)
100                    if (!array_key_exists($sid, $state) && !array_key_exists($sid, $alreadyDeleted)) {
101                        ZLog::Write(LOGLEVEL_INFO, sprintf("ChangesMemoryWrapper->Config(AdditionalFolders) : previously synchronized folder '%s' is not to be synched anymore. Sending delete to mobile.", $folder->displayname));
102                        $this->ImportFolderDeletion($folder->serverid, $folder->parentid);
103                    }
104                }
105            }
106        }
107        return true;
108    }
109
110
111    /**
112     * Implement interfaces which are never used
113     */
114    public function GetState() { return false;}
115    public function LoadConflicts($contentparameters, $state) { return true; }
116    public function ConfigContentParameters($contentparameters) { return true; }
117    public function ImportMessageReadFlag($id, $flags) { return true; }
118    public function ImportMessageMove($id, $newfolder) { return true; }
119
120    /**----------------------------------------------------------------------------------------------------------
121     * IImportChanges & destination importer
122     */
123
124    /**
125     * Sets an importer where incoming changes should be sent to
126     *
127     * @param IImportChanges    $importer   message to be changed
128     *
129     * @access public
130     * @return boolean
131     */
132    public function SetDestinationImporter(&$importer) {
133        $this->destinationImporter = $importer;
134    }
135
136    /**
137     * Imports a message change, which is imported into memory
138     *
139     * @param string        $id         id of message which is changed
140     * @param SyncObject    $message    message to be changed
141     *
142     * @access public
143     * @return boolean
144     */
145    public function ImportMessageChange($id, $message) {
146        $this->changes[] = array(self::CHANGE, $id);
147        return true;
148    }
149
150    /**
151     * Imports a message deletion, which is imported into memory
152     *
153     * @param string        $id     id of message which is deleted
154     *
155     * @access public
156     * @return boolean
157     */
158    public function ImportMessageDeletion($id) {
159        $this->changes[] = array(self::DELETION, $id);
160        return true;
161    }
162
163    /**
164     * Checks if a message id is flagged as changed
165     *
166     * @param string        $id     message id
167     *
168     * @access public
169     * @return boolean
170     */
171    public function IsChanged($id) {
172        return (array_search(array(self::CHANGE, $id), $this->changes) === false) ? false:true;
173    }
174
175    /**
176     * Checks if a message id is flagged as deleted
177     *
178     * @param string        $id     message id
179     *
180     * @access public
181     * @return boolean
182     */
183    public function IsDeleted($id) {
184       return (array_search(array(self::DELETION, $id), $this->changes) === false) ? false:true;
185    }
186
187    /**
188     * Imports a folder change
189     *
190     * @param SyncFolder    $folder     folder to be changed
191     *
192     * @access public
193     * @return boolean
194     */
195    public function ImportFolderChange($folder) {
196        // if the destinationImporter is set, then this folder should be processed by another importer
197        // instead of being loaded in memory.
198        if (isset($this->destinationImporter)) {
199            // normally the $folder->type is not set, but we need this value to check if the change operation is permitted
200            // e.g. system folders can normally not be changed - set the type from cache and let the destinationImporter decide
201            if (!isset($folder->type)) {
202                $cacheFolder = $this->GetFolder($folder->serverid);
203                $folder->type = $cacheFolder->type;
204                ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->ImportFolderChange(): Set foldertype for folder '%s' from cache as it was not sent: '%s'", $folder->displayname, $folder->type));
205            }
206
207            $ret = $this->destinationImporter->ImportFolderChange($folder);
208
209            // if the operation was sucessfull, update the HierarchyCache
210            if ($ret) {
211                // for folder creation, the serverid is not set and has to be updated before
212                if (!isset($folder->serverid) || $folder->serverid == "")
213                    $folder->serverid = $ret;
214
215                $this->AddFolder($folder);
216            }
217            return $ret;
218        }
219        // load into memory
220        else {
221            if (isset($folder->serverid)) {
222                // The Zarafa HierarchyExporter exports all kinds of changes for folders (e.g. update no. of unread messages in a folder).
223                // These changes are not relevant for the mobiles, as something changes but the relevant displayname and parentid
224                // stay the same. These changes will be dropped and are not sent!
225                $cacheFolder = $this->GetFolder($folder->serverid);
226                if ($folder->equals($this->GetFolder($folder->serverid))) {
227                    ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->ImportFolderChange(): Change for folder '%s' will not be sent as modification is not relevant.", $folder->displayname));
228                    return false;
229                }
230
231                // load this change into memory
232                $this->changes[] = array(self::CHANGE, $folder);
233
234                // HierarchyCache: already add/update the folder so changes are not sent twice (if exported twice)
235                $this->AddFolder($folder);
236                return true;
237            }
238            return false;
239        }
240    }
241
242    /**
243     * Imports a folder deletion
244     *
245     * @param string        $id
246     * @param string        $parent     (opt) the parent id of the folders
247     *
248     * @access public
249     * @return boolean
250     */
251    public function ImportFolderDeletion($id, $parent = false) {
252        // if the forwarder is set, then this folder should be processed by another importer
253        // instead of being loaded in mem.
254        if (isset($this->destinationImporter)) {
255            $ret = $this->destinationImporter->ImportFolderDeletion($id, $parent);
256
257            // if the operation was sucessfull, update the HierarchyCache
258            if ($ret)
259                $this->DelFolder($id);
260
261            return $ret;
262        }
263        else {
264            // if this folder is not in the cache, the change does not need to be streamed to the mobile
265            if ($this->GetFolder($id)) {
266
267                // load this change into memory
268                $this->changes[] = array(self::DELETION, $id, $parent);
269
270                // HierarchyCache: delete the folder so changes are not sent twice (if exported twice)
271                $this->DelFolder($id);
272                return true;
273            }
274        }
275    }
276
277
278    /**----------------------------------------------------------------------------------------------------------
279     * IExportChanges & destination importer
280     */
281
282    /**
283     * Initializes the Exporter where changes are synchronized to
284     *
285     * @param IImportChanges    $importer
286     *
287     * @access public
288     * @return boolean
289     */
290    public function InitializeExporter(&$importer) {
291        $this->exportImporter = $importer;
292        $this->step = 0;
293        return true;
294    }
295
296    /**
297     * Returns the amount of changes to be exported
298     *
299     * @access public
300     * @return int
301     */
302    public function GetChangeCount() {
303        return count($this->changes);
304    }
305
306    /**
307     * Synchronizes a change. Only HierarchyChanges will be Synchronized()
308     *
309     * @access public
310     * @return array
311     */
312    public function Synchronize() {
313        if($this->step < count($this->changes) && isset($this->exportImporter)) {
314
315            $change = $this->changes[$this->step];
316
317            if ($change[0] == self::CHANGE) {
318                if (! $this->GetFolder($change[1]->serverid, true))
319                    $change[1]->flags = SYNC_NEWMESSAGE;
320
321                $this->exportImporter->ImportFolderChange($change[1]);
322            }
323            // deletion
324            else {
325                $this->exportImporter->ImportFolderDeletion($change[1], $change[2]);
326            }
327            $this->step++;
328
329            // return progress array
330            return array("steps" => count($this->changes), "progress" => $this->step);
331        }
332        else
333            return false;
334    }
335
336    /**
337     * Initializes a few instance variables
338     * called after unserialization
339     *
340     * @access public
341     * @return array
342     */
343    public function __wakeup() {
344        $this->changes = array();
345        $this->step = 0;
346    }
347}
348
349?>
Note: See TracBrowser for help on using the repository browser.