Skip to content

Commit

Permalink
[jan] Add API to backup and restore user data.
Browse files Browse the repository at this point in the history
  • Loading branch information
yunosh committed Jun 6, 2017
1 parent 7327304 commit 1bd442c
Show file tree
Hide file tree
Showing 4 changed files with 315 additions and 5 deletions.
1 change: 1 addition & 0 deletions turba/docs/CHANGES
Expand Up @@ -2,6 +2,7 @@
v5.0.0-git
----------

[jan] Add API to backup and restore user data.
[jan] Show photos when displaying contacts in calendars.
[jan] Add CLI script to search for invalid email addresses.
[mjr] Remove Facebook driver.
Expand Down
187 changes: 184 additions & 3 deletions turba/lib/Application.php
Expand Up @@ -11,6 +11,10 @@
* @package Turba
*/

use Horde\Backup;
use Sabre\CalDAV;
use Sabre\CardDAV;

/**
* Turba application API.
*
Expand Down Expand Up @@ -43,9 +47,6 @@
* Horde_Registry_Application::). */
require_once HORDE_BASE . '/lib/core.php';

use Sabre\CalDAV;
use Sabre\CardDAV;

class Turba_Application extends Horde_Registry_Application
{
/**
Expand Down Expand Up @@ -492,6 +493,186 @@ public function topbarCreate(Horde_Tree_Renderer_Base $tree, $parent = null,
));
}

/* Backup/restore */

/**
*/
public function backup(array $users = array())
{
global $injector, $session;

$factory = $injector->getInstance('Turba_Factory_Driver');
$cfgSources = Turba::availableSources();

if (!$users && $session->get('turba', 'has_share')) {
foreach ($injector->getInstance('Turba_Shares')->listAllShares() as $share) {
$users[$share->get('owner')] = true;
}
$users = array_keys($users);
}

$getUser = function($user) use ($factory, $cfgSources)
{
global $injector, $registry, $session;

$backup = new Backup\User($user);

// Need to pushApp() here because this method is called delayed,
// but we need Turba's $conf.
$pushed = $registry->pushApp(
'turba', array('check_perms' => false)
);

if ($session->get('turba', 'has_share')) {
$turba_shares = $injector->getInstance('Turba_Shares');
$shares = $turba_shares->listShares(
$user,
array('attributes' => $user)
);
$cfgSources = Turba::getConfigFromShares(
$cfgSources,
true,
array('shares' => $shares, 'auth_user' => $user)
);
$addressbooks = array();
foreach ($shares as $share) {
$addressbooks[$share->getId()] = $share->toHash();
}
$backup->collections[] = new Backup\Collection(
new ArrayIterator($addressbooks),
$user,
'addressbooks'
);
}

foreach ($cfgSources as $sourceId => $source) {
$driver = $factory->create($source, $sourceId, $cfgSources);
if ($driver->getContactOwner() == $registry->getAuth()) {
$driver->setContactOwner($user);
}
$backup->collections[] = new Backup\Collection(
new Turba\Backup\Contacts($driver, $user),
$user,
'contacts'
);
}

if ($pushed === true) {
$registry->popApp();
}

return $backup;
};

return new Backup\Users(new ArrayIterator($users), $getUser);
}

/**
*/
public function restore(Backup\Collection $data)
{
global $injector, $registry, $session;

$user = $data->getUser();

switch ($data->getType()) {
case 'addressbooks':
$turba_shares = $injector->getInstance('Turba_Shares');
foreach ($data as $addressbook) {
$addressbook['owner'] = $user;
$addressbook['attributes'] = array_intersect_key(
$addressbook['attributes'],
array(
'name' => true,
'desc' => true,
'params' => true)
);
$turba_shares->fromHash($addressbook);
}
break;

case 'contacts':
$cfgSources = Turba::availableSources();
if ($session->get('turba', 'has_share')) {
$turba_shares = $injector->getInstance('Turba_Shares');
$shares = $turba_shares->listShares(
$user,
array('attributes' => $user)
);
$cfgSources = Turba::getConfigFromShares(
$cfgSources,
true,
array('shares' => $shares, 'auth_user' => $user)
);
}
$map = array();
foreach (array('Object', 'Group') as $type) {
foreach ($data as $contact) {
if ($contact['contact']['__type'] != $type) {
continue;
}
$map = $this->_restoreContact(
$contact, $user, $cfgSources, $map
);
}
}
break;
}
}

/**
* Restores a single contact.
*
* @param array $contact A contact hash.
* @param string $user A user name.
* @param array $cfgSources A backend configuration list.
* @param array $map Maps old group member IDs to new IDs.
*
* @return array The updated map.
*/
protected function _restoreContact($contact, $user, $cfgSources, $map)
{
global $injector, $registry;

$driver = $injector->getInstance('Turba_Factory_Driver')->create(
$cfgSources[$contact['addressbook']],
$contact['addressbook'],
$cfgSources
);
if ($driver->getContactOwner() == $registry->getAuth()) {
$driver->setContactOwner($user);
}
$blobs = array_keys($driver->getBlobs());
foreach ($blobs as $blob) {
if (strlen($contact['contact'][$blob])) {
$contact['contact'][$blob] = base64_decode(
$contact['contact'][$blob]
);
}
}
if ($contact['contact']['__type'] == 'Group') {
$members = array();
foreach (unserialize($contact['contact']['__members']) as $member) {
if (isset($map[$member])) {
$members[] = $map[$member];
}
}
$contact['contact']['__members'] = serialize($members);
}
$map[$contact['contact']['__key']] = $driver->add(
$contact['contact']
);

return $map;
}

/**
*/
public function restoreDependencies()
{
return array('contacts' => array('addressbooks'));
}

/* Download data. */

/**
Expand Down
122 changes: 122 additions & 0 deletions turba/lib/Backup/Contacts.php
@@ -0,0 +1,122 @@
<?php
/**
* Copyright 2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (ASL). If you did
* did not receive this file, see http://www.horde.org/licenses/apache.
*
* @author Jan Schneider <jan@horde.org>
* @license http://www.horde.org/licenses/apache ASL
* @package Turba
*/

namespace Turba\Backup;

use ArrayIterator;
use EmptyIterator;
use Iterator;
use Turba_Driver;

/**
* Backup iterator for contacts.
*
* @author Jan Schneider <jan@horde.org>
* @copyright 2017 Horde LLC
* @license http://www.horde.org/licenses/apache ASL
* @package Turba
*/
class Contacts implements Iterator
{
/**
* The driver instance.
*
* @var Turba_Driver
*/
protected $_driver;

/**
* The blob attributes.
*
* @var array
*/
protected $_blobs;

/**
* The contacts iterator.
*
* @var Iterator
*/
protected $_list;

/**
* Constructor.
*
* @param Turba_Driver $driver A driver instance.
*/
public function __construct(Turba_Driver $driver)
{
$this->_driver = $driver;
$this->_blobs = array_keys($this->_driver->getBlobs());
}

// Iterator methods.

/**
*/
public function current()
{
$current = $this->_list->current();
if (!$current) {
return false;
}
$hash = $current->getAttributes();
foreach ($this->_blobs as $blob) {
if (strlen($hash[$blob])) {
$hash[$blob] = base64_encode($hash[$blob]);
}
}
return array(
'addressbook' => $this->_driver->getName(),
'contact' => $hash
);
}

/**
*/
public function key()
{
$current = $this->_list->current();
if (!$current) {
return false;
}
return $current->getValue('__key');
}

/**
*/
public function next()
{
$this->_list->next();
}

/**
*/
public function rewind()
{
if (!isset($this->_driver->map['__owner'])) {
$this->_list = new EmptyIterator();
return;
}

$this->_list = new ArrayIterator(
$this->_driver->search(array())->objects
);
}

/**
*/
public function valid()
{
return $this->_list->valid();
}
}
10 changes: 8 additions & 2 deletions turba/package.xml
Expand Up @@ -28,7 +28,7 @@
<email>chuck@horde.org</email>
<active>no</active>
</lead>
<date>2016-12-14</date>
<date>2017-06-06</date>
<version>
<release>5.0.0</release>
<api>5.0.0</api>
Expand All @@ -39,6 +39,7 @@
</stability>
<license uri="http://www.horde.org/licenses/apache">ASL</license>
<notes>
* [jan] Add API to backup and restore user data.
* [jan] Show photos when displaying contacts in calendars.
* [jan] Add CLI script to search for invalid email addresses.
* [mjr] Remove Facebook driver.
Expand Down Expand Up @@ -134,6 +135,9 @@
</dir> <!-- /lib/Ajax/Imple -->
<file name="Application.php" role="horde" />
</dir> <!-- /lib/Ajax -->
<dir name="Backup">
<file name="Contacts.php" role="horde" />
</dir> <!-- /lib/Backup -->
<dir name="Block">
<file name="Minisearch.php" role="horde" />
</dir> <!-- /lib/Block -->
Expand Down Expand Up @@ -946,6 +950,7 @@
<install as="turba/lib/Ajax/Application/Handler/Minisearch.php" name="lib/Ajax/Application/Handler/Minisearch.php" />
<install as="turba/lib/Ajax/Application/Handler/Smartmobile.php" name="lib/Ajax/Application/Handler/Smartmobile.php" />
<install as="turba/lib/Ajax/Imple/TagAutoCompleter.php" name="lib/Ajax/Imple/TagAutoCompleter.php" />
<install as="turba/lib/Backup/Contacts.php" name="lib/Backup/Contacts.php" />
<install as="turba/lib/Block/Minisearch.php" name="lib/Block/Minisearch.php" />
<install as="turba/lib/Data/Ldif.php" name="lib/Data/Ldif.php" />
<install as="turba/lib/Driver/Favourites.php" name="lib/Driver/Favourites.php" />
Expand Down Expand Up @@ -2197,7 +2202,7 @@
</notes>
</release>
<release>
<date>2016-12-14</date>
<date>2017-06-06</date>
<version>
<release>5.0.0</release>
<api>5.0.0</api>
Expand All @@ -2208,6 +2213,7 @@
</stability>
<license uri="http://www.horde.org/licenses/apache">ASL</license>
<notes>
* [jan] Add API to backup and restore user data.
* [jan] Show photos when displaying contacts in calendars.
* [jan] Add CLI script to search for invalid email addresses.
* [mjr] Remove Facebook driver.
Expand Down

0 comments on commit 1bd442c

Please sign in to comment.