1<?php
2require_once($CFG->libdir.'/portfolio/plugin.php');
3require_once($CFG->libdir.'/filelib.php');
4require_once($CFG->libdir.'/boxlib.php');
5
6class portfolio_plugin_boxnet extends portfolio_plugin_push_base {
7
8    public $boxclient;
9    private $ticket;
10    private $authtoken;
11    private $folders;
12    private $accounttree;
13
14    public static function get_name() {
15        return get_string('pluginname', 'portfolio_boxnet');
16    }
17
18    public function prepare_package() {
19        // don't do anything for this plugin, we want to send all files as they are.
20    }
21
22    public function send_package() {
23        // if we need to create the folder, do it now
24        if ($newfolder = $this->get_export_config('newfolder')) {
25            $created = $this->boxclient->create_folder($newfolder);
26            if (empty($created->id)) {
27                throw new portfolio_plugin_exception('foldercreatefailed', 'portfolio_boxnet');
28            }
29            $this->folders[$created->id] = $created->name;
30            $this->set_export_config(array('folder' => $created->id));
31        }
32        foreach ($this->exporter->get_tempfiles() as $file) {
33            $return = $this->boxclient->upload_file($file, $this->get_export_config('folder'));
34            if (!empty($result->type) && $result->type == 'error') {
35                throw new portfolio_plugin_exception('sendfailed', 'portfolio_boxnet', $result->message);
36            }
37            $createdfile = reset($return->entries);
38            if (!empty($createdfile->id)) {
39                $result = $this->rename_file($createdfile->id, $file->get_filename());
40                // If this fails, the file was sent but not renamed.
41            }
42        }
43    }
44
45    public function get_export_summary() {
46        $allfolders = $this->get_folder_list();
47        if ($newfolder = $this->get_export_config('newfolder')) {
48            $foldername = $newfolder . ' (' . get_string('tobecreated', 'portfolio_boxnet') . ')';
49        } else if ($this->get_export_config('folder')) {
50            $foldername = $allfolders[$this->get_export_config('folder')];
51        } else {
52            $foldername = '/';
53        }
54        return array(
55            get_string('targetfolder', 'portfolio_boxnet') => s($foldername)
56        );
57    }
58
59    public function get_interactive_continue_url() {
60        return 'https://app.box.net/files/0/f/' . $this->get_export_config('folder') . '/';
61    }
62
63    public function expected_time($callertime) {
64        // We're forcing this to be run 'interactively' because the plugin
65        // does not support running in cron.
66        return PORTFOLIO_TIME_LOW;
67    }
68
69    public static function has_admin_config() {
70        return true;
71    }
72
73    public static function get_allowed_config() {
74        return array('clientid', 'clientsecret');
75    }
76
77    public function has_export_config() {
78        return true;
79    }
80
81    public function get_allowed_export_config() {
82        return array('folder', 'newfolder');
83    }
84
85    public function export_config_form(&$mform) {
86        $folders = $this->get_folder_list();
87        $mform->addElement('text', 'plugin_newfolder', get_string('newfolder', 'portfolio_boxnet'));
88        $mform->setType('plugin_newfolder', PARAM_RAW);
89        $folders[0] = '/';
90        ksort($folders);
91        $mform->addElement('select', 'plugin_folder', get_string('existingfolder', 'portfolio_boxnet'), $folders);
92    }
93
94    public function export_config_validation(array $data) {
95        $allfolders = $this->get_folder_list();
96        if (in_array($data['plugin_newfolder'], $allfolders)) {
97            return array('plugin_newfolder' => get_string('folderclash', 'portfolio_boxnet'));
98        }
99    }
100
101    public static function admin_config_form(&$mform) {
102        global $CFG;
103
104        $mform->addElement('text', 'clientid', get_string('clientid', 'portfolio_boxnet'));
105        $mform->addRule('clientid', get_string('required'), 'required', null, 'client');
106        $mform->setType('clientid', PARAM_RAW_TRIMMED);
107
108        $mform->addElement('text', 'clientsecret', get_string('clientsecret', 'portfolio_boxnet'));
109        $mform->addRule('clientsecret', get_string('required'), 'required', null, 'client');
110        $mform->setType('clientsecret', PARAM_RAW_TRIMMED);
111
112        $a = new stdClass();
113        $a->servicesurl = 'https://app.box.com/developers/services';
114        $mform->addElement('static', 'setupinfo', get_string('setupinfo', 'portfolio_boxnet'),
115            get_string('setupinfodetails', 'portfolio_boxnet', $a));
116
117        if (!is_https()) {
118            $mform->addElement('static', 'warninghttps', '', get_string('warninghttps', 'portfolio_boxnet'));
119        }
120    }
121
122    public function steal_control($stage) {
123        if ($stage != PORTFOLIO_STAGE_CONFIG) {
124            return false;
125        }
126        if (empty($this->boxclient)) {
127            $returnurl = new moodle_url('/portfolio/add.php', array('postcontrol' => 1, 'type' => 'boxnet',
128                'sesskey' => sesskey()));
129            $this->boxclient = new boxnet_client($this->get_config('clientid'), $this->get_config('clientsecret'), $returnurl, '');
130        }
131        if ($this->boxclient->is_logged_in()) {
132            return false;
133        }
134        return $this->boxclient->get_login_url();
135    }
136
137    public function post_control($stage, $params) {
138        if ($stage != PORTFOLIO_STAGE_CONFIG) {
139            return;
140        }
141        if (!$this->boxclient->is_logged_in()) {
142            throw new portfolio_plugin_exception('noauthtoken', 'portfolio_boxnet');
143        }
144    }
145
146    /**
147     * Get the folder list.
148     *
149     * This is limited to the folders in the root folder.
150     *
151     * @return array of folders.
152     */
153    protected function get_folder_list() {
154        if (empty($this->folders)) {
155            $folders = array();
156            $result = $this->boxclient->get_folder_items();
157            foreach ($result->entries as $item) {
158                if ($item->type != 'folder') {
159                    continue;
160                }
161                $folders[$item->id] = $item->name;
162                if (!empty($item->shared)) {
163                    $folders[$item->id] .= ' (' . get_string('sharedfolder', 'portfolio_boxnet') . ')';
164                }
165            }
166            $this->folders = $folders;
167        }
168        return $this->folders;
169    }
170
171    /**
172     * Rename a file.
173     *
174     * If the name is already taken, we append the current date to the file
175     * to prevent name conflicts.
176     *
177     * @param int $fileid The file ID.
178     * @param string $newname The new name.
179     * @return bool Whether it succeeded or not.
180     */
181    protected function rename_file($fileid, $newname) {
182        $result = $this->boxclient->rename_file($fileid, $newname);
183        if (!empty($result->type) && $result->type == 'error') {
184            $bits = explode('.', $newname);
185            $suffix = '';
186            if (count($bits) == 1) {
187                $prefix = $newname;
188            } else {
189                $suffix = '.' . array_pop($bits);
190                $prefix = implode('.', $bits);
191            }
192            $newname = $prefix . ' (' . date('Y-m-d H-i-s') . ')' . $suffix;
193            $result = $this->boxclient->rename_file($fileid, $newname);
194            if (empty($result->type) || $result->type != 'error') {
195                return true;
196            } else {
197                // We could not rename the file for some reason...
198                debugging('Error while renaming the file on Box.net', DEBUG_DEVELOPER);
199            }
200        } else {
201            return true;
202        }
203        return false;
204    }
205
206    public function instance_sanity_check() {
207        global $CFG;
208        if (!$this->get_config('clientid') || !$this->get_config('clientsecret')) {
209            return 'missingoauthkeys';
210        } else if (!is_https()) {
211            return 'missinghttps';
212        }
213    }
214
215    public static function allows_multiple_instances() {
216        return false;
217    }
218
219    public function supported_formats() {
220        return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_RICHHTML);
221    }
222
223    /*
224     * for now , boxnet doesn't support this,
225     * because we can't dynamically construct return urls.
226     */
227    public static function allows_multiple_exports() {
228        return false;
229    }
230}
231