2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * This plugin is used to access box.net repository
21 * @package repository_boxnet
22 * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 require_once($CFG->dirroot
. '/repository/lib.php');
26 require_once($CFG->libdir
. '/boxlib.php');
29 * repository_boxnet class implements box.net client
32 * @package repository_boxnet
33 * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org}
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 class repository_boxnet
extends repository
{
38 /** @const MANAGE_URL Manage URL. */
39 const MANAGE_URL
= 'https://app.box.com/files';
41 /** @const SESSION_PREFIX Key used to store information in the session. */
42 const SESSION_PREFIX
= 'repository_boxnet';
44 /** @var string Client ID */
47 /** @var string Client secret */
48 protected $clientsecret;
50 /** @var string Access token */
51 protected $accesstoken;
53 /** @var object Box.net object */
54 protected $boxnetclient;
59 * @param int $repositoryid
60 * @param stdClass $context
61 * @param array $options
63 public function __construct($repositoryid, $context = SYSCONTEXTID
, $options = array()) {
64 parent
::__construct($repositoryid, $context, $options);
66 $clientid = get_config('boxnet', 'clientid');
67 $clientsecret = get_config('boxnet', 'clientsecret');
68 $returnurl = new moodle_url('/repository/repository_callback.php');
69 $returnurl->param('callback', 'yes');
70 $returnurl->param('repo_id', $this->id
);
71 $returnurl->param('sesskey', sesskey());
73 $this->boxnetclient
= new boxnet_client($clientid, $clientsecret, $returnurl, '');
77 * Construct a breadcrumb from a path.
79 * @param string $fullpath Path containing multiple parts separated by slashes.
80 * @return array Array expected to be generated in {@link self::get_listing()}.
82 protected function build_breadcrumb($fullpath) {
83 $breadcrumb = array(array(
84 'name' => get_string('pluginname', 'repository_boxnet'),
88 $crumbs = explode('/', $fullpath);
89 foreach ($crumbs as $crumb) {
91 // That is probably the root crumb, we've already added it.
94 list($unused, $tosplit) = explode(':', $crumb, 2);
95 if (strpos($tosplit, '|') !== false) {
96 list($id, $crumbname) = explode('|', $tosplit, 2);
98 $crumbname = $tosplit;
100 $breadcrumbpath .= '/' . $crumb;
101 $breadcrumb[] = array(
102 'name' => urldecode($crumbname),
103 'path' => $breadcrumbpath
110 * Build a part of the path.
112 * This is used to construct the path that the user is currently browsing.
113 * It must contain a 'type', and a 'value'. Then it can also contain a
114 * 'name' which is very useful to prevent extra queries to get the name only.
116 * See {@link self::split_part} to extra the information from a part.
118 * @param string $type Type of part, typically 'folder' or 'search'.
119 * @param string $value The value of the part, eg. a folder ID or search terms.
120 * @param string $name The name of the part.
121 * @return string type:value or type:value|name
123 protected function build_part($type, $value, $name = '') {
124 $return = $type . ':' . urlencode($value);
126 $return .= '|' . urlencode($name);
132 * Extract information from a part of path.
134 * @param string $part value generated from {@link self::build_parth()}.
135 * @return array containing type, value and name.
137 protected function split_part($part) {
138 list($type, $tosplit) = explode(':', $part);
140 if (strpos($tosplit, '|') !== false) {
141 list($value, $name) = explode('|', $tosplit, 2);
145 return array($type, urldecode($value), urldecode($name));
149 * check if user logged
153 public function check_login() {
154 return $this->boxnetclient
->is_logged_in();
162 public function logout() {
163 if ($this->check_login()) {
164 $this->boxnetclient
->log_out();
166 return $this->print_login();
170 * Search files from box.net
172 * @param string $search_text
175 public function search($search_text, $page = 0) {
176 return $this->get_listing($this->build_part('search', $search_text));
180 * Downloads a repository file and saves to a path.
182 * @param string $ref reference to the file
183 * @param string $filename to save file as
186 public function get_file($ref, $filename = '') {
189 $ref = unserialize(self
::convert_to_valid_reference($ref));
190 $path = $this->prepare_file($filename);
191 if (!empty($ref->downloadurl
)) {
193 $result = $c->download_one($ref->downloadurl
, null, array('filepath' => $filename,
194 'timeout' => $CFG->repositorygetfiletimeout
, 'followlocation' => true));
195 $info = $c->get_info();
196 if ($result !== true ||
!isset($info['http_code']) ||
$info['http_code'] != 200) {
197 throw new moodle_exception('errorwhiledownload', 'repository', '', $result);
200 if (!$this->boxnetclient
->download_file($ref->fileid
, $path)) {
201 throw new moodle_exception('cannotdownload', 'repository');
204 return array('path' => $path);
210 * @param string $path
211 * @param string $page
214 public function get_listing($fullpath = '', $page = ''){
218 $ret['list'] = array();
219 $ret['manage'] = self
::MANAGE_URL
;
220 $ret['dynload'] = true;
222 $crumbs = explode('/', $fullpath);
223 $path = array_pop($crumbs);
228 $pathname = get_string('pluginname', 'repository_boxnet');
230 list($type, $pathid, $pathname) = $this->split_part($path);
233 $ret['path'] = $this->build_breadcrumb($fullpath);
237 if ($type == 'search') {
238 $result = $this->boxnetclient
->search($pathname);
240 $result = $this->boxnetclient
->get_folder_items($pathid);
242 foreach ($result->entries
as $item) {
243 if ($item->type
== 'folder') {
244 $folders[$item->name
. ':' . $item->id
] = array(
245 'title' => $item->name
,
246 'path' => $fullpath . '/' . $this->build_part('folder', $item->id
, $item->name
),
247 'date' => strtotime($item->modified_at
),
248 'thumbnail' => $OUTPUT->image_url(file_folder_icon(64))->out(false),
249 'thumbnail_height' => 64,
250 'thumbnail_width' => 64,
251 'children' => array(),
252 'size' => $item->size
,
255 $files[$item->name
. ':' . $item->id
] = array(
256 'title' => $item->name
,
257 'source' => $this->build_part('file', $item->id
, $item->name
),
258 'size' => $item->size
,
259 'date' => strtotime($item->modified_at
),
260 'thumbnail' => $OUTPUT->image_url(file_extension_icon($item->name
, 64))->out(false),
261 'thumbnail_height' => 64,
262 'thumbnail_width' => 64,
263 'author' => $item->owned_by
->name
,
268 core_collator
::ksort($folders, core_collator
::SORT_NATURAL
);
269 core_collator
::ksort($files, core_collator
::SORT_NATURAL
);
270 $ret['list'] = array_merge($folders, $files);
271 $ret['list'] = array_filter($ret['list'], array($this, 'filter'));
281 public function print_login(){
282 $url = $this->boxnetclient
->get_login_url();
283 if ($this->options
['ajax']) {
285 $popup_btn = new stdClass();
286 $popup_btn->type
= 'popup';
287 $popup_btn->url
= $url->out(false);
288 $ret['login'] = array($popup_btn);
291 echo html_writer
::link($url, get_string('login', 'repository'), array('target' => '_blank'));
296 * Names of the plugin settings
300 public static function get_type_option_names() {
301 return array('clientid', 'clientsecret', 'pluginname');
305 * Catch the request token.
307 public function callback() {
308 $this->boxnetclient
->is_logged_in();
312 * Add Plugin settings input to Moodle form
314 * @param moodleform $mform
315 * @param string $classname
317 public static function type_config_form($mform, $classname = 'repository') {
319 parent
::type_config_form($mform);
321 $clientid = get_config('boxnet', 'clientid');
322 $clientsecret = get_config('boxnet', 'clientsecret');
323 $strrequired = get_string('required');
325 $mform->addElement('text', 'clientid', get_string('clientid', 'repository_boxnet'),
326 array('value' => $clientid, 'size' => '40'));
327 $mform->addRule('clientid', $strrequired, 'required', null, 'client');
328 $mform->setType('clientid', PARAM_RAW_TRIMMED
);
330 $mform->addElement('text', 'clientsecret', get_string('clientsecret', 'repository_boxnet'),
331 array('value' => $clientsecret, 'size' => '40'));
332 $mform->addRule('clientsecret', $strrequired, 'required', null, 'client');
333 $mform->setType('clientsecret', PARAM_RAW_TRIMMED
);
335 $mform->addElement('static', null, '', get_string('information', 'repository_boxnet'));
338 $mform->addElement('static', null, '', get_string('warninghttps', 'repository_boxnet'));
343 * Box.net supports copied and links.
345 * Theoretically this API is ready for references, though it only works for
346 * Box.net Business accounts, but it is not enabled because we are not supporting it.
350 public function supported_returntypes() {
351 return FILE_INTERNAL | FILE_EXTERNAL
;
355 * Convert a reference to the new reference style.
357 * While converting Box.net to APIv2 we introduced a new format for
358 * file references, see {@link self::get_file_reference()}. This function
359 * ensures that the format is always the same regardless of the whether
360 * the reference was from APIv1 or v2.
362 * @param mixed $reference File reference.
363 * @return stdClass Valid file reference.
365 public static function convert_to_valid_reference($reference) {
366 if (strpos($reference, 'http') === 0) {
367 // It is faster to check if the reference is a URL rather than trying to unserialize it.
368 $reference = serialize((object) array('downloadurl' => $reference, 'fileid' => '', 'filename' => '', 'userid' => ''));
374 * Prepare file reference information
376 * @param string $source
377 * @return string file referece
379 public function get_file_reference($source) {
381 list($type, $fileid, $filename) = $this->split_part($source);
382 $reference = new stdClass();
383 $reference->fileid
= $fileid;
384 $reference->filename
= $filename;
385 $reference->userid
= $USER->id
;
386 $reference->downloadurl
= '';
387 if (optional_param('usefilereference', false, PARAM_BOOL
)) {
389 $shareinfo = $this->boxnetclient
->share_file($reference->fileid
);
390 } catch (moodle_exception
$e) {
391 throw new repository_exception('cannotcreatereference', 'repository_boxnet');
393 $reference->downloadurl
= $shareinfo->download_url
;
395 return serialize($reference);
399 * Get a link to the file.
401 * This returns the URL of the web view of the file. To generate this link the
402 * file must be shared.
404 * @param stdClass $reference Reference.
405 * @return string URL.
407 public function get_link($reference) {
408 $reference = unserialize(self
::convert_to_valid_reference($reference));
409 $shareinfo = $this->boxnetclient
->share_file($reference->fileid
, false);
410 return $shareinfo->url
;
414 * Synchronize the references.
416 * @param stored_file $file Stored file.
419 public function sync_reference(stored_file
$file) {
421 if ($file->get_referencelastsync() + DAYSECS
> time()) {
422 // Synchronise not more often than once a day.
426 $reference = unserialize(self
::convert_to_valid_reference($file->get_reference()));
427 $url = $reference->downloadurl
;
428 if (file_extension_in_typegroup($file->get_filename(), 'web_image')) {
429 $path = $this->prepare_file('');
430 $result = $c->download_one($url, null, array('filepath' => $path, 'timeout' => $CFG->repositorysyncimagetimeout
));
431 $info = $c->get_info();
432 if ($result === true && isset($info['http_code']) && $info['http_code'] == 200) {
433 $file->set_synchronised_content_from_file($path);
437 $c->get($url, null, array('timeout' => $CFG->repositorysyncimagetimeout
, 'followlocation' => true, 'nobody' => true));
438 $info = $c->get_info();
439 if (isset($info['http_code']) && $info['http_code'] == 200 &&
440 array_key_exists('download_content_length', $info) &&
441 $info['download_content_length'] >= 0) {
442 $filesize = (int)$info['download_content_length'];
443 $file->set_synchronized(null, $filesize);
446 $file->set_missingsource();
451 * Return human readable reference information
452 * {@link stored_file::get_reference()}
454 * @param string $reference
455 * @param int $filestatus status of the file, 0 - ok, 666 - source missing
458 public function get_reference_details($reference, $filestatus = 0) {
459 // Indicate it's from box.net repository.
460 $reference = unserialize(self
::convert_to_valid_reference($reference));
462 return $this->get_name() . ': ' . $reference->filename
;
464 return get_string('lostsource', 'repository', $reference->filename
);
469 * Return the source information.
471 * @param string $source Not the reference, just the source.
472 * @return string|null
474 public function get_file_source_info($source) {
476 list($type, $fileid, $filename) = $this->split_part($source);
477 return 'Box ('. fullname($USER) . '): ' . $filename;
481 * Repository method to serve the referenced file
483 * @param stored_file $storedfile the file that contains the reference
484 * @param int $lifetime Number of seconds before the file should expire from caches (null means $CFG->filelifetime)
485 * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only
486 * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin
487 * @param array $options additional options affecting the file serving
489 public function send_file($storedfile, $lifetime=null , $filter=0, $forcedownload=false, array $options = null) {
490 $ref = unserialize(self
::convert_to_valid_reference($storedfile->get_reference()));
491 header('Location: ' . $ref->downloadurl
);