Pimcore Dropdox Sync

Dominik [php, Pimcore, Tricks und Tools, Technologien]

Als Programmieren bin ich ja grundsätzlich darauf aus, möglichst viel Arbeit zu automatisieren. Das nicht nur mir, sondern auch unseren Kunden. Im Laufe einer "Wäre es nicht bequem wenn..." Diskussion kam die Idee auf, die Asset Ordner von Pimcore mit Dropbox zu synchronisieren. So spart man sich den lästen Schritt des Hochladens, indem man seine Dateien ganz einfach bereits auf dem eigenen Computer am richtigen Ort ablegt. Dank Dropbox funktioniert das auch auf diverses Betriebssystemen.

Verwendet man desweiteren so wie wir Area-Bricks, die einen Asset Ordner als Galerie rendern, kann der Kunde mit einem einfachen verschieben einer Datei auf seinem Computer seine Homepage aktualisieren. Wenn das mal nicht ein Verkaufsargument ist!

Die Dropbox API

Dropbox hat eine gut dokumentierte Schnittstelle. Auf der selben Seite findet sich eine PHP Library zum Download sowie ein gut verständliches Tutorial. Diese kommt in unserem eigenen pimcore dropbox plugin zum Einsatz.

Das Pimcore Dropbox Plugin

Neben den üblichen Teilen wie dem plugin.xml und dropbox php library hier die besonderen Ausschnitte.

Die install methode erstellt uns eine Tabelle, in der wir den API Code ablegen, sowie den sync cursor. Mithilfe des sync Cursers weis die API, welche Dateien bereits übertragen wurden damit nur die Änderungen synchronisiert werden.

public static function install() {
    $db = Pimcore_Resource_Mysql::get();
    $db->query("DROP TABLE IF EXISTS `plugin_dropboxsync_settings`;");

    $db->query("CREATE TABLE `plugin_dropboxsync_settings` (
            `id` TINYINT NOT NULL,
            `api_code` TEXT NULL,
            `sync_cursor` TEXT NULL,
                  PRIMARY KEY  (`id`)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
    $db->query("INSERT INTO plugin_dropboxsync_settings VALUES(1, NULL, NULL)");
    [...]
}

Damit der sync von selbst im Hintergrund abläuft, hängen wir unser plugin in das maintenance script.

public function maintenance() {
    if (!self::isInstalled()) return;

    $log = array();
    $isOk = $this->syncDropbox($log);
    if (!$isOk) {
        echo join("n", $log);
    }
}

Nachfolgend holen wir den API Code sowie den aktuellen Cursor aus der Datenbank. Über die Dropbox Library erhalten wir ein $data array welches man nun ganz einfach durchiterieren kann.

$db = Pimcore_Resource_Mysql::get();
$settings = $db->fetchAll("SELECT * from plugin_dropboxsync_settings WHERE id = 1");

$accessToken = $settings[0]["api_code"];
if ($accessToken == null) {
    $log[] = "Dropbox Plugin: No AccessToken configured!";
    return false;
}
$client = new dbxClient($accessToken, "pimcore-sync", "en");

$cursor = $settings[0]["sync_cursor"];
$data = $client->getDelta($cursor);

Hier die gekürzte Iteration über das erhaltene Array. Je nachdem ob der aktuelle Eintrag ein Verzeichnis, eine Datei oder aber eine zu löschende Datei ist,
wird die dazugehörige Aktion ausgeführt. Das ist entweder das Erstellen des Ordners bzw. des neuen Assets oder aber das Ersetzen oder Löschen derjenigen. 

foreach ($data["entries"] as $entry) {
    $fullPath = "/".$entry[0];
    [...]
    if ($entry[1]["is_dir"]) {
        // Ordner erstellen falls noch nicht existent
        $folder = Asset_Folder::getByPath($fullPath);
        if ($folder == null) {
            self::createAssetFolder($fullPath, $log);
        }
    } else if ($entry[1] != null) {
        $file = Asset_Unknown::getByPath($fullPath);
        [...]
        if ($file == null) {
            // Neues File
            $tempFilePath = $this->downloadToTemp($client, $dropboxPath);
            $folder = Asset_Folder::getByPath($folderName);
            $file = new Asset_Unknown();
            $file->setParentId($folder->getId());
            $file->setFilename(Pimcore_File::getValidFilename($filename));
            $fileContent = file_get_contents($tempFilePath);
            $file->setData($fileContent);
            $file->save();
        } else if ($file != null && $file->getModificationDate() < strtotime($entry[1]["modified"])) {
            // Ersetze bestehendes File.
            $tempFilePath = $this->downloadToTemp($client, $dropboxPath);
            $file->setData(file_get_contents($tempFilePath));
            $file->save();
        }
    } else if ($entry[1] == null) {
        // Asset lösschen
        [...]
        $file = Asset_Unknown::getByPath($folderName."/".Pimcore_File::getValidFilename($filename));
        if ($file != null) {
            $file->delete();
        }
    }
}

Das funktioniert soweit schon ganz gut. Es gibt aber noch einige Baustellen. So etwa haben wir es bisher noch nicht für nötig befunden, ein Webinterface für die Eingabe des API Keys zu erstellen. Ebenso wird der Sync Aufruf derzeit mit jedem aufruf des maintenance Scripts, also in der Regel alle 5 Minuten getätigt. Dies wäre allenfalls ebenso über ein Webinterface zu konfigurieren.

Auch dürfen derzeit nicht zu viele Dateien aufs mal synchronisiert werden. Dies zeigt sich vorallem dann, wenn das Plugin installiert wird und zum ersten mal mit einem Dropbox Ordner synchronisiert, welcher viele Dateien enthält. Dies kann auf zu Memory Problemen führen, da die Dateien jeweils von der Library ins Memory geladen werden um von dort ins Asset Objekt überführt zu werden.

Interessenten am Plugin dürfen gerne Kontakt aufnehmen.

zurück