Commit f80e3589 authored by Sebastian Kummer's avatar Sebastian Kummer

ZP-556 General:

- work with read-only flag for static additional folders,
- change IBackend->Setup() to include a read-only flag,
- fixed Setup() read-only flag for default Backend, Combined and
DiffBackend,
- changed ChangesMemoryWrapper for read-only Setup() flag,
- added read-only flag to SyncFolder,
- added comparision method for SyncObjects that returns a human readable
output with data and differences (needs improvement),
- Check status before doing GetImporter() in Sync,
- recheck folderstat after export to catch ReplyBack changes,
- check for backendIds in resync folder of z-push-admin

For Zarafa:
- check for read/secretary permissions in Setup() depending on flag,
- added template for email notification to zarafa config,
- implemented ZarafaChangesWrapper (common wrapper for exporter &
importer) - decides state based what needs to be done,
- implemented ReplyBackImExporter to process changes on read-only
folders and reply back with the original data (overwriting the changes
on the mobiles),
- added simple StateObject to hold ReplyBack states,
- always return the ZarafaChangesWrapper when content im/exporters are
being requested,
- SendMail() should always work on the default store, not the current
store (when sharing folders),
- changed GetFolderStat() to work with ReplyBack cases.

Released under the Affero GNU General Public License (AGPL) version 3.
parent 683a5bc1
...@@ -139,12 +139,13 @@ class BackendCombined extends Backend implements ISearchProvider { ...@@ -139,12 +139,13 @@ class BackendCombined extends Backend implements ISearchProvider {
* @param string $store target store, could contain a "domain\user" value * @param string $store target store, could contain a "domain\user" value
* @param boolean $checkACLonly if set to true, Setup() should just check ACLs * @param boolean $checkACLonly if set to true, Setup() should just check ACLs
* @param string $folderid if set, only ACLs on this folderid are relevant * @param string $folderid if set, only ACLs on this folderid are relevant
* @param boolean $readonly if set, the folder needs at least read permissions
* *
* @access public * @access public
* @return boolean * @return boolean
*/ */
public function Setup($store, $checkACLonly = false, $folderid = false) { public function Setup($store, $checkACLonly = false, $folderid = false, $readonly = false) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->Setup('%s', '%s', '%s')", $store, Utils::PrintAsString($checkACLonly), $folderid)); ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->Setup('%s', '%s', '%s', '%s')", $store, Utils::PrintAsString($checkACLonly), $folderid, Utils::PrintAsString($readonly)));
if(!is_array($this->backends)){ if(!is_array($this->backends)){
return false; return false;
} }
...@@ -153,7 +154,7 @@ class BackendCombined extends Backend implements ISearchProvider { ...@@ -153,7 +154,7 @@ class BackendCombined extends Backend implements ISearchProvider {
if(isset($this->config['backends'][$i]['users']) && isset($this->config['backends'][$i]['users'][$store]['username'])){ if(isset($this->config['backends'][$i]['users']) && isset($this->config['backends'][$i]['users'][$store]['username'])){
$u = $this->config['backends'][$i]['users'][$store]['username']; $u = $this->config['backends'][$i]['users'][$store]['username'];
} }
if($this->backends[$i]->Setup($u, $checkACLonly, $folderid) == false){ if($this->backends[$i]->Setup($u, $checkACLonly, $folderid, $readonly) == false){
ZLog::Write(LOGLEVEL_WARN, "Combined->Setup() failed"); ZLog::Write(LOGLEVEL_WARN, "Combined->Setup() failed");
return false; return false;
} }
...@@ -444,7 +445,7 @@ class BackendCombined extends Backend implements ISearchProvider { ...@@ -444,7 +445,7 @@ class BackendCombined extends Backend implements ISearchProvider {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSinkInitialize('%s') is supported, initializing", $folderid)); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSinkInitialize('%s') is supported, initializing", $folderid));
return $backend->ChangesSinkInitialize($this->GetBackendFolder($folderid)); return $backend->ChangesSinkInitialize($this->GetBackendFolder($folderid));
} }
// if the backend doesn't support ChangesSink, we also return true so we don't get an error // if the backend doesn't support ChangesSink, we also return true so we don't get an error
return true; return true;
} }
......
...@@ -47,3 +47,35 @@ ...@@ -47,3 +47,35 @@
// Defines the server to which we want to connect // Defines the server to which we want to connect
define('MAPI_SERVER', 'http://127.0.0.1:236/zarafa'); define('MAPI_SERVER', 'http://127.0.0.1:236/zarafa');
// Read-Only shared folders
// When trying to write a change on a read-only folder this data is dropped and replaced on the device of the user.
// Enabling the option below, sends an email to the user notifying that this happened (default enabled).
// If this is disabled, the data will be dropped silently and will be lost.
// The template of the email sent can be customized here. The placeholders can also be used in the subject.
define('READ_ONLY_NOTIFY_LOST_DATA', true);
// String to mark the data changed by the user (that he is trying to save)
define('READ_ONLY_NOTIFY_YOURDATA', 'Your data');
// Email template to be sent to the user
define('READ_ONLY_NOTIFY_SUBJECT', "Z-Push: Writing operation not permitted - data reset");
define('READ_ONLY_NOTIFY_BODY', <<<END
Dear **USERFULLNAME**,
on **DATE** at **TIME** you've tried to save a data in the folder '**FOLDERNAME**' on your device '**MOBILETYPE**' ID: '**MOBILEDEVICEID**'.
This operation was not successful, as you lack write access to this folder.
Your data has been dropped and replaced with the original data on your device to ensure data integrity.
Below the data you tried to save. You should seek other means to it (e.g. forward this email to a person with write access to this folder).
**DIFFERENCES**
If you have questions about this email, please contact your e-mail administrator.
Sincerely,
Your Z-Push system
END
);
// Format of the **DATE** and **TIME** placeholders - more information on formats, see http://php.net/manual/en/function.strftime.php
define('READ_ONLY_NOTIFY_DATE_FORMAT', "%d.%m.%Y");
define('READ_ONLY_NOTIFY_TIME_FORMAT', "%H:%M:%S");
This diff is collapsed.
<?php
/***********************************************
* File : replybackstate.php
* Project : Z-Push
* Descr : Holds the state of the ReplyBackImExporter
* and also the ICS state to continue on later
*
* Created : 25.04.2016
*
* Copyright 2016 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 ReplyBackState extends StateObject {
protected $unsetdata = array(
'replybackstate' => array(),
'icsstate' => "",
);
static public function FromState($state) {
if (strpos($state, 'ReplyBackState') !== false) {
return unserialize($state);
}
else {
$s = new ReplyBackState();
$s->SetICSState($state);
$s->SetReplyBackState(array());
return $s;
}
}
static public function ToState($state) {
if (!empty($state->GetReplyBackState())) {
return serialize($state);
}
else {
return $state->GetICSState();
}
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
...@@ -283,13 +283,21 @@ ...@@ -283,13 +283,21 @@
* SYNC_FOLDER_TYPE_USER_APPOINTMENT * SYNC_FOLDER_TYPE_USER_APPOINTMENT
* SYNC_FOLDER_TYPE_USER_TASK * SYNC_FOLDER_TYPE_USER_TASK
* SYNC_FOLDER_TYPE_USER_MAIL * SYNC_FOLDER_TYPE_USER_MAIL
* SYNC_FOLDER_TYPE_USER_NOTE
* readonly: indicates if the folder should be opened read-only.
* If set to false, full writing permissions are required.
* *
* Additional notes: * Additional notes:
* - 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 full writing permissions (secretary rights) so * - all Z-Push users must have at least reading permissions so the configured
* the configured folders can be synchronized to the mobile * folders can be synchronized to the mobile. Else they are ignored.
*
* - 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
* discarted and overwritten with data from the server. Check backend
* 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,
* as ALL users from ALL tenents need access to the configured store & folder. * as ALL users from ALL tenents need access to the configured store & folder.
...@@ -310,6 +318,7 @@ ...@@ -310,6 +318,7 @@
'folderid' => "", 'folderid' => "",
'name' => "Public Contacts", 'name' => "Public Contacts",
'type' => SYNC_FOLDER_TYPE_USER_CONTACT, 'type' => SYNC_FOLDER_TYPE_USER_CONTACT,
'readonly' => false,
), ),
*/ */
); );
...@@ -77,7 +77,7 @@ class ChangesMemoryWrapper extends HierarchyCache implements IImportChanges, IEx ...@@ -77,7 +77,7 @@ class ChangesMemoryWrapper extends HierarchyCache implements IImportChanges, IEx
foreach($state as $addKey => $addFolder) { foreach($state as $addKey => $addFolder) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->Config(AdditionalFolders) : process folder '%s'", $addFolder->displayname)); ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->Config(AdditionalFolders) : process folder '%s'", $addFolder->displayname));
if (isset($addFolder->NoBackendFolder) && $addFolder->NoBackendFolder == true) { if (isset($addFolder->NoBackendFolder) && $addFolder->NoBackendFolder == true) {
$hasRights = ZPush::GetBackend()->Setup($addFolder->Store, true, $addFolder->BackendId); $hasRights = ZPush::GetBackend()->Setup($addFolder->Store, true, $addFolder->BackendId, $addFolder->ReadOnly);
// delete the folder on the device // delete the folder on the device
if (! $hasRights) { if (! $hasRights) {
// delete the folder only if it was an additional folder before, else ignore it // delete the folder only if it was an additional folder before, else ignore it
......
...@@ -369,6 +369,8 @@ class ZPush { ...@@ -369,6 +369,8 @@ class ZPush {
// save store as custom property which is not streamed directly to the device // save store as custom property which is not streamed directly to the device
$folder->NoBackendFolder = true; $folder->NoBackendFolder = true;
$folder->Store = $af['store']; $folder->Store = $af['store'];
$folder->ReadOnly = $af['readonly'];
self::$addSyncFolders[$folder->BackendId] = $folder; self::$addSyncFolders[$folder->BackendId] = $folder;
} }
......
...@@ -326,6 +326,7 @@ define("SYNC_FOLDERHIERARCHY_VERSION","FolderHierarchy:Version"); ...@@ -326,6 +326,7 @@ define("SYNC_FOLDERHIERARCHY_VERSION","FolderHierarchy:Version");
define("SYNC_FOLDERHIERARCHY_IGNORE_STORE","FolderHierarchy:IgnoreStore"); define("SYNC_FOLDERHIERARCHY_IGNORE_STORE","FolderHierarchy:IgnoreStore");
define("SYNC_FOLDERHIERARCHY_IGNORE_NOBCKENDFLD","FolderHierarchy:IgnoreNoBackendFolder"); define("SYNC_FOLDERHIERARCHY_IGNORE_NOBCKENDFLD","FolderHierarchy:IgnoreNoBackendFolder");
define("SYNC_FOLDERHIERARCHY_IGNORE_BACKENDID","FolderHierarchy:IgnoreBackendId"); define("SYNC_FOLDERHIERARCHY_IGNORE_BACKENDID","FolderHierarchy:IgnoreBackendId");
define("SYNC_FOLDERHIERARCHY_IGNORE_READONLY","FolderHierarchy:IgnoreReadOnly");
// MeetingResponse // MeetingResponse
define("SYNC_MEETINGRESPONSE_CALENDARID","MeetingResponse:CalendarId"); define("SYNC_MEETINGRESPONSE_CALENDARID","MeetingResponse:CalendarId");
......
...@@ -105,7 +105,7 @@ abstract class Backend implements IBackend { ...@@ -105,7 +105,7 @@ abstract class Backend implements IBackend {
* Methods to be implemented * Methods to be implemented
* *
* public function Logon($username, $domain, $password); * public function Logon($username, $domain, $password);
* public function Setup($store, $checkACLonly = false, $folderid = false); * public function Setup($store, $checkACLonly = false, $folderid = false, $readonly = false);
* public function Logoff(); * public function Logoff();
* public function GetHierarchy(); * public function GetHierarchy();
* public function GetImporter($folderid = false); * public function GetImporter($folderid = false);
......
...@@ -69,11 +69,12 @@ abstract class BackendDiff extends Backend { ...@@ -69,11 +69,12 @@ abstract class BackendDiff extends Backend {
* @param string $store target store, could contain a "domain\user" value * @param string $store target store, could contain a "domain\user" value
* @param boolean $checkACLonly if set to true, Setup() should just check ACLs * @param boolean $checkACLonly if set to true, Setup() should just check ACLs
* @param string $folderid if set, only ACLs on this folderid are relevant * @param string $folderid if set, only ACLs on this folderid are relevant
* @param boolean $readonly if set, the folder needs at least read permissions
* *
* @access public * @access public
* @return boolean * @return boolean
*/ */
public function Setup($store, $checkACLonly = false, $folderid = false) { public function Setup($store, $checkACLonly = false, $folderid = false, $readonly = false) {
$this->store = $store; $this->store = $store;
// we don't know if and how diff backends implement the "admin" check, but this will disable it for the webservice // we don't know if and how diff backends implement the "admin" check, but this will disable it for the webservice
......
...@@ -102,11 +102,12 @@ interface IBackend { ...@@ -102,11 +102,12 @@ interface IBackend {
* @param string $store target store, could contain a "domain\user" value * @param string $store target store, could contain a "domain\user" value
* @param boolean $checkACLonly if set to true, Setup() should just check ACLs * @param boolean $checkACLonly if set to true, Setup() should just check ACLs
* @param string $folderid if set, only ACLs on this folderid are relevant * @param string $folderid if set, only ACLs on this folderid are relevant
* @param boolean $readonly if set, the folder needs at least read permissions
* *
* @access public * @access public
* @return boolean * @return boolean
*/ */
public function Setup($store, $checkACLonly = false, $folderid = false); public function Setup($store, $checkACLonly = false, $folderid = false, $readonly = false);
/** /**
* Logs off * Logs off
......
...@@ -1120,7 +1120,15 @@ class Sync extends RequestProcessor { ...@@ -1120,7 +1120,15 @@ class Sync extends RequestProcessor {
// changecount is initialized with 'false', so 0 means no changes! // changecount is initialized with 'false', so 0 means no changes!
if ($changecount === 0 || ($changecount !== false && $changecount <= $windowSize)) { if ($changecount === 0 || ($changecount !== false && $changecount <= $windowSize)) {
self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_COMPLETED); self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_COMPLETED);
$this->setFolderStat($spa, $newFolderStat);
// we should update the folderstat, but we recheck to see if it changed. If so, it's not updated to force another sync
$newFolderStatAfterExport = self::$backend->GetFolderStat(ZPush::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()), $spa->GetBackendFolderId());
if ($newFolderStat === $newFolderStatAfterExport) {
$this->setFolderStat($spa, $newFolderStat);
}
else {
ZLog::Write(LOGLEVEL_DEBUG, "Sync() Folderstat differs after export, force another exporter run.");
}
} }
else else
self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_INPROGRESS); self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_INPROGRESS);
...@@ -1226,29 +1234,30 @@ class Sync extends RequestProcessor { ...@@ -1226,29 +1234,30 @@ class Sync extends RequestProcessor {
$status = $this->loadStates($sc, $spa, $actiondata, true); $status = $this->loadStates($sc, $spa, $actiondata, true);
try { try {
// Configure importer with last state if ($status == SYNC_STATUS_SUCCESS) {
$this->importer = self::$backend->GetImporter($spa->GetBackendFolderId()); // Configure importer with last state
$this->importer = self::$backend->GetImporter($spa->GetBackendFolderId());
// if something goes wrong, ask the mobile to resync the hierarchy // if something goes wrong, ask the mobile to resync the hierarchy
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);
// 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());
// 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"));
}
} }
catch (StatusException $stex) { catch (StatusException $stex) {
$status = $stex->getCode(); $status = $stex->getCode();
} }
$this->importer->LoadConflicts($spa->GetCPO(), $sc->GetParameter($spa, "state"));
return $status; return $status;
} }
......
...@@ -54,6 +54,7 @@ class SyncFolder extends SyncObject { ...@@ -54,6 +54,7 @@ class SyncFolder extends SyncObject {
public $Store; public $Store;
public $NoBackendFolder; public $NoBackendFolder;
public $BackendId; public $BackendId;
public $ReadOnly;
function SyncFolder() { function SyncFolder() {
$mapping = array ( $mapping = array (
...@@ -79,6 +80,10 @@ class SyncFolder extends SyncObject { ...@@ -79,6 +80,10 @@ class SyncFolder extends SyncObject {
SYNC_FOLDERHIERARCHY_IGNORE_BACKENDID => array ( self::STREAMER_VAR => "BackendId", SYNC_FOLDERHIERARCHY_IGNORE_BACKENDID => array ( self::STREAMER_VAR => "BackendId",
self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE), self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE),
SYNC_FOLDERHIERARCHY_IGNORE_READONLY => array ( self::STREAMER_VAR => "ReadOnly",
self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE),
); );
parent::SyncObject($mapping); parent::SyncObject($mapping);
......
...@@ -166,6 +166,80 @@ abstract class SyncObject extends Streamer { ...@@ -166,6 +166,80 @@ abstract class SyncObject extends Streamer {
return true; return true;
} }
/**
* Compares this a SyncObject to another, while printing out all properties and showing where they differ.
*
* @see SyncObject
* @param SyncObject $odo other SyncObject
* @param string $odoName how different data should be named
* @param int $recCount recursion counter
* @return array with one property per line, key being the property instance variable name
*/
public function EvaluateAndCompare($odo, $odoName = "", $keyprefix = "", $recCount = 0) {
if ($odo === false)
return false;
// check objecttype
if (! ($odo instanceof SyncObject)) {
ZLog::Write(LOGLEVEL_DEBUG, "SyncObject->EvaluateAndCompare() the target object is not a SyncObject");
return false;
}
$out = array();
if ($keyprefix)
$keyprefix = $keyprefix . $recCount;
// check for mapped fields
foreach ($this->mapping as $v) {
$val = $v[self::STREAMER_VAR];
// array of values?
if (isset($v[self::STREAMER_ARRAY])) {
// if neither array is created then don't fail the comparison
if (!isset($this->$val) && !isset($odo->$val)) {
continue;
}
else {
// if both arrays exist then seek for differences in the arrays
if (count(array_diff($this->$val, $odo->$val)) + count(array_diff($odo->$val, $this->$val)) > 0) {
$out[$keyprefix.$val] = implode(", ", $this->$val) ." - ". $odoName .": ". implode(", ", $odo->$val);
}
}
}
else {
// if both are not set, don't even bother the output
if (!isset($this->$val) && !isset($odo->$val)) {
continue;
}
// they are both set
else if (isset($this->$val) && isset($odo->$val)) {
//if they are subobjects, compare them recursively
if (isset($v[self::STREAMER_TYPE])) {
if ($this->$val instanceof SyncObject) {
$out += $this->$val->EvaluateAndCompare($odo->$val, $odoName, substr(get_class($this->$val), 4), $recCount++);
}
// if they are streams, compare the streams
else if ($v[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASPLAIN || $v[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASBASE64) {
$t = stream_get_contents($this->$val);
$o = stream_get_contents($odo->$val);
$out[$keyprefix.$val] = ($t === $o) ? $t : $t." - ". $odoName .": ".$o;
}
}
// else just compare their values
else {
if($this->$val === $odo->$val) {
$out[$keyprefix.$val] = $this->$val;
}
else {
$out[$keyprefix.$val] = (isset($this->$val) && $this->$val ? $this->$val:"undefined") ." - ". $odoName .": ". (isset($odo->$val) && $odo->$val ? $odo->$val:"undefined");
}
}
}
}
}
return $out;
}
/** /**
* String representation of the object * String representation of the object
* *
......
...@@ -498,6 +498,7 @@ class ZPushAdmin { ...@@ -498,6 +498,7 @@ class ZPushAdmin {
'syncfolderid' => $device->GetFolderIdForBackendId($fid, false), 'syncfolderid' => $device->GetFolderIdForBackendId($fid, false),
'name' => $so->displayname, 'name' => $so->displayname,
'type' => $so->type, 'type' => $so->type,
'readonly' => $so->ReadOnly,
'source' => 'static' 'source' => 'static'
); );
} }
......
...@@ -632,7 +632,7 @@ class ZPushAdminCLI { ...@@ -632,7 +632,7 @@ class ZPushAdminCLI {
$folders = array(); $folders = array();
foreach ($device->GetAllFolderIds() as $folderid) { foreach ($device->GetAllFolderIds() as $folderid) {
// if submitting a folderid as type to resync a specific folder. // if submitting a folderid as type to resync a specific folder.
if ($folderid == $type) { if ($folderid == $type || $device->GetFolderBackendId($folderid) === $type) {
printf("Found and resynching requested folderid '%s' on device '%s' of user '%s'\n", $folderid, $deviceId, $user); printf("Found and resynching requested folderid '%s' on device '%s' of user '%s'\n", $folderid, $deviceId, $user);
$folders[] = $folderid; $folders[] = $folderid;
break; break;
......
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