1: <?php
2: /**
3: * Gollem external API interface.
4: *
5: * This file defines Gollem's external API interface. Other applications
6: * can interact with Gollem through this API.
7: *
8: * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
9: *
10: * See the enclosed file COPYING for license information (GPL). If you
11: * did not receive this file, see http://www.horde.org/licenses/gpl.
12: *
13: * @author Amith Varghese <amith@xalan.com>
14: * @author Michael Slusarz <slusarz@horde.org>
15: * @author Ben Klang <bklang@alkaloid.net>
16: * @category Horde
17: * @license http://www.horde.org/licenses/gpl GPL
18: * @package Gollem
19: */
20: class Gollem_Api extends Horde_Registry_Api
21: {
22: /**
23: * Browses through the VFS tree.
24: *
25: * Each VFS backend is listed as a directory at the top level. No modify
26: * operations are allowed outside any VFS area.
27: *
28: * @param string $path The level of the tree to browse.
29: * @param array $properties The item properties to return. Defaults to
30: * 'name', 'icon', and 'browseable'.
31: *
32: * @return array The contents of $path.
33: * @throws Gollem_Exception
34: */
35: public function browse($path = '',
36: $properties = array('name', 'icon' ,'browseable'))
37: {
38: $path = Gollem::stripAPIPath($path);
39: $results = array();
40:
41: if ($path == '') {
42: // We are at the root of gollem. Return a set of folders, one for
43: // each backend available.
44: foreach (Gollem_Auth::getBackend() as $backend => $curBackend) {
45: $results['gollem/' . $backend]['name'] = $curBackend['name'];
46: $results['gollem/' . $backend]['browseable'] = true;
47: }
48: } else {
49: $backend_key = $this->_getBackend($path);
50:
51: // Trim off the backend_key (and '/') to get the VFS relative path
52: $fullpath = substr($path, strlen($backend_key) + 1);
53:
54: // Get the VFS-standard $name,$path pair
55: list($name, $path) = Gollem::getVFSPath($fullpath);
56:
57: // Check to see if the request is a file or folder
58: $gollem_vfs = $GLOBALS['injector']->getInstance('Gollem_Vfs');
59: if ($gollem_vfs->isFolder($path, $name)) {
60: // This is a folder request. Return a directory listing.
61: $list = Gollem::listFolder($path . '/' . $name);
62:
63: // Iterate over the directory contents
64: if (is_array($list) && count($list)) {
65: $index = 'gollem/' . $backend_key . '/' . $fullpath;
66: foreach ($list as $key => $val) {
67: $entry = Gollem::pathEncode($index . '/' . $val['name']);
68: $results[$entry]['name'] = $val['name'];
69: $results[$entry]['modified'] = $val['date'];
70: if ($val['type'] == '**dir') {
71: $results[$entry]['browseable'] = true;
72: } else {
73: $results[$entry]['browseable'] = false;
74: $results[$entry]['contentlength'] = $val['size'];
75: }
76: }
77: }
78: } else {
79: // A file has been requested. Return the contents of the file.
80: // Get the file meta-data
81: $list = Gollem::listFolder($path);
82: $i = false;
83: foreach ($list as $key => $file) {
84: if ($file['name'] == $name) {
85: $i = $key;
86: break;
87: }
88: }
89: if ($i === false) {
90: // File not found
91: return $i;
92: }
93:
94: // Send the file
95: $results['name'] = $name;
96: $results['data'] = $gollem_vfs->read($path, $name);
97: $results['contentlength'] = $list[$i]['size'];
98: $results['mtime'] = $list[$i]['date'];
99: }
100: }
101:
102: return $results;
103: }
104:
105: /**
106: * Accepts a file for storage into the VFS.
107: *
108: * @param string $path Path to store file.
109: * @param string $content Contents of file.
110: * @param string $content_type MIME type of file.
111: *
112: * @throws Gollem_Exception
113: */
114: public function put($path, $content, $content_type)
115: {
116: // Clean off the irrelevant portions of the path
117: $path = Gollem::stripAPIPath($path);
118:
119: if ($path == '') {
120: // We are at the root of gollem. Any writes at this level are
121: // disallowed.
122: throw new Gollem_Exception(_("Files must be written inside a VFS backend."));
123: }
124:
125: $backend_key = $this->_getBackend($path);
126:
127: // Trim off the backend_key (and '/') to get the VFS relative path
128: $fullpath = substr($path, strlen($backend_key) + 1);
129:
130: // Get the VFS-standard $name,$path pair
131: list($name, $path) = Gollem::getVFSPath($fullpath);
132:
133: return $GLOBALS['injector']
134: ->getInstance('Gollem_Vfs')
135: ->writeData($path, $name, $content);
136: }
137:
138: /**
139: * Creates a directory ("collection" in WebDAV-speak) within the VFS
140: *
141: * @param string $path Path of directory to create
142: *
143: * @throws Gollem_Exception
144: */
145: public function mkcol($path)
146: {
147: // Clean off the irrelevant portions of the path
148: $path = Gollem::stripAPIPath($path);
149:
150: if ($path == '') {
151: // We are at the root of gollem. Any writes at this level are
152: // disallowed.
153: throw new Gollem_Exception(_('Folders must be created inside a VFS backend.'));
154: }
155:
156: $backend_key = $this->_getBackend($path);
157:
158: // Trim off the backend_key (and '/') to get the VFS relative path
159: $fullpath = substr($path, strlen($backend_key) + 1);
160:
161: // Get the VFS-standard $name,$path pair
162: list($name, $path) = Gollem::getVFSPath($fullpath);
163:
164: return $GLOBALS['injector']
165: ->getInstance('Gollem_Vfs')
166: ->createFolder($path, $name);
167: }
168:
169: /**
170: * Renames a file or directory
171: *
172: * @param string $path Path to source object to be renamed
173: * @param string $dest Path to new name
174: */
175: public function move($path, $dest)
176: {
177: // Clean off the irrelevant portions of the path
178: $path = Gollem::stripAPIPath($path);
179: $dest = Gollem::stripAPIPath($dest);
180:
181: if ($path == '') {
182: // We are at the root of gollem. Any writes at this level are
183: // disallowed.
184: throw new Gollem_Exception(_('Folders must be created inside a VFS backend.'));
185: }
186:
187: // We must be inside one of the VFS areas. Determine which one.
188: // Locate the backend_key in the path
189: if (!strchr($path, '/') ||
190: !strchr($dest, '/')) {
191: // Disallow attempts to rename a share-level directory.
192: throw new Gollem_Exception(_('Renaming of backends is not allowed.'));
193: }
194:
195: $backend_key = $this->_getBackend($path);
196: $dest_backend_key = substr($path, 0, strpos($path, '/'));
197: if ($dest_backend_key != $backend_key) {
198: throw new Gollem_Exception(_('Renaming across backends is not supported.'));
199: }
200:
201: // Trim off the backend_key (and '/') to get the VFS relative path
202: $srcfullpath = substr($path, strlen($backend_key) + 1);
203: $dstfullpath = substr($dest, strlen($backend_key) + 1);
204:
205: // Get the VFS-standard $name,$path pair
206: list($srcname, $srcpath) = Gollem::getVFSPath($srcfullpath);
207: list($dstname, $dstpath) = Gollem::getVFSPath($dstfullpath);
208:
209: $GLOBALS['injector']
210: ->getInstance('Gollem_Vfs')
211: ->rename($srcpath, $srcname, $dstpath, $dstname);
212: }
213:
214: /**
215: * Removes a file or folder from the VFS
216: *
217: * @param string $path Path of file or folder to delete
218: */
219: public function path_delete($path)
220: {
221: // Clean off the irrelevant portions of the path
222: $path = Gollem::stripAPIPath($path);
223:
224: if ($path == '') {
225: // We are at the root of gollem. Any writes at this level are
226: // disallowed.
227: throw new Gollem_Exception(_("The application folder can not be deleted."));
228: }
229:
230: $backend_key = $this->_getBackend($path);
231:
232: // Trim off the backend_key (and '/') to get the VFS relative path
233: $fullpath = substr($path, strlen($backend_key) + 1);
234:
235: // Get the VFS-standard $name,$path pair
236: list($name, $path) = Gollem::getVFSPath($fullpath);
237:
238: // Apparently Gollem::verifyDir() (called by deleteF* next) needs to
239: // see a path with a leading '/'
240: $path = $backends[$backend_key]['root'] . $path;
241:
242: $GLOBALS['injector']
243: ->getInstance('Gollem_Vfs')
244: ->isFolder($path, $name)
245: ? Gollem::deleteFolder($path, $name)
246: : Gollem::deleteFile($path, $name);
247: }
248:
249: /**
250: * Returns a link to the gollem file preview interface
251: *
252: * @param string $dir File absolute path
253: * @param string $file File basename
254: * @param string $backend_key Backend key. Defaults to
255: * Gollem_Auth::getPreferredBackend().
256: *
257: * @return Horde_Url The URL object.
258: */
259: public function getViewLink($dir, $file, $backend_key = '')
260: {
261: if (empty($backend_key)) {
262: $backend_key = Gollem_Auth::getPreferredBackend();
263: }
264: $backend = Gollem_Auth::getBackend($backend_key);
265:
266: return Horde::url('view.php')->add(array(
267: 'actionID' => 'view_file',
268: 'dir' => $dir,
269: 'driver' => $backend['driver'],
270: 'file' => $file,
271: 'type' => substr($file, strrpos($file, '.') + 1)
272: ));
273: }
274:
275: /**
276: * Creates a link to the gollem file selection window.
277: *
278: * The file section window will return a cache ID value which should be
279: * used (along with the selectListResults and returnFromSelectList
280: * functions below) to obtain the data from a list of selected files.
281: *
282: * There MUST be a form field named 'selectlist_selectid' in the calling
283: * form. This field will be populated with the selection ID when the user
284: * completes file selection.
285: *
286: * There MUST be a form parameter named 'actionID' in the calling form.
287: * This form will be populated with the value 'selectlist_process' when
288: * the user completes file selection. The calling form will be submitted
289: * after the window closes (i.e. the calling form must process the
290: * 'selectlist_process' actionID).
291: *
292: * @param string $link_text The text to use in the link.
293: * @param string $link_style The style to use for the link.
294: * @param string $formid The formid of the calling script.
295: * @param boolean $icon Create the link with an icon instead of
296: * text?
297: * @param string $selectid Selection ID.
298: *
299: * @return string The URL string.
300: */
301: public function selectlistLink($link_text, $link_style, $formid,
302: $icon = false, $selectid = '')
303: {
304: $link = Horde::link('#', $link_text, $link_style, '_blank', Horde::popupJs(Horde::url('selectlist.php'), array('params' => array_filter(array('formid' => $formid, 'cacheid' => $selectid)), 'height' => 500, 'width' => 300, 'urlencode' => true)) . 'return false;');
305: if ($icon) {
306: $link_text = Horde::img('gollem.png', $link_text);
307: }
308: return '<script type="text/javascript">document.write(\''
309: . addslashes($link . $link_text) . '<\' + \'/a>\');</script>';
310: }
311:
312: /**
313: * Returns the list of files selected by the user for a given selection ID.
314: *
315: * @param string $selectid The selection ID.
316: *
317: * @param array An array with each file entry stored in its own array,
318: * with the key as the directory name and the value as the
319: * filename.
320: */
321: public function selectlistResults($selectid)
322: {
323: $selectlist = $GLOBALS['session']->get('gollem', 'selectlist/' . $selectid);
324:
325: if (!isset($selectlist['files'])) {
326: return null;
327: }
328:
329: $list = array();
330: foreach ($selectlist['files'] as $val) {
331: list($dir, $filename) = explode('|', $val);
332: $list[] = array($dir => $filename);
333: }
334:
335: return $list;
336: }
337:
338: /**
339: * Returns the data for a given selection ID and index.
340: *
341: * @param string $selectid The selection ID.
342: * @param integer $index The index of the file data to return.
343: *
344: * @return string The file data.
345: */
346: public function returnFromSelectlist($selectid, $index)
347: {
348: $selectlist = $GLOBALS['session']->get('gollem', 'selectlist/' . $selectid);
349:
350: if (!isset($selectlist['files'][$index])) {
351: return null;
352: }
353:
354: list($dir, $filename) = explode('|', $selectlist['files'][$index]);
355: return $GLOBALS['injector']
356: ->getInstance('Gollem_Vfs')
357: ->read($dir, $filename);
358: }
359:
360: /**
361: * Sets the files selected for a given selection ID.
362: *
363: * @param string $selectid The selection ID to use.
364: * @param array $files An array with each file entry stored in its
365: * own array, with the key as the directory name
366: * and the value as the filename.
367: *
368: * @return string The selection ID.
369: */
370: public function setSelectlist($selectid = '', $files = array())
371: {
372: if (empty($selectid)) {
373: $selectid = uniqid(mt_rand());
374: }
375:
376: if (count($files) > 0) {
377: $list = array();
378: foreach ($files as $file) {
379: $list[] = key($file) . '|' . current($file);
380: }
381: $selectlist = $GLOBALS['session']->get('gollem', 'selectlist/' . $selectid, Horde_Session::TYPE_ARRAY);
382: $selectlist['files'] = $list;
383: $GLOBALS['session']->set('gollem', 'selectlist/' . $selectid, $selectlist);
384: }
385:
386: return $selectid;
387: }
388:
389: /**
390: * @throws Gollem_Exception
391: */
392: protected function _getBackend($path)
393: {
394: // A file or directory has been requested.
395: // Locate the backend_key in the path.
396: $backend_key = strchr($path, '/')
397: ? substr($path, 0, strpos($path, '/'))
398: : $path;
399:
400: throw new Gollem_Exception('Not implemented');
401:
402: // Validate and perform permissions checks on the requested backend
403: if (!$GLOBALS['session']->exists('gollem', 'backends/' . $backend_key)) {
404: throw new Gollem_Exception(sprintf(_("Invalid backend requested: %s"), $backend_key));
405: }
406:
407: if (!Gollem_Session::createSession($backend_key)) {
408: throw new Gollem_Exception(_("Unable to create Gollem session"));
409: }
410:
411: if (!Gollem::checkPermissions('backend', Horde_Perms::READ)) {
412: throw new Gollem_Exception(_("Permission denied to this backend."));
413: }
414:
415: return $backend_key;
416: }
417:
418: }
419: