Commit 6c2ee8b3 authored by Sebastian Kummer's avatar Sebastian Kummer

ZP-556 Introduced Set & Get MoveStates to IChanges. Implemented interface.

Released under the Affero GNU General Public License (AGPL) version 3.
parent f7e8e550
...@@ -62,6 +62,8 @@ class ExportChangesICS implements IExportChanges{ ...@@ -62,6 +62,8 @@ class ExportChangesICS implements IExportChanges{
private $flags; private $flags;
private $exporterflags; private $exporterflags;
private $exporter; private $exporter;
private $moveSrcState;
private $moveDstState;
/** /**
* Constructor * Constructor
...@@ -269,6 +271,32 @@ class ExportChangesICS implements IExportChanges{ ...@@ -269,6 +271,32 @@ class ExportChangesICS implements IExportChanges{
return $state; return $state;
} }
/**
* Sets the states from move operations.
* When src and dst state are set, a MOVE operation is being executed.
*
* @param mixed $srcState
* @param mixed (opt) $dstState, default: null
*
* @access public
* @return boolean
*/
public function SetMoveStates($srcState, $dstState = null) {
$this->moveSrcState = $srcState;
$this->moveDstState = $dstState;
return true;
}
/**
* Gets the states of special move operations.
*
* @access public
* @return array(0 => $srcState, 1 => $dstState)
*/
public function GetMoveStates() {
return array($this->moveSrcState, $this->moveDstState);
}
/** /**
* Returns the amount of changes to be exported * Returns the amount of changes to be exported
* *
......
...@@ -73,6 +73,8 @@ class ImportChangesICS implements IImportChanges { ...@@ -73,6 +73,8 @@ class ImportChangesICS implements IImportChanges {
private $cutoffdate; private $cutoffdate;
private $contentClass; private $contentClass;
private $prefix; private $prefix;
private $moveSrcState;
private $moveDstState;
/** /**
* Constructor * Constructor
...@@ -230,6 +232,32 @@ class ImportChangesICS implements IImportChanges { ...@@ -230,6 +232,32 @@ class ImportChangesICS implements IImportChanges {
return $state; return $state;
} }
/**
* Sets the states from move operations.
* When src and dst state are set, a MOVE operation is being executed.
*
* @param mixed $srcState
* @param mixed (opt) $dstState, default: null
*
* @access public
* @return boolean
*/
public function SetMoveStates($srcState, $dstState = null) {
$this->moveSrcState = $srcState;
$this->moveDstState = $dstState;
return true;
}
/**
* Gets the states of special move operations.
*
* @access public
* @return array(0 => $srcState, 1 => $dstState)
*/
public function GetMoveStates() {
return array($this->moveSrcState, $this->moveDstState);
}
/** /**
* Checks if a message is in the synchronization interval (window) * Checks if a message is in the synchronization interval (window)
* if a filter (e.g. Sync items two weeks back) or limits this synchronization. * if a filter (e.g. Sync items two weeks back) or limits this synchronization.
......
...@@ -54,10 +54,13 @@ class ReplyBackImExporter implements IImportChanges, IExportChanges { ...@@ -54,10 +54,13 @@ class ReplyBackImExporter implements IImportChanges, IExportChanges {
private $store; private $store;
private $folderid; private $folderid;
private $changes; private $changes;
private $changesDest;
private $step; private $step;
private $exportImporter; private $exportImporter;
private $mapiprovider; private $mapiprovider;
private $contentparameters; private $contentparameters;
private $moveSrcState;
private $moveDstState;
/** /**
* Constructor * Constructor
...@@ -77,7 +80,10 @@ class ReplyBackImExporter implements IImportChanges, IExportChanges { ...@@ -77,7 +80,10 @@ class ReplyBackImExporter implements IImportChanges, IExportChanges {
$this->changes = array(); $this->changes = array();
$this->step = 0; $this->step = 0;
$this->changesDest = array();
$this->mapiprovider = new MAPIProvider($this->session, $this->store); $this->mapiprovider = new MAPIProvider($this->session, $this->store);
$this->moveSrcState = false;
$this->moveDstState = false;
} }
/** /**
...@@ -92,7 +98,7 @@ class ReplyBackImExporter implements IImportChanges, IExportChanges { ...@@ -92,7 +98,7 @@ class ReplyBackImExporter implements IImportChanges, IExportChanges {
*/ */
public function Config($state, $flags = 0) { public function Config($state, $flags = 0) {
if (is_array($state)) { if (is_array($state)) {
$this->changes = $state; $this->changes = array_merge($this->changes, $state);
} }
$this->step = 0; $this->step = 0;
return true; return true;
...@@ -123,6 +129,48 @@ class ReplyBackImExporter implements IImportChanges, IExportChanges { ...@@ -123,6 +129,48 @@ class ReplyBackImExporter implements IImportChanges, IExportChanges {
return array_slice($this->changes, $this->step); return array_slice($this->changes, $this->step);
} }
/**
* Sets the states from move operations.
* When src and dst state are set, a MOVE operation is being executed.
*
* @param mixed $srcState
* @param mixed (opt) $dstState, default: null
*
* @access public
* @return boolean
*/
public function SetMoveStates($srcState, $dstState = null) {
// TODO remove log
ZLog::Write(LOGLEVEL_DEBUG, "-------------------- ReplyBackImExporter: SetMoveStates: src:". print_r($srcState,1). " dest:". print_r($dstState,1));
if (is_array($srcState)) {
$this->changes = array_merge($this->changes, $srcState);
}
if (is_array($dstState)) {
$this->changesDest = array_merge($this->changes, $dstState);
}
return true;
}
/**
* Gets the states of special move operations.
*
* @access public
* @return array(0 => $srcState, 1 => $dstState)
*/
public function GetMoveStates() {
// if a move was executed, there will be changes for the destination folder, so we have to return the
// source changes as well. If not, they will be transported via GetState().
$srcMoveState = false;
if (!empty($this->changesDest)) {
$srcMoveState = $this->changesDest;
}
$ret = array($srcMoveState, $this->changesDest);
// TODO remove log
ZLog::Write(LOGLEVEL_DEBUG, "-------------------- ReplyBackImExporter: GetMoveState: ".print_r($ret,1));
return $ret;
}
/** /**
* Implement interfaces which are never used * Implement interfaces which are never used
*/ */
...@@ -156,69 +204,24 @@ class ReplyBackImExporter implements IImportChanges, IExportChanges { ...@@ -156,69 +204,24 @@ class ReplyBackImExporter implements IImportChanges, IExportChanges {
if (strtolower($newfolder) == strtolower(bin2hex($this->folderid)) ) if (strtolower($newfolder) == strtolower(bin2hex($this->folderid)) )
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, source and destination are equal", $id, $newfolder), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST); throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, source and destination are equal", $id, $newfolder), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST);
// Get the entryid of the message we're moving // At this point, we don't know which case of move is happening:
$entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($id)); // 1. ReadOnly -> Writeable (should normally work, message is duplicated)
if(!$entryid) // 2. ReadOnly -> ReadOnly
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, unable to resolve source message id", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); // 3. Writeable -> ReadOnly
// As we don't know which case happens, we do the same for all cases (no move, no duplication!):
//open the source message // 1. in the src folder, the message is added again (same case as a deletion in RO)
$srcmessage = mapi_msgstore_openentry($this->store, $entryid); // 2. generate a tmp-id for the destination message in the destination folder
if (!$srcmessage) { // 3. for the destination folder, the tmp-id message is deleted (same as creation in RO)
$code = SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID;
// if we move to the trash and the source message is not found, we can also just tell the mobile that we successfully moved to avoid errors (ZP-624)
if ($newfolder == ZPush::GetBackend()->GetWasteBasket()) {
$code = SYNC_MOVEITEMSSTATUS_SUCCESS;
}
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, unable to open source message: 0x%X", $id, $newfolder, mapi_last_hresult()), $code);
}
// check if the source message is in the current syncinterval // make sure the message is added again to the src folder
// TODO check if we need this $this->changes[] = array(self::DELETION, $id, null);
// if (!$this->isMessageInSyncInterval($id))
// throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Source message is outside the sync interval. Move not performed.", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
// get correct mapi store for the destination folder
$dststore = ZPush::GetBackend()->GetMAPIStoreForFolderId(ZPush::GetAdditionalSyncFolderStore($newfolder), $newfolder);
if ($dststore === false)
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, unable to open store of destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
$dstentryid = mapi_msgstore_entryidfromsourcekey($dststore, hex2bin($newfolder));
if(!$dstentryid)
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, unable to resolve destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
$dstfolder = mapi_msgstore_openentry($dststore, $dstentryid);
if(!$dstfolder)
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, unable to open destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
$newmessage = mapi_folder_createmessage($dstfolder);
if (!$newmessage)
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, unable to create message in destination folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
// Copy message
mapi_copyto($srcmessage, array(), array(), $newmessage);
if (mapi_last_hresult())
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, copy to destination message failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE);
$srcfolderentryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid);
if(!$srcfolderentryid)
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, unable to resolve source folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
$srcfolder = mapi_msgstore_openentry($this->store, $srcfolderentryid);
if (!$srcfolder)
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, unable to open source folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
// Save changes
mapi_savechanges($newmessage);
if (mapi_last_hresult())
throw new StatusException(sprintf("ReplyBackImExporter->ImportMessageMove('%s','%s'): Error, mapi_savechanges() failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE);
$sourcekeyprops = mapi_getprops($newmessage, array (PR_SOURCE_KEY));
if (isset($sourcekeyprops[PR_SOURCE_KEY]) && $sourcekeyprops[PR_SOURCE_KEY]) {
$this->changes[] = array(self::DELETION, $id, null);
return bin2hex($sourcekeyprops[PR_SOURCE_KEY]);
}
return false; // generate tmp-id and have it removed later via the dest changes (saved via DstMoveState)
$tmpId = "ReplyBackImExporter-temporaryId-". microtime();
$this->changesDest[] = array(self::CREATION, $tmpId, null);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ReplyBackImExporter->ImportMessageMove(): Move forbidden. Restoring message in source folder and added a delete request for the destination folder for the id: %s", $tmpId));
return $tmpId;
} }
/** /**
......
...@@ -103,12 +103,8 @@ class BackendZarafa implements IBackend, ISearchProvider { ...@@ -103,12 +103,8 @@ class BackendZarafa implements IBackend, ISearchProvider {
$this->session = false; $this->session = false;
$this->folderStatCache = array(); $this->folderStatCache = array();
<<<<<<< HEAD
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa using PHP-MAPI version: %s", phpversion("mapi")));
ZarafaChangesWrapper::SetBackend($this);
=======
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa using PHP-MAPI version: %s - PHP version: %s", phpversion("mapi"), phpversion())); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa using PHP-MAPI version: %s - PHP version: %s", phpversion("mapi"), phpversion()));
>>>>>>> FETCH_HEAD ZarafaChangesWrapper::SetBackend($this);
} }
/** /**
......
...@@ -64,6 +64,8 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges { ...@@ -64,6 +64,8 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges {
private $replyback; private $replyback;
private $ownFolder; private $ownFolder;
private $state; private $state;
private $moveSrcState;
private $moveDstState;
/** /**
* Sets the backend to be used by the wrappers. This is used to check for permissions. * Sets the backend to be used by the wrappers. This is used to check for permissions.
...@@ -130,6 +132,9 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges { ...@@ -130,6 +132,9 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges {
$this->replyback = null; $this->replyback = null;
$this->current = null; $this->current = null;
$this->state = null; $this->state = null;
$this->didMove = false;
$this->moveSrcState = false;
$this->moveDstState = false;
} }
/** /**
...@@ -237,17 +242,20 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges { ...@@ -237,17 +242,20 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges {
* @throws StatusException * @throws StatusException
*/ */
public function Config($state, $flags = 0) { public function Config($state, $flags = 0) {
ZLog::Write(LOGLEVEL_DEBUG, "-------------------- ZarafaChangesWrapper->Config:". $state);
// if there is an ICS state, it will remain untouched in the ReplyBackState object // if there is an ICS state, it will remain untouched in the ReplyBackState object
$this->state = ReplyBackState::FromState($state); $this->state = ReplyBackState::FromState($state);
$this->init(); $this->init();
if ($this->isReplyBackExporter()) { $config = false;
return $this->current->Config($this->state->GetReplyBackState(), $flags); if ($this->isReplyBackExporter() || !empty($this->moveSrcState)) {
$config = $this->current->Config($this->state->GetReplyBackState(), $flags);
} }
else { else {
return $this->current->Config($this->state->GetICSState(), $flags); $config = $this->current->Config($this->state->GetICSState(), $flags);
} }
$this->current->SetMoveStates($this->moveSrcState, $this->moveDstState);
} }
/** /**
...@@ -260,7 +268,7 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges { ...@@ -260,7 +268,7 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges {
* @throws StatusException * @throws StatusException
*/ */
public function ConfigContentParameters($contentparameters) { public function ConfigContentParameters($contentparameters) {
$this->init(); //$this->init();
return $this->current->ConfigContentParameters($contentparameters); return $this->current->ConfigContentParameters($contentparameters);
} }
...@@ -281,6 +289,33 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges { ...@@ -281,6 +289,33 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges {
return ReplyBackState::ToState($this->state); return ReplyBackState::ToState($this->state);
} }
/**
* Sets the states from move operations.
* When src and dst state are set, a MOVE operation is being executed.
*
* @param mixed $srcState
* @param mixed (opt) $dstState, default: null
*
* @access public
* @return boolean
*/
public function SetMoveStates($srcState, $dstState = null) {
ZLog::Write(LOGLEVEL_DEBUG, "-------------------- ZarafaChangesWrapper: SetMoveStates: src:". print_r($srcState,1). " dest:". print_r($dstState,1));
$this->moveSrcState = $srcState;
$this->moveDstState = $dstState;
return true;
}
/**
* Gets the states of special move operations.
*
* @access public
* @return array(0 => $srcState, 1 => $dstState)
*/
public function GetMoveStates() {
return $this->current->GetMoveStates();
}
/**---------------------------------------------------------------------------------------------------------- /**----------------------------------------------------------------------------------------------------------
* IImportChanges - pass everything directly through to $this->current * IImportChanges - pass everything directly through to $this->current
*/ */
...@@ -354,6 +389,21 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges { ...@@ -354,6 +389,21 @@ class ZarafaChangesWrapper implements IImportChanges, IExportChanges {
* @throws StatusException * @throws StatusException
*/ */
public function ImportMessageMove($id, $newfolder) { public function ImportMessageMove($id, $newfolder) {
$this->didMove = true;
// Wwhen we setup the $current importer, we didn't know what we needed to do, so we look only at the src folder.
// Now the $newfolder could be read only as well. So we need to check it's permissions and then switch to a ReplyBackImExporter if its r/o.
if (!self::$backend->HasSecretaryACLs($this->store, $this->folderid)) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaChangesWrapper->ImportMessageMove(): destination folderid '%s' is missing permissions. Switching to ReplyBackImExporter.", Utils::PrintAsString($this->folderid)));
// save the state
$this->state->SetICSState( $this->current->GetState());
$this->replyback = $this->getReplyBackImExporter();
$this->current = $this->replyback;
$this->current->SetMoveStates($this->moveSrcState, $this->moveDstState);
$this->current->Config($this->state->GetReplyBackState());
// TODO: the contentparameters are not available anymore. Do we really need them?
}
return $this->current->ImportMessageMove($id, $newfolder); return $this->current->ImportMessageMove($id, $newfolder);
} }
......
...@@ -309,12 +309,12 @@ ...@@ -309,12 +309,12 @@
* - on Zarafa systems use backend/zarafa/listfolders.php script to get a list * - on Zarafa systems use backend/zarafa/listfolders.php script to get a list
* of available folders * of available folders
* *
* - all Z-Push users must have at least reading permissions so the configured * - all Z-Push users must have at least reading permissions so the configured
* folders can be synchronized to the mobile. Else they are ignored. * folders can be synchronized to the mobile. Else they are ignored.
* *
* - if read-only is set to 'false' only users with full permissions (secretary * - if read-only is set to 'false' only users with full permissions (secretary
* rights) are able to change entries. For all others, the changes will be * rights) are able to change entries. For all others, the changes will be
* discarted and overwritten with data from the server. Check backend * discarted and overwritten with data from the server. Check backend
* compatibility and configuration for this feature. * compatibility and configuration for this feature.
* *
* - this feature is only partly suitable for multi-tenancy environments, * - this feature is only partly suitable for multi-tenancy environments,
...@@ -330,13 +330,20 @@ ...@@ -330,13 +330,20 @@
$additionalFolders = array( $additionalFolders = array(
// demo entry for the synchronization of contacts from the public folder. // demo entry for the synchronization of contacts from the public folder.
// uncomment (remove '/*' '*/') and fill in the folderid // uncomment (remove '/*' '*/') and fill in the folderid
/*
array( array(
'store' => "SYSTEM", 'store' => "SYSTEM",
'folderid' => "", 'folderid' => "5a37a3f4faa340e49f5c0dc09cf6cb040e2900000000",
'name' => "Public Contacts", 'name' => "Public Contacts",
'type' => SYNC_FOLDER_TYPE_USER_CONTACT, 'type' => SYNC_FOLDER_TYPE_USER_CONTACT,
'readonly' => false, 'readonly' => true,
),
array(
'store' => "SYSTEM",
'folderid' => "5a37a3f4faa340e49f5c0dc09cf6cb04a02a00000000",
'name' => "Public EMAIL",
'type' => SYNC_FOLDER_TYPE_USER_MAIL,
'readonly' => true,
), ),
*/
); );
...@@ -114,6 +114,8 @@ class ChangesMemoryWrapper extends HierarchyCache implements IImportChanges, IEx ...@@ -114,6 +114,8 @@ class ChangesMemoryWrapper extends HierarchyCache implements IImportChanges, IEx
public function GetState() { return false;} public function GetState() { return false;}
public function LoadConflicts($contentparameters, $state) { return true; } public function LoadConflicts($contentparameters, $state) { return true; }
public function ConfigContentParameters($contentparameters) { return true; } public function ConfigContentParameters($contentparameters) { return true; }
public function SetMoveStates($srcState, $dstState = null) { return true; }
public function GetMoveStates() { return array(false, false); }
public function ImportMessageReadFlag($id, $flags) { return true; } public function ImportMessageReadFlag($id, $flags) { return true; }
public function ImportMessageMove($id, $newfolder) { return true; } public function ImportMessageMove($id, $newfolder) { return true; }
......
...@@ -71,6 +71,8 @@ class ImportChangesStream implements IImportChanges { ...@@ -71,6 +71,8 @@ class ImportChangesStream implements IImportChanges {
public function Config($state, $flags = 0) { return true; } public function Config($state, $flags = 0) { return true; }
public function ConfigContentParameters($contentparameters) { return true; } public function ConfigContentParameters($contentparameters) { return true; }
public function GetState() { return false;} public function GetState() { return false;}
public function SetMoveStates($srcState, $dstState = null) { return true; }
public function GetMoveStates() { return array(false, false); }
public function LoadConflicts($contentparameters, $state) { return true; } public function LoadConflicts($contentparameters, $state) { return true; }
/** /**
......
...@@ -681,6 +681,7 @@ class SyncCollections implements Iterator { ...@@ -681,6 +681,7 @@ class SyncCollections implements Iterator {
if ($exporter !== false && isset($this->addparms[$folderid]["state"])) { if ($exporter !== false && isset($this->addparms[$folderid]["state"])) {
$importer = false; $importer = false;
$exporter->SetMoveStates($spa->GetMoveState());
$exporter->Config($this->addparms[$folderid]["state"], BACKEND_DISCARD_DATA); $exporter->Config($this->addparms[$folderid]["state"], BACKEND_DISCARD_DATA);
$exporter->ConfigContentParameters($spa->GetCPO()); $exporter->ConfigContentParameters($spa->GetCPO());
$ret = $exporter->InitializeExporter($importer); $ret = $exporter->InitializeExporter($importer);
......
...@@ -76,6 +76,7 @@ class SyncParameters extends StateObject { ...@@ -76,6 +76,7 @@ class SyncParameters extends StateObject {
'foldersyncremaining' => false, 'foldersyncremaining' => false,
'folderstat' => false, 'folderstat' => false,
'folderstattimeout' => false, 'folderstattimeout' => false,
'movestate' => false,
); );
/** /**
......
...@@ -54,6 +54,8 @@ class DiffState implements IChanges { ...@@ -54,6 +54,8 @@ class DiffState implements IChanges {
protected $flags; protected $flags;
protected $contentparameters; protected $contentparameters;
protected $cutoffdate; protected $cutoffdate;
protected $moveSrcState;
protected $moveDstState;
/** /**
* Initializes the state * Initializes the state
...@@ -118,6 +120,32 @@ class DiffState implements IChanges { ...@@ -118,6 +120,32 @@ class DiffState implements IChanges {
return $this->syncstate; return $this->syncstate;
} }
/**
* Sets the states from move operations.
* When src and dst state are set, a MOVE operation is being executed.
*
* @param mixed $srcState
* @param mixed (opt) $dstState, default: null
*
* @access public
* @return boolean
*/
public function SetMoveStates($srcState, $dstState = null) {
$this->moveSrcState = $srcState;
$this->moveDstState = $dstState;
return true;
}
/**
* Gets the states of special move operations.
*
* @access public
* @return array(0 => $srcState, 1 => $dstState)
*/
public function GetMoveStates() {
return array($this->moveSrcState, $this->moveDstState);
}
/**---------------------------------------------------------------------------------------------------------- /**----------------------------------------------------------------------------------------------------------
* DiffState specific stuff * DiffState specific stuff
......
...@@ -81,4 +81,24 @@ interface IChanges { ...@@ -81,4 +81,24 @@ interface IChanges {
* @return string * @return string
*/ */
public function GetState(); public function GetState();
/**
* Sets the states from move operations.
* When src and dst state are set, a MOVE operation is being executed.
*
* @param mixed $srcState
* @param mixed (opt) $dstState, default: null
*
* @access public
* @return boolean
*/
public function SetMoveStates($srcState, $dstState = null);
/**
* Gets the states of special move operations.
*
* @access public
* @return array(0 => $srcState, 1 => $dstState)
*/
public function GetMoveStates();
} }
...@@ -105,14 +105,34 @@ class MoveItems extends RequestProcessor { ...@@ -105,14 +105,34 @@ class MoveItems extends RequestProcessor {
if ($importer === false) if ($importer === false)
throw new StatusException(sprintf("HandleMoveItems() could not get an importer for folder id %s/%s", $move["srcfldid"], $sourceBackendFolderId), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); throw new StatusException(sprintf("HandleMoveItems() could not get an importer for folder id %s/%s", $move["srcfldid"], $sourceBackendFolderId), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
// get saved SyncParameters for this folder // get saved SyncParameters of the source folder
$spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($move["srcfldid"]); $spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($move["srcfldid"]);
if (!$spa->HasSyncKey()) if (!$spa->HasSyncKey())
throw new StatusException(sprintf("MoveItems(): Source folder id '%s' is not fully synchronized. Unable to perform operation.", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); throw new StatusException(sprintf("MoveItems(): Source folder id '%s' is not fully synchronized. Unable to perform operation.", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
// get saved SyncParameters of the destination folder
$destSpa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($move["dstfldid"]);
if (!$spa->HasSyncKey())
throw new StatusException(sprintf("MoveItems(): Destination folder id '%s' is not fully synchronized. Unable to perform operation.", $move["dstfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
$importer->SetMoveStates($spa->GetMoveState(), $destSpa->GetMoveState());
$importer->ConfigContentParameters($spa->GetCPO()); $importer->ConfigContentParameters($spa->GetCPO());
$result = $importer->ImportMessageMove($move["srcmsgid"], self::$deviceManager->GetBackendIdForFolderId($move["dstfldid"])); $result = $importer->ImportMessageMove($move["srcmsgid"], self::$deviceManager->GetBackendIdForFolderId($move["dstfldid"]));
// We discard the importer state for now. // We discard the standard importer state for now.
// Get the move states and save them in the SyncParameters of the src and dst folder
list($srcMoveState, $dstMoveState) = $importer->GetMoveStates();
// TODO REMOVE LOG
ZLog::Write(LOGLEVEL_DEBUG, "Importer ---> GET Move state: src: ". print_r($srcMoveState,1) . " dst: ".print_r($dstMoveState,1));
$spa->SetMoveStates($srcMoveState);
$destSpa->SetMoveStates($dstMoveState);
if ($spa->IsDataChanged()) {
self::$deviceManager->GetStateManager()->SetSynchedFolderState($spa);
}
if ($destSpa->IsDataChanged()) {
self::$deviceManager->GetStateManager()->SetSynchedFolderState($destSpa);
}
} }
catch (StatusException $stex) { catch (StatusException $stex) {
if ($stex->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) // same as SYNC_FSSTATUS_CODEUNKNOWN if ($stex->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) // same as SYNC_FSSTATUS_CODEUNKNOWN
......
...@@ -788,6 +788,7 @@ class Sync extends RequestProcessor { ...@@ -788,6 +788,7 @@ class Sync extends RequestProcessor {
$streamimporter = new ImportChangesStream(self::$encoder, ZPush::getSyncObjectFromFolderClass($spa->GetContentClass())); $streamimporter = new ImportChangesStream(self::$encoder, ZPush::getSyncObjectFromFolderClass($spa->GetContentClass()));
if ($exporter !== false) { if ($exporter !== false) {
$exporter->SetMoveStates($spa->GetMoveState());
$exporter->Config($sc->GetParameter($spa, "state")); $exporter->Config($sc->GetParameter($spa, "state"));
$exporter->ConfigContentParameters($spa->GetCPO()); $exporter->ConfigContentParameters($spa->GetCPO());
$exporter->InitializeExporter($streamimporter); $exporter->InitializeExporter($streamimporter);
...@@ -1154,9 +1155,16 @@ class Sync extends RequestProcessor { ...@@ -1154,9 +1155,16 @@ class Sync extends RequestProcessor {
self::$topCollector->AnnounceInformation("Saving state"); self::$topCollector->AnnounceInformation("Saving state");
try { try {
if (isset($exporter) && $exporter) if (isset($exporter) && $exporter) {
$state = $exporter->GetState(); $state = $exporter->GetState();
// update the move state (it should be gone now)
list($moveState,) = $exporter->GetMoveStates();
// TODO REMOVE LOG
ZLog::Write(LOGLEVEL_DEBUG, "EXPORTER ---> GET Move state: ". Utils::PrintAsString($moveState));
$spa->SetMoveStates($moveState);
}
// nothing exported, but possibly imported - get the importer state // nothing exported, but possibly imported - get the importer state
else if ($sc->GetParameter($spa, "state") !== null) else if ($sc->GetParameter($spa, "state") !== null)
$state = $sc->GetParameter($spa, "state"); $state = $sc->GetParameter($spa, "state");
...@@ -1255,13 +1263,16 @@ class Sync extends RequestProcessor { ...@@ -1255,13 +1263,16 @@ class Sync extends RequestProcessor {
if ($this->importer === false) if ($this->importer === false)
throw new StatusException(sprintf("Sync->getImporter(): no importer for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED); throw new StatusException(sprintf("Sync->getImporter(): no importer for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
// set the move state so the importer is aware of previous made moves
$this->importer->SetMoveStates($spa->GetMoveState());
ZLog::Write(LOGLEVEL_DEBUG, "-----------after setmove steates");
// if there is a valid state obtained after importing changes in a previous loop, we use that state // if there is a valid state obtained after importing changes in a previous loop, we use that state
if (isset($actiondata["failstate"]) && isset($actiondata["failstate"]["failedsyncstate"])) { if (isset($actiondata["failstate"]) && isset($actiondata["failstate"]["failedsyncstate"])) {
$this->importer->Config($actiondata["failstate"]["failedsyncstate"], $spa->GetConflict()); $this->importer->Config($actiondata["failstate"]["failedsyncstate"], $spa->GetConflict());
} }
else else
$this->importer->Config($sc->GetParameter($spa, "state"), $spa->GetConflict()); $this->importer->Config($sc->GetParameter($spa, "state"), $spa->GetConflict());
ZLog::Write(LOGLEVEL_DEBUG, "-----------after config");
// the CPO is also needed by the importer to check if imported changes are inside the sync window - see ZP-258 // the CPO is also needed by the importer to check if imported changes are inside the sync window - see ZP-258
$this->importer->ConfigContentParameters($spa->GetCPO()); $this->importer->ConfigContentParameters($spa->GetCPO());
$this->importer->LoadConflicts($spa->GetCPO(), $sc->GetParameter($spa, "state")); $this->importer->LoadConflicts($spa->GetCPO(), $sc->GetParameter($spa, "state"));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment