Fi1osof 04 декабря 2014 3 12
Разрабатываем мы тут один проект, для реализации которого сразу было понятно, что надо будет использовать Dropbox. Суть в том, чтобы в одном месте синхронизировать рабочие файлы различных сотрудников, каждый из которых работает на своем локальном компьютере и как бы не подключен к какому-то единому серверу. А дальше уже эти файлы затягивать на сервер, обрабатывать, превращать в нужные сущности и в личном кабинете конечные клиенты могли бы получать доступ к преобразованной информации, чтобы распечатать или отправить себе на почту в виде PDF-файла (ну, это в общих чертах).

Итак, главная здесь задача — это работать с Dropbox посредством API. Честно сказать, я думал все будет намного проще, ибо я и ранее в сети видел упоминания о Dropbox MediaSource for MODX, и одно из них на странице Марка Хамстра. Но установив этот пакет при попытке просмотреть список файлов в полученном источнике файлов все, что я увидел — фатальная ошибка выполнения скрипта. Вообще пакет написан в 2012-ом году, так что он может быть просто не актуален. Забегая вперед, скажу, что этот пакет я в последствии перепишу и заставлю таки нормально работать, но это уже позже. Есть еще профильная статья на modx.pro, но это работа shell-командами, а меня интересует именно взаимодействие средствами PHP, то есть в целом эта статья мне тоже никак не помогла, во всяком случае в текущем вопросе. В общем, в этой же статье я просто опишу много того, с чем столкнулся и чего нового узнал/освоил. Она будет объемная, но и полезной информации тоже много.

Кстати, здесь и далее я буду писать только о том, что сам пробовал и какие результаты получил. В действительности же функционал в разы больше, чем мы здесь рассматриваем, так как только типов API у Dropbox несколько. На этот счет приглянулась вот эта статья.

Создание API-приложения

Для того, чтобы к вашему dropbox-у появился какой-то доступ извне, надо создать приложение (само собой разумеется, что вы для начала должны зарегистрироваться в Dropbox). Для этого идем на страничку www.dropbox.com/developers/apps и жмем Create app (Кстати, название приложения должно быть уникальное для всего Dropbox-а (так как есть возможность публиковать публичные приложения, так что лучше указывать домен сайта или типа того, ибо названия типа myApp и т.п. заняты и дропбокс не даст сохранить)). Типов создаваемых приложений два Drop-ins app и Dropbox API app, мы будем создавать второй.


Здесь сразу же подробней рассмотрим шаг 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. Сгенерируем его и скопипастим куда-нибудь в недоступное место. Повторно его не получится увидеть. Правда можно сгенерировать новый токен, и что интересно — работать будут и предшествующие. Я сгенерил три токена и все они работают, при чем я не могу увидеть список используемых токенов и отменить не нужные, могу только удалить все приложение. Катца не секурно немного, но это скорее фича нежели бага.


Выполнение основных запросов.

Все, access token у нас есть, можно выполнять простейшие запросы. Здесь нам в помощь оффдокументация по CORE API for PHP. Для начала качаем PHP SDK (я качал v.1.1.4) (Git-проект). Заливайте куда-нибудь в корень сайта, так как в дальнейшем мы рассмотрим примеры, требующие доступ к экзамплам извне. А так примеры мы будем выполнять в консоли.
Информация о профиле.
Для начала получим информацию о профиле:
<?php
ini_set('display_errors', 1);
print '<pre>';
# Include the Dropbox SDK libraries
require_once MODX_BASE_PATH . "dropbox/lib/Dropbox/autoload.php";
use \Dropbox as dbx;
 
$accessToken = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // Здесь ваш Access token
$dbxClient = new dbx\Client($accessToken, "PHP-Example/1.0");
$accountInfo = $dbxClient->getAccountInfo();
print_r($accountInfo);

В ответ вы должны увидеть что-то типа такого:
Array
(
    [referral_link] => https://db.tt/xxxxx
    [display_name] => Nikolay Lanets
    [uid] => 123456789
    [email_verified] => 1
    [team] => 
    [quota_info] => Array
        (
            [datastores] => 0
            [shared] => 24229
            [quota] => 2415919104
            [normal] => 458685614
        )

    [country] => RU
    [email] => info@local.host
)

Если видите, значит все работает :)

Запись файла.
Лучше сразу начинать с записи файла, так как если сразу не понятно четко где реально корневая папка приложения, получая пустой список при попытке чтения директории начинаешь терять время где же что не так.
Чтобы записать файл в дропбокс, надо указать путь до локального файла и указать путь и имя для заливаемого файла (может отличаться). Например:
<?php
ini_set('display_errors', 1);
print '<pre>';
# Include the Dropbox SDK libraries
require_once MODX_BASE_PATH . "dropbox/lib/Dropbox/autoload.php";
use \Dropbox as dbx;
 
$accessToken = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$dbxClient = new dbx\Client($accessToken, "PHP-Example/1.0");

$f = fopen(MODX_BASE_PATH . "robots.txt", "rb");
$fileMetadata = $dbxClient->uploadFile("/robots4.txt", dbx\WriteMode::add(), $f);
fclose($f);
print_r($fileMetadata);

Ответ:
Array
(
    [revision] => 66056
    [bytes] => 25
    [thumb_exists] => 
    [rev] => 10208031c0d71
    [modified] => Wed, 03 Dec 2014 20:37:35 +0000
    [mime_type] => text/plain
    [path] => /robots4.txt
    [is_dir] => 
    [size] => 25 bytes
    [root] => dropbox
    [client_mtime] => Wed, 03 Dec 2014 20:37:35 +0000
    [icon] => page_white_text
)

Запись сделана, файл можно обнаружить в папке дропбокса (если у приложения OwnFolderOnly-права, то файл точно не будет в корневой папке, смотрите в Приложения/[AppName]).

Содержимое папки.
<?php
ini_set('display_errors', 1);
print '<pre>';
# Include the Dropbox SDK libraries
require_once MODX_BASE_PATH . "dropbox/lib/Dropbox/autoload.php";
use \Dropbox as dbx;
 
$accessToken = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$dbxClient = new dbx\Client($accessToken, "PHP-Example/1.0");

$folderMetadata = $dbxClient->getMetadataWithChildren("/");
print_r($folderMetadata);


Ответ:
Array
(
    [hash] => fe7f3531dac71e5df7d1be97d2b8e3e3
    [thumb_exists] => 
    [bytes] => 0
    [path] => /
    [is_dir] => 1
    [icon] => folder
    [root] => dropbox
    [contents] => Array
        (
            [0] => Array
                (
                    [bytes] => 0
                    [rev] => b74031c0d71
                    [revision] => 2932
                    [icon] => folder
                    [path] => /Programs
                    [is_dir] => 1
                    [thumb_exists] => 
                    [root] => dropbox
                    [modified] => Thu, 15 Dec 2011 18:24:59 +0000
                    [size] => 0 bytes
                )

        )

    [size] => 0 bytes
)


Чтение файла.
<?php
ini_set('display_errors', 1);
print '<pre>';
# Include the Dropbox SDK libraries
require_once MODX_BASE_PATH . "dropbox/lib/Dropbox/autoload.php";
use \Dropbox as dbx;
 
$accessToken = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$dbxClient = new dbx\Client($accessToken, "PHP-Example/1.0");

$f = fopen(MODX_BASE_PATH . "robots2.txt", "w+b");
$fileMetadata = $dbxClient->getFile("/robots4.txt", $f);
fclose($f);
print_r($fileMetadata);

Ответ:
Array
(
    [rev] => 10208031c0d71
    [thumb_exists] => 
    [path] => /robots4.txt
    [is_dir] => 
    [client_mtime] => Wed, 03 Dec 2014 20:37:35 +0000
    [icon] => page_white_text
    [bytes] => 25
    [modified] => Wed, 03 Dec 2014 20:37:35 +0000
    [size] => 25 bytes
    [root] => dropbox
    [mime_type] => text/plain
    [revision] => 66056
)


Вот минимальный набор функций для работы с Dropbox. А вот дальше будет гораздо сложнее…

Файловый менеджер Dropbox на вашем сайте.

В примерах, имеющихся в скаченном SDK, есть вот такой файл: examples/web-file-browser.php. Вот его мы и будем мучить… Сразу советую первой строчкой дописать ini_set('display_errors', 1); Далее пойдем по порядку по мере столкновения с проблемами.

Настройка web-file-browser.app
При заходе на страничку (у меня это dropbox/examples/web-file-browser.php), мы получаем следующую ошибку:
Fatal error: Uncaught exception 'Exception' with message 'Unable to load "...../dropbox/examples/web-file-browser.app": File doesn't exist: "...../dropbox/examples/web-file-browser.app"' бла-бла-бла

Суть в том, что приложение пытается получить файл с ключами. Необходимо создать этот файл, в который в JSON-формате записываем app-ключи:
{
  "key": "INSERT_APP_KEY_HERE",
  "secret": "INSERT_SECRET_HERE"
}

Эти ключи вы найдете на страничке управления своим приложением.


Создаем указанный файл и если все ОК, получаем следующую ошибку, уже более симпатичную)) *Сарказм*


Для нас здесь две печали: маленькая и большая. Маленькая — это «Invalid redirect_uri:....». Надо просто добавить указанный УРЛ в настройке приложения. А большая — это 'only localhost URIs can start with «http://»; all others must start with «https://»'. То есть если у вас на сайте не используется протокол https, то вы просто не сможете использовать этот функционал. Так что придется поднимать https.



Настройка HTTPS (SSL).
Если у вас shared-хостинг или типа того, то проще всего обратиться в службу поддержки хостинга, но это совсем не гарантирует вам решения. Почему? Не все так просто там с этими сертификатами, в том числе в плане нескольких сертификатов на один ip. Мы сидим на digitalocean.com на облачных серверах, так что сервер в нашем распоряжении и будем настраивать SSL самостоятельно. Кстати, в этом плане опять-таки сильно радует DO, ибо есть отличные статьи по настройке SSL и на nginx, и на apache. У нас используется связка nginx+apache, так что сертификат мы создаем только один раз и в настройках этих серверов указываем путь до единого сертификата.
Чтобы сгенерить сертификат выполняем:
sudo mkdir /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

Вот когда вы выполняете третью команду, надо будет пошагово ответить на несколько ответов, и в итоге у вас будет карточка типа
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:New York
Locality Name (eg, city) []:New York City
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Bouncy Castles, Inc.
Organizational Unit Name (eg, section) []:Ministry of Water Slides
Common Name (e.g. server FQDN or YOUR name) []:your_domain.com
Email Address []:admin@your_domain.com

Вообще читайте те статьи, там все подробно расписано. Один только момент: как я и говорил, если у вас связка nginx+apache, то при конфигурации апача не надо повторно создавать сертификат, а надо просто в конфигурации прописать путь до созданного уже сертификата. Но с апачем будьте внимательней, там не все так просто (читайте статью). Во-первых, надо активировать мод SSL.
sudo a2enmod ssl
sudo service apache2 restart


Во-вторых, после настройки виртдомена активировать защищенный домен и пестартануть апач.
sudo a2ensite default-ssl
sudo service apache2 restart


Вот здесь (или на первом рестарте) у меня вылезла логичная ошибка — конфликт портов. 443 порт (default SSL) у меня уже был занят nginx-ом, поэтому апачу пришлось указать другой порт (к примеру, 4444). Для этого не только для хоста указывается этот порт, но и в apache2/ports.conf правится
<IfModule mod_ssl.c>
    # If you add NameVirtualHost *:443 here, you will also have to change
    # the VirtualHost statement in /etc/apache2/sites-available/default-ssl
    # to <VirtualHost *:443>
    # Server Name Indication for SSL named virtual hosts is currently not
    # supported by MSIE on Windows XP.
    Listen 4444
</IfModule>

<IfModule mod_gnutls.c>
    Listen 4444
</IfModule>


Если рестарт апача прошел успешно, то ваш сайт должен быть доступен по адресу domain/ или domain:4444/ (я не буду сейчас очень сильно углубляться в это дело, просто вот для заметки написал).

И, если вы захотите обзавестись трастовым сертификатом, да еще и на холяву, советую к прочтению. Я себе оформил, жду :)

Если вы все настроили верно (в том числе в настройках приложения указали верный коллбэк-УРЛ), то вы увидите предложение авторизоваться и присоединиться к вашему приложению.


Вводим свои данные, и…




Конечно тут все без оформления, и буковки русские он не очень понимает, но это же сэмпл, и главное — механизм общий работает, и даже можно скачивать и загружать файлы прям через эту морду, так что в целом задача решена. Ну и возможно в этом будет полезен этот раздел: www.dropbox.com/developers/dropins
12 комментариев
gsnx1
gsnx 04 декабря 2014г в 20:14 #
Классная статья и полезная информация! Спасибо!
Fi1osof1
Fi1osof 04 декабря 2014г в 20:14 #
Пожалуйста!
w
wgame 08 января 2015г в 04:51 #
Классный компонент. Но так и не понял как им пользоваться сделал все по статье, в консоле все выводит как надо при, если из админки создавать папку то создается, если загрузить файл то выдает ошибку если файл загружен через dropbox и вставляю его в tv но картинку не видит. Может что то я не так понял или нужно какой то сниппет создавать?
Fi1osof1
Fi1osof 08 января 2015г в 09:02 #
Я не знаю почему у вас там что не работает. Я просто создал сейчас TV-шку, указал ей источник файлов Dropbox, в документе выбрал желаемую картинку и все ок, на странице формируется путь типа
http://domain/assets/components/dropbox/connector.php?source=26&action=web%2Fview&path=%2F3df.jpg

Если в TV-шке указать тип вывода Изоражение, то формируется корректный тег img и картинка так же выводится.
А
Анатолий Пауль 01 июня 2015г в 13:23 #
Здравствуйте, как отправить файл через рукописный PHP/ Отправляю так:
ini_set('display_errors', 1); 
# Include the Dropbox SDK libraries
require_once "lib/Dropbox/autoload.php";
use \Dropbox as dbx;
$accessToken = "cBr9qwdqwdqwdqwdqwdZeUHc3YS0fYtrYA"; // Здесь ваш Access token
$dbxClient = new dbx\Client($accessToken, "PHP-Example/1.0");
if($_FILES['ya']['name']){
$f = fopen($_FILES['ya']['name'], "rb");
$fbuch=$_FILES['ya']['name'];
$fbuch2="/".$fbuch;
$fileMetadata = $dbxClient->uploadFile("/ChangeLog.txt", dbx\WriteMode::add(), $f);
fclose($f);
print_r($fileMetadata);}
...
<form action="" method="POST" enctype="multipart/form-data">
<input type = "file" name = "ya" />
<input type="submit" value="Загрузить">
</form>
Выдает ошибку:
Fatal error:  Uncaught exception 'InvalidArgumentException' with message ''inStream' has bad type; expecting resource, got boolean' in /var/www/v-2348/data/www/stylus.kz/lib/Dropbox/Checker.php:22
Stack trace:
#0 /var/www/v-2348/data/www/stylus.kz/lib/Dropbox/Checker.php(27): Dropbox\Checker::throwError('inStream', false, 'resource')
#1 /var/www/v-2348/data/www/stylus.kz/lib/Dropbox/Client.php(278): Dropbox\Checker::argResource('inStream', false)
#2 /var/www/v-2348/data/www/stylus.kz/ya.php(16): Dropbox\Client->uploadFile('/Sites.txt', Object(Dropbox\WriteMode), false)
#3 {main}
  thrown in /var/www/v-2348/data/www/stylus.kz/lib/Dropbox/Checker.php on line 22
Fi1osof1
Fi1osof 01 июня 2015г в 13:37 #
1. Не палите свой API access token. Для доступа в ваш дропбокс его достаточно. Более того, генерация нового ключа не спасает, старый не перестает действовать.

2. Проверяйте это:
$f = fopen($_FILES['ya']['name'], "rb");
А вы уверены, что у вас файл был открыт для чтения? Директория для загрузки может находиться там, куда просто так не попадешь (к примеру сайт закрыт по open basedir). Надо юзать move_uploaded_file(). И опять-таки проверять ифом был ли файл перекачен куда надо или нет.
А
Анатолий Пауль 01 июня 2015г в 16:15 #
Решил проблему так:
if(isset($_FILES['upl']) && $_FILES['upl']['error'] == 0){
$fbuch=$_FILES['upl']['tmp_name'];
$f = fopen($fbuch, «r+»);
$fbuch2="/".$_FILES['upl']['name'];
$fileMetadata = $dbxClient->uploadFile($fbuch2, dbx\WriteMode::add(), $f);
fclose($f);
}
А как быть с API access token?)) ПО новому регистрироваться7
Fi1osof1
Fi1osof 01 июня 2015г в 17:57 #
$fbuch=$_FILES['upl']['tmp_name'];
$f = fopen($fbuch, «r+»);
Все равно так не правильно. Правильней переносить файл методом move_uploaded_file(). Или вы рискуете закинуть проект но новый хостинг и там это работать не будет.

А как быть с API access token?)) ПО новому регистрироваться7
Я в комменте подправил его, так что вряд ли широкие массы его заметили, можно особо не париться. Но если принципиально, то можно удалить приложение полностью и новое создать (не аккаунт, а именно приложение).
А
Анатолий Пауль 01 июня 2015г в 19:09 #
Дело в том что я закидываю на DropBox, в инструкции так было показано, подскажите пожалуйста, как можно переделать на move_uploaded_file()?
Fi1osof1
Fi1osof 01 июня 2015г в 19:10 #
Читайте мануалы по move_uploaded_file. Эта функция подробно описана в сети.
А
Анатолий Пауль 02 июня 2015г в 23:24 #
А как получить ссылку на закаченный файл?
Fi1osof1
Fi1osof 03 июня 2015г в 10:10 #
Когда вы закачиваете файл в дропбокс, вы сами указываете путь для него, а значит он вам известен. Вопрос как скачать файл, зная этот путь. Создаете сниппет, вот примерный код:
<?php
// $modx->setLogLevel(1);
$id = 33;    // Здесь ID вашего медиасурса
$source = $modx->getObject('source.modMediaSource', $id);
$source->initialize();
$path = '/path_to_file.ext';    // Путь до вашего файла в дропбоксе
return $source->getContent($path);  // Возвращаем полученный контент.
Авторизуйтесь или зарегистрируйтесь (можно через соцсети ), чтобы оставлять комментарии.