Для реализации одного проекта возникла необходимость использовать какое-либо облачное хранилище. Суть заключается в том, что есть несколько распределенных сотрудников, каждый из которых использует специальную программу, результатом деятельности которой являются обособленные XML-файлы. Данные этих файлов необходимы для того, чтобы сформировать отчетность на сайте, где пользователи могли бы скачать доступную им информацию. Проблема в том, что сотрудники работают на своих локальных машинах и как бы с интернетом не взаимодействуют (то есть не используется какой-то единый сервер или типа того). И вот я решил задействовать Dropbox, типа настрою каждому пользователю свою папочку, куда и будут синхронизироваться их файлы, а на стороне сайта по крону буду все забирать с дропбокса и использовать для формирования отчетности. Под катом расскажу, что из этого получилось.
Вообще, изначально я думал, что все будет гораздо проще, так как довольно давно уже натыкался на готовый компонент, который устанавливает в MODX источник файлов, взаимодействующий с Dropbox, но когда я его установил, получил только кучу фатальных ошибок. Компонент разрабатывался в 2012-ом году, и то ли использовал старое Dropbox PHP SDK, то ли вообще что-то самописное было, но в любом случае оно уже совсем не работало. В итоге было понятно, что все придется переписывать. Примеров особых я не нашел в сети, но дропбокс радует довольно добротной документацией здесь и здесь. В итоге я взял имеющийся в пакете объект источника файлов за основу, залил последнюю версию PHP SDK (сейчас это 1.1.4) и допилил до состояния, когда через админку сайта можно было управлять файлами в дропбоксе.
На этом завершилась разработка пакета dropbox-2.0.0-beta. Но покоя не давала мысль, что нет у меня еще возможности получать от дропбокса данные изменений. Просто насколько я понимаю, мне придется иметь дело с десятками тысяч документов в итоге, и не могу же я каждый раз просто перебором проходиться по папкам и сверять ревизии уже на стороне сайта (к слову, за раз дропбокс методом Client::getMetadataWithChildren($path) возвращает не более 2000 записей, хотя в методе указано 25000). Поковыряв доки, добрался до метода Client::getDelta($cursor = null, $pathPrefix = null). Это как раз то, что нам нужно, так как передав в него известный курсор, можно получить информацию о изменениях с момента его создания. Но вот здесь небольшая неприятность: как получить последний курсор? Дело в том, что если дропбокс-аккаунт давно уже существует, в нем за всю историю может накопиться куча изменений, и простым перебором курсоров можно очень долго добираться до актуального. Что интересно, такого метода нет в PHP SDK, хотя в самом API для этого есть метод. Ну да ладно, если в ядре есть метод, то не долго допилить свой класс клиента, чтобы умел получать эту информацию. Теперь можно зафиксировать для себя отправную точку и следить за изменениями без всяких переборов. Когда я все это получил, для меня стало очевидно, что эти данные удобно было бы хранить в базе данных, а некоторые профильные методы дропбокс-клиента вынести в специфические сущности. Далее просто покажу несколько примеров использования того, что получилось.
Регистрация API-приложения.
Конечно же прежде чем что-то делать, надо зарегистрировать свое Dropbox-приложение (разумеется, вы должны быть зарегистрированы в Dropbox).
Здесь сразу же подробней рассмотрим шаг 3.
Can your app be limited to its own folder?
Если выбрать «My app only needs access to files it creates.», то для приложения будет создана собственная папка, куда и будет доступ у приложения. У меня это папка Приложения/[AppName]. То есть папка Приложения — это системная для всех приложений, а далее для каждого приложения создается своя папка.
А если выбрать «My app needs access to files already on Dropbox.», то приложение получит доступ к корневой папке и далее список доступных файлов будет ограничиваться только выбранными типами файлов. То есть если вы выбираете этот пункт, то появляется новый шаг What type of files does your app need access to? и там вы можете выбрать или «All file types» (то есть все файлы) или «Specific file types» и указать какие типы файлов доступны.
После того, как мы нажали «Create App», приложение будет создано и нам открывается страница настроек приложения. На ней мы кроме всего прочего найдем App key и App secret, но они нам пока не понадобятся, их использование мы рассмотрим позже. А пока нас интересует Access token. Сгенерируем его и скопипастим куда-нибудь в недоступное место. Повторно его не получится увидеть. Правда можно сгенерировать новый токен, и что интересно — работать будут и предшествующие. Я сгенерил три токена и все они работают, при чем я не могу увидеть список используемых токенов и отменить не нужные, могу только удалить все приложение. Катца не секурно немного, но это скорее фича нежели бага.
Настройка источника файлов
После установки пакета Dropbox в системе появляется новый тип источников файлов — Dropbox. Создаем медиасурс с этим типом.
Когда создали источник файлов, открываем его для редактирования и указываем Access Token приложения.
Если все ОК, то после сохранения источник файлов должен показывать содержимое вашего дропбокса (см. первую картинку вверху).
Dropbox-клиент
Первое, что я сделал в обновленной версии пакета — это вынес возможность получить Dropbox-клиент в отдельный метод медиасурса. Получить его можно, к примеру, вот так:
$source_id = 27; // ID вашего источника файлов
$source = $modx->getObject('modMediaSource', $source_id);
$client = $source->getClient();
Это удобно тем, что можно иметь сколько угодно медиасурсов и на заморачиваться с их настройками, при этом каждый медиасурс будет работать со своей дропбокс-папкой.
Получаем информацию о содержимом директорий
$source_id = 27; // ID вашего источника файлов
$source = $modx->getObject('modMediaSource', $source_id);
$client = $source->getClient();
$result = $client->getMetadataWithChildren('/');
print_r($result);
Array
(
[hash] => cfcf0b65c94cff03c32390b3c30a5812
[thumb_exists] =>
[bytes] => 0
[path] => /
[is_dir] => 1
[icon] => folder
[root] => dropbox
[contents] => Array
(
[0] => Array
(
[rev] => 115ce031c0d71
[thumb_exists] => 1
[path] => /315a73ec24 - копия.jpg
[is_dir] =>
[client_mtime] => Fri, 05 Dec 2014 23:12:30 +0000
[icon] => page_white_picture
[bytes] => 258713
[modified] => Mon, 08 Dec 2014 02:45:31 +0000
[size] => 252.6 KB
[root] => dropbox
[mime_type] => image/jpeg
[revision] => 71118
)
[1] => Array
(
[rev] => 102d6031c0d71
[thumb_exists] => 1
[path] => /315a73ec24.jpg
[is_dir] =>
[client_mtime] => Fri, 05 Dec 2014 23:12:30 +0000
[icon] => page_white_picture
[bytes] => 258713
[modified] => Sat, 06 Dec 2014 21:21:20 +0000
[size] => 252.6 KB
[root] => dropbox
[mime_type] => image/jpeg
[revision] => 66262
)
)
[size] => 0 bytes
)
Получаем содержимое файла
$source_id = 27; // ID вашего источника файлов
$source = $modx->getObject('modMediaSource', $source_id);
$content = $source->getContent('/315a73ec24.jpg');
$source_id = 27; // ID вашего источника файлов
$entry = $modx->newObject('DropboxEntry', array(
"source_id" => $source_id,
"path" => '/315a73ec24.jpg',
));
$content = $entry->getContent(); // Или $entry->get('content');
$source_id = 27; // ID вашего источника файлов
$source = $modx->getObject('modMediaSource', $source_id);
$entry = $modx->newObject('DropboxEntry', array(
"path" => '/315a73ec24.jpg',
));
$entry->Source = $source;
$content = $entry->getContent(); // Или $entry->get('content');
$entry = $modx->getObject('DropboxEntry', array(
"path" => '/315a73ec24.jpg',
));
$content = $entry->getContent(); // Или $entry->get('content');
Отслеживаем изменения в разделах
Здесь необходимы два этапа:
$source_id = 27; // ID вашего источника файлов
$cursor = $modx->newObject('DropboxCursor', array(
"source_id" => $source_id,
"path" => '/',
));
if($result = $cursor->getLast()){
$cursor->fromArray($result);
$cursor->save();
}
$cursor= $modx->getObject('DropboxCursor', array(
"path" => '/',
));
$result = $entry->getDelta();
print_r($result);
Array
(
[has_more] =>
[cursor] => AAE3sefSDffe3SiOcfkaMd7vyzjQvjxngkI1gkJGLvBQplsNUhF9tx5Okb7epnGufIziuDWjyG16sWaxjRcnt-XjT-jBD_8fDW89enF7OO3AHBjsZz9vHTBJenmeCdLUgO0f-EqRJYigzELD0rzD3hFNn-YGfDQaxUOs0bHd2F5qND3J6HX-HwOqOLWQfcbuMZMNHN1_UGN2VODIbaPzBbML0rap_B6ibV03JQxtiGHZtxwjP-mSw8drigK3yJtvqyR3XdzHP5uVsyrD8gkyGk1J9dOTLdzFRA-woImCg1Iwc9XA7l1XsZGCRvDYpXjg
[entries] => Array
(
)
[reset] =>
)
Если будут изменения, то вы их увидите в массиве entries.
Ревизии файлов
$entry = $modx->getObject('DropboxEntry', array(
"path" => '/315a73ec24.jpg',
));
$result = $entry->getRevisions();
$entry = $modx->getObject('DropboxEntry', array(
"path" => '/315a73ec24.jpg',
));
$result = $entry->restoreFile('102d6031c0d71');
В общем, если покопать официальную документацию, можно много чего на базе этого сделать. А вообще есть желание разработать под MODX компонент файлового менеджера, который умел бы обмениваться файлами между различными типами медиасурсов, но пока на это нет времени/ресурсов.
Скачать пакет можно с оффрепозитория modx.com
Проект на гитхабе: github.com/Fi1osof/modx-Dropbox
И напоследок небольшая заметка об одной коварной проблемке, с которой столкнулся во время разработки компонента.
Автор: Fi1osof