1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/z-push_ynh.git synced 2024-09-03 18:05:58 +02:00
z-push_ynh/sources/lib/core/changesmemorywrapper.php

347 lines
13 KiB
PHP

<?php
/***********************************************
* File : changesmemorywrapper.php
* Project : Z-Push
* Descr : Class that collect changes in memory
*
* Created : 18.08.2011
*
* Copyright 2007 - 2013 Zarafa Deutschland GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation with the following additional
* term according to sec. 7:
*
* According to sec. 7 of the GNU Affero General Public License, version 3,
* the terms of the AGPL are supplemented with the following terms:
*
* "Zarafa" is a registered trademark of Zarafa B.V.
* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
* The licensing of the Program under the AGPL does not imply a trademark license.
* Therefore any rights, title and interest in our trademarks remain entirely with us.
*
* However, if you propagate an unmodified version of the Program you are
* allowed to use the term "Z-Push" to indicate that you distribute the Program.
* Furthermore you may use our trademarks where it is necessary to indicate
* the intended purpose of a product or service provided you use it in accordance
* with honest practices in industrial or commercial matters.
* If you want to propagate modified versions of the Program under the name "Z-Push",
* you may only do so if you have a written permission by Zarafa Deutschland GmbH
* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
class ChangesMemoryWrapper extends HierarchyCache implements IImportChanges, IExportChanges {
const CHANGE = 1;
const DELETION = 2;
private $changes;
private $step;
private $destinationImporter;
private $exportImporter;
/**
* Constructor
*
* @access public
* @return
*/
public function ChangesMemoryWrapper() {
$this->changes = array();
$this->step = 0;
parent::HierarchyCache();
}
/**
* Only used to load additional folder sync information for hierarchy changes
*
* @param array $state current state of additional hierarchy folders
*
* @access public
* @return boolean
*/
public function Config($state, $flags = 0) {
// we should never forward this changes to a backend
if (!isset($this->destinationImporter)) {
foreach($state as $addKey => $addFolder) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->Config(AdditionalFolders) : process folder '%s'", $addFolder->displayname));
if (isset($addFolder->NoBackendFolder) && $addFolder->NoBackendFolder == true) {
$hasRights = ZPush::GetBackend()->Setup($addFolder->Store, true, $addFolder->serverid);
// delete the folder on the device
if (! $hasRights) {
// delete the folder only if it was an additional folder before, else ignore it
$synchedfolder = $this->GetFolder($addFolder->serverid);
if (isset($synchedfolder->NoBackendFolder) && $synchedfolder->NoBackendFolder == true)
$this->ImportFolderDeletion($addFolder->serverid, $addFolder->parentid);
continue;
}
}
// add folder to the device - if folder is already on the device, nothing will happen
$this->ImportFolderChange($addFolder);
}
// look for folders which are currently on the device if there are now not to be synched anymore
$alreadyDeleted = $this->GetDeletedFolders();
foreach ($this->ExportFolders(true) as $sid => $folder) {
// we are only looking at additional folders
if (isset($folder->NoBackendFolder)) {
// look if this folder is still in the list of additional folders and was not already deleted (e.g. missing permissions)
if (!array_key_exists($sid, $state) && !array_key_exists($sid, $alreadyDeleted)) {
ZLog::Write(LOGLEVEL_INFO, sprintf("ChangesMemoryWrapper->Config(AdditionalFolders) : previously synchronized folder '%s' is not to be synched anymore. Sending delete to mobile.", $folder->displayname));
$this->ImportFolderDeletion($folder->serverid, $folder->parentid);
}
}
}
}
return true;
}
/**
* Implement interfaces which are never used
*/
public function GetState() { return false;}
public function LoadConflicts($contentparameters, $state) { return true; }
public function ConfigContentParameters($contentparameters) { return true; }
public function ImportMessageReadFlag($id, $flags) { return true; }
public function ImportMessageMove($id, $newfolder) { return true; }
/**----------------------------------------------------------------------------------------------------------
* IImportChanges & destination importer
*/
/**
* Sets an importer where incoming changes should be sent to
*
* @param IImportChanges $importer message to be changed
*
* @access public
* @return boolean
*/
public function SetDestinationImporter(&$importer) {
$this->destinationImporter = $importer;
}
/**
* Imports a message change, which is imported into memory
*
* @param string $id id of message which is changed
* @param SyncObject $message message to be changed
*
* @access public
* @return boolean
*/
public function ImportMessageChange($id, $message) {
$this->changes[] = array(self::CHANGE, $id);
return true;
}
/**
* Imports a message deletion, which is imported into memory
*
* @param string $id id of message which is deleted
*
* @access public
* @return boolean
*/
public function ImportMessageDeletion($id) {
$this->changes[] = array(self::DELETION, $id);
return true;
}
/**
* Checks if a message id is flagged as changed
*
* @param string $id message id
*
* @access public
* @return boolean
*/
public function IsChanged($id) {
return (array_search(array(self::CHANGE, $id), $this->changes) === false) ? false:true;
}
/**
* Checks if a message id is flagged as deleted
*
* @param string $id message id
*
* @access public
* @return boolean
*/
public function IsDeleted($id) {
return (array_search(array(self::DELETION, $id), $this->changes) === false) ? false:true;
}
/**
* Imports a folder change
*
* @param SyncFolder $folder folder to be changed
*
* @access public
* @return boolean
*/
public function ImportFolderChange($folder) {
// if the destinationImporter is set, then this folder should be processed by another importer
// instead of being loaded in memory.
if (isset($this->destinationImporter)) {
// normally the $folder->type is not set, but we need this value to check if the change operation is permitted
// e.g. system folders can normally not be changed - set the type from cache and let the destinationImporter decide
if (!isset($folder->type)) {
$cacheFolder = $this->GetFolder($folder->serverid);
$folder->type = $cacheFolder->type;
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->ImportFolderChange(): Set foldertype for folder '%s' from cache as it was not sent: '%s'", $folder->displayname, $folder->type));
}
$ret = $this->destinationImporter->ImportFolderChange($folder);
// if the operation was sucessfull, update the HierarchyCache
if ($ret) {
// for folder creation, the serverid is not set and has to be updated before
if (!isset($folder->serverid) || $folder->serverid == "")
$folder->serverid = $ret;
$this->AddFolder($folder);
}
return $ret;
}
// load into memory
else {
if (isset($folder->serverid)) {
// The Zarafa HierarchyExporter exports all kinds of changes for folders (e.g. update no. of unread messages in a folder).
// These changes are not relevant for the mobiles, as something changes but the relevant displayname and parentid
// stay the same. These changes will be dropped and are not sent!
$cacheFolder = $this->GetFolder($folder->serverid);
if ($folder->equals($this->GetFolder($folder->serverid))) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->ImportFolderChange(): Change for folder '%s' will not be sent as modification is not relevant.", $folder->displayname));
return false;
}
// load this change into memory
$this->changes[] = array(self::CHANGE, $folder);
// HierarchyCache: already add/update the folder so changes are not sent twice (if exported twice)
$this->AddFolder($folder);
return true;
}
return false;
}
}
/**
* Imports a folder deletion
*
* @param string $id
* @param string $parent (opt) the parent id of the folders
*
* @access public
* @return boolean
*/
public function ImportFolderDeletion($id, $parent = false) {
// if the forwarder is set, then this folder should be processed by another importer
// instead of being loaded in mem.
if (isset($this->destinationImporter)) {
$ret = $this->destinationImporter->ImportFolderDeletion($id, $parent);
// if the operation was sucessfull, update the HierarchyCache
if ($ret)
$this->DelFolder($id);
return $ret;
}
else {
// if this folder is not in the cache, the change does not need to be streamed to the mobile
if ($this->GetFolder($id)) {
// load this change into memory
$this->changes[] = array(self::DELETION, $id, $parent);
// HierarchyCache: delete the folder so changes are not sent twice (if exported twice)
$this->DelFolder($id);
return true;
}
}
}
/**----------------------------------------------------------------------------------------------------------
* IExportChanges & destination importer
*/
/**
* Initializes the Exporter where changes are synchronized to
*
* @param IImportChanges $importer
*
* @access public
* @return boolean
*/
public function InitializeExporter(&$importer) {
$this->exportImporter = $importer;
$this->step = 0;
return true;
}
/**
* Returns the amount of changes to be exported
*
* @access public
* @return int
*/
public function GetChangeCount() {
return count($this->changes);
}
/**
* Synchronizes a change. Only HierarchyChanges will be Synchronized()
*
* @access public
* @return array
*/
public function Synchronize() {
if($this->step < count($this->changes) && isset($this->exportImporter)) {
$change = $this->changes[$this->step];
if ($change[0] == self::CHANGE) {
if (! $this->GetFolder($change[1]->serverid, true))
$change[1]->flags = SYNC_NEWMESSAGE;
$this->exportImporter->ImportFolderChange($change[1]);
}
// deletion
else {
$this->exportImporter->ImportFolderDeletion($change[1], $change[2]);
}
$this->step++;
// return progress array
return array("steps" => count($this->changes), "progress" => $this->step);
}
else
return false;
}
/**
* Initializes a few instance variables
* called after unserialization
*
* @access public
* @return array
*/
public function __wakeup() {
$this->changes = array();
$this->step = 0;
}
}