/[drupal]/contributions/modules/fileserver/fileserver.module
ViewVC logotype

Contents of /contributions/modules/fileserver/fileserver.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.4 - (show annotations) (download) (as text)
Mon Sep 8 22:24:12 2008 UTC (14 months, 2 weeks ago) by incanus
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-ALPHA3, HEAD
Changes since 1.3: +67 -9 lines
File MIME type: text/x-php
bringing Drupal CVS up to date with development repository r5280
 * first cut of node revisions for DAV-based operations
  * all types of DAV-based actions affected
   * file rename (i.e., node title change)
   * file delete (i.e., node un-tag)
   * file move (i.e., node tag remove/add)
   * file copy (i.e., node add)
   * file content change (i.e. node attachment edit)
  * obey file node type workflow settings for creating new revisions
  * offload preview generation of revisions to cron, just like new files
  * generate revision log message (with WebDAV label) for all revisions created by DAV
1 <?php
2 // $Id: fileserver.module,v 1.3 2008/09/03 19:26:14 incanus Exp $
3
4 /**
5 * @file
6 * Provides WebDAV access to taxonomies and files.
7 */
8
9 //////////////////////////////////////////////////////////////////////////////
10 // MODULE SETTINGS
11
12 define('FILESERVER_ADMIN_PATH', 'admin/settings/dav/fileserver');
13
14 define('FILESERVER_OG_VOCAB', variable_get('fileserver_og_vocab', FALSE));
15
16 define('FILESERVER_VOCABULARY', 'taxonomy_vocabulary');
17 define('FILESERVER_CATEGORY', 'taxonomy_term');
18 define('FILESERVER_FILE', 'node_file');
19
20 define('FILESERVER_NODE_TYPE', 'file');
21
22 define('FILESERVER_SIG', ' (WebDAV)');
23
24 //////////////////////////////////////////////////////////////////////////////
25 // CORE API HOOKS
26
27 /**
28 * Implementation of hook_help().
29 */
30 function fileserver_help($path, $arg) {
31 switch ($path) {
32 case 'admin/help#fileserver':
33 return '<p>' . t('File taxonomy server provides WebDAV access to a site\'s taxonomies and files. You can add, remove, move, copy, and edit vocabularies, taxonomy terms, and their associated files using an ordinary WebDAV client.') . '</p>';
34 }
35 }
36
37 /**
38 * Implementation of hook_menu().
39 */
40 function fileserver_menu() {
41 return array(
42 FILESERVER_ADMIN_PATH => array(
43 'title' => 'File taxonomy server',
44 'description' => 'Configure File taxonomy server settings.',
45 'access arguments' => array('administer site configuration'),
46 'page callback' => 'drupal_get_form',
47 'page arguments' => array('fileserver_admin_settings_form'),
48 )
49 );
50 }
51
52 /**
53 * Admin settings form, called by hook_menu().
54 */
55 function fileserver_admin_settings_form() {
56 $form = array();
57
58 // Vocabulary settings
59 $form['vocabs'] = array('#type' => 'fieldset', '#title' => t('Vocabulary settings'), '#collapsible' => TRUE, '#collapsed' => TRUE);
60 $options = array_map(create_function('$vocab', 'return $vocab->name;'), taxonomy_get_vocabularies());
61 $form['vocabs']['fileserver_vocabularies'] = array(
62 '#type' => 'select',
63 '#title' => t('Exported vocabularies'),
64 '#default_value' => array_filter(variable_get('fileserver_vocabularies', array()), 'is_string'),
65 '#options' => $options,
66 '#multiple' => TRUE,
67 '#description' => t('Select which vocabularies will be made accessible via DAV as top-level collections (or "root folders"). Access to any other vocabularies will be prohibited.'),
68 );
69
70 // Organic groups integration
71 if (module_exists('og_vocab')) {
72 $form['og'] = array('#type' => 'fieldset', '#title' => t('Organic groups integration'), '#collapsible' => TRUE, '#collapsed' => TRUE);
73 $form['og']['fileserver_og_vocab'] = array(
74 '#type' => 'radios',
75 '#title' => t('Only show vocabularies associated with the user\'s groups'),
76 '#default_value' => FILESERVER_OG_VOCAB,
77 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
78 '#description' => t('Whether to enable access to the group vocabularies of those organic groups the user is subscribed to. Note that the exported vocabularies setting, above, has higher priority than this, meaning that the user is always granted access to any explicitly exported vocabularies.</em>'),
79 );
80 }
81
82 return system_settings_form($form);
83 }
84
85 //////////////////////////////////////////////////////////////////////////////
86 // DAV API HOOKS (NAMESPACE/METADATA)
87
88 /**
89 * Implementation of hook_dav_lookup().
90 */
91 function fileserver_dav_lookup($collection, $name) {
92 list($type, $key) = $collection;
93
94 switch ($type) {
95 case DAV_ROOT_COLLECTION:
96 if ($vocabulary = _fileserver_lookup_vocabulary($name))
97 return array(FILESERVER_VOCABULARY, $vocabulary->vid);
98 break;
99
100 case FILESERVER_VOCABULARY:
101 if ($term = _fileserver_lookup_term($name, $key))
102 return array(FILESERVER_CATEGORY, $term->tid);
103 break;
104
105 case FILESERVER_CATEGORY:
106 //if ($term = _fileserver_lookup_term($name, $parent->vid, $parent->tid))
107 if ($term = _fileserver_lookup_term($name, NULL, $key))
108 return array(FILESERVER_CATEGORY, $term->tid);
109 if ($node = _fileserver_lookup_file($name, $key))
110 return array(FILESERVER_FILE, $node->nid);
111 break;
112
113 case FILESERVER_FILE:
114 break;
115 }
116 }
117
118 /**
119 * Implementation of hook_dav_list().
120 */
121 function fileserver_dav_list($collection) {
122 list($type, $key) = $collection;
123 $resources = array();
124
125 switch ($type) {
126 case DAV_ROOT_COLLECTION:
127 // top level: list vocabularies
128 foreach (taxonomy_get_vocabularies(FILESERVER_NODE_TYPE) as $vocabulary) {
129 if (_fileserver_include_vocabulary($vocabulary->vid)) {
130 $resources[$vocabulary->name] = array(FILESERVER_VOCABULARY, $vocabulary->vid);
131 }
132 }
133 break;
134
135 case FILESERVER_VOCABULARY:
136 // one level deep: list categories under this vocabulary
137 if (_fileserver_include_vocabulary($key)) {
138 foreach (taxonomy_get_children(NULL, $key) as $term) {
139 $resources[$term->name] = array(FILESERVER_CATEGORY, $term->tid);
140 }
141 }
142 break;
143
144 case FILESERVER_CATEGORY:
145 // two or more levels deep: list categories and files under this category
146 foreach (taxonomy_get_children($key) as $term) {
147 $resources[$term->name] = array(FILESERVER_CATEGORY, $term->tid);
148 }
149 $result = taxonomy_select_nodes(array((int)$key), 'or', 0, FALSE);
150 while ($node = db_fetch_object($result)) {
151 $node = node_load($node->nid);
152 if ($node->type == FILESERVER_NODE_TYPE && _fileserver_node_access('view', $node) /*&& !empty($node->file)*/) { // FIXME
153 $resources[$node->title] = array(FILESERVER_FILE, $node->nid);
154 }
155 }
156 break;
157
158 case FILESERVER_FILE:
159 break;
160 }
161
162 return $resources;
163 }
164
165 /**
166 * Implementation of hook_dav_propfind().
167 */
168 function fileserver_dav_propfind($resource) {
169 list($type, $key) = $resource;
170 $props = array();
171
172 switch ($type) {
173 // As DAV collections, we support taxonomy.module vocabularies and terms.
174 case FILESERVER_VOCABULARY:
175 case FILESERVER_CATEGORY:
176 // Note that vocabulary terms don't store any creation/modification
177 // date information in the database, so we have to use the current
178 // time by default. One way to fix this would be to add additional
179 // columns to the core Drupal tables, and watch on hook_taxonomy() in
180 // order to update their values. Nasty, but useful; this could be
181 // provided as a separate add-on since we check whether the term has
182 // 'created' and 'changed' attributes.
183 $term = ($type == FILESERVER_VOCABULARY) ? taxonomy_vocabulary_load($key) : taxonomy_get_term($key);
184 $props['displayname'] = $term->name;
185 $props['creationdate'] = !empty($term->created) ? $term->created : time();
186 $props['getlastmodified'] = !empty($term->changed) ? $term->changed : time();
187 $props['resourcetype'] = 'collection';
188 $props['getcontenttype'] = 'httpd/unix-directory';
189 break;
190
191 // As DAV files, we support file.module nodes.
192 case FILESERVER_FILE:
193 if (($node = node_load($key)) && ($file = $node->file) && _fileserver_node_access('view', $node)) {
194 $props['displayname'] = $node->title; // TODO: give suffix automatically?
195 $props['creationdate'] = $node->created;
196 $props['getlastmodified'] = $node->changed;
197 $props['resourcetype'] = '';
198 $props['getcontenttype'] = $file->type;
199 $props['getcontentlength'] = (int)$file->size;
200 }
201 break;
202 }
203
204 return $props;
205 }
206
207 //////////////////////////////////////////////////////////////////////////////
208 // DAV API HOOKS (VERBS)
209
210 /**
211 * Implementation of hook_dav_get().
212 */
213 function fileserver_dav_get($resource, &$options) {
214 list($type, $key) = $resource;
215
216 switch ($type) {
217 case FILESERVER_FILE:
218 $node = node_load($key);
219 $file = $node->file;
220 $hash = file_get_hash($file);
221 if (_fileserver_node_access('view', $node) && file_bitcache_access($hash, $node->vid)) {
222 return array(
223 'mtime' => $node->changed,
224 'mimetype' => $file->type,
225 'size' => (int)$file->size,
226 'stream' => bitcache_get_stream($hash),
227 );
228 }
229 return FALSE;
230 }
231 }
232
233 /**
234 * Implementation of hook_dav_put().
235 */
236 function fileserver_dav_put($container, $name, &$options, $filepath = NULL) {
237 list($type, $key) = $container;
238
239 switch ($type) {
240 case DAV_ROOT_COLLECTION:
241 return FALSE; // TODO: maybe we could provide some smarts here?
242
243 case FILESERVER_VOCABULARY:
244 return FALSE; // decline file upload to the vocabulary root
245
246 case FILESERVER_CATEGORY:
247 if (empty($filepath)) { // pre-process upload FIXME what does this mean?
248 return TRUE; // accept file upload
249 }
250 else { // post-process upload
251 $handler = $options['new'] ? '_fileserver_create_file' : '_fileserver_update_file';
252 return $handler($key, $name, $options, $filepath);
253 }
254 }
255 }
256
257 /**
258 * Implementation of hook_dav_mkcol().
259 */
260 function fileserver_dav_mkcol($container, $name) {
261 list($type, $key) = $container;
262
263 switch ($type) {
264 case DAV_ROOT_COLLECTION:
265 if (!user_access('administer taxonomy'))
266 return STATUS_403; // Forbidden
267
268 if ($vocabulary = _fileserver_create_vocabulary($name)) {
269 // Immediately add the new vocabulary to the exported list, or else
270 // the user would never see it until an administrator were to
271 // manually adjust the module's settings:
272 _fileserver_export_vocabulary($vocabulary->vid);
273 return array(FILESERVER_VOCABULARY, $vocabulary->vid);
274 }
275 return FALSE;
276
277 case FILESERVER_VOCABULARY:
278 case FILESERVER_CATEGORY:
279 if (!user_access('administer taxonomy'))
280 return STATUS_403; // Forbidden
281
282 $parent = ($type == FILESERVER_VOCABULARY) ? taxonomy_vocabulary_load($key) : taxonomy_get_term($key);
283 if ($term = _fileserver_create_term($name, $parent->vid, @$parent->tid))
284 return array(FILESERVER_CATEGORY, $term->tid);
285 return FALSE;
286 }
287 }
288
289 /**
290 * Implementation of hook_dav_delete().
291 */
292 function fileserver_dav_delete($resource, $container, $move = FALSE) {
293 list($type, $key) = $resource;
294
295 switch ($type) {
296 case FILESERVER_VOCABULARY:
297 // NB: We need to be careful about the deletion of vocabularies...
298 if (!user_access('administer taxonomy'))
299 return STATUS_403; // Forbidden
300
301 return taxonomy_del_vocabulary($key);
302
303 case FILESERVER_CATEGORY:
304 if (!user_access('administer taxonomy'))
305 return STATUS_403; // Forbidden
306
307 return taxonomy_del_term($key);
308
309 case FILESERVER_FILE:
310 $node = node_load($key);
311 if (!_fileserver_node_access('update', $node) && !$move)
312 return STATUS_403; // Forbidden
313
314 list($parent_type, $parent_key) = $container;
315 switch ($parent_type) {
316 case FILESERVER_CATEGORY:
317 $node = node_load($key);
318 if (!$move) {
319 // revision the about-to-be untagged node
320 _fileserver_revision_node($node, 'delete', array(taxonomy_get_term($parent_key)->name));
321 }
322 else {
323 // reload the (uncached) node (already revisioned in fileserver_dav_move())
324 $node = node_load($key, NULL, TRUE);
325 }
326 // only modify the latest revision
327 db_query('DELETE FROM {term_node} WHERE tid = %d AND nid = %d AND vid = %d', $parent_key, $key, $node->vid);
328 // TODO: really delete when reference count reaches zero?
329 // This would require _fileserver_node_access('delete', $node)
330 //node_delete($key);
331 return TRUE;
332 }
333 return NULL;
334 }
335 }
336
337 /**
338 * Implementation of hook_dav_rename().
339 */
340 function fileserver_dav_rename($resource, $source_name, $target_name) {
341 list($type, $key) = $resource;
342
343 switch ($type) {
344 case FILESERVER_VOCABULARY:
345 if (user_access('administer taxonomy')) {
346 $edit = (array)taxonomy_vocabulary_load($key);
347 $edit['name'] = $target_name;
348 db_query("UPDATE {vocabulary} SET name = '%s' WHERE vid = %d", $edit['name'], $edit['vid']);
349 //module_invoke_all('taxonomy', 'update', 'vocabulary', $edit);
350 return TRUE;
351 }
352
353 case FILESERVER_CATEGORY:
354 if (user_access('administer taxonomy')) {
355 $edit = (array)taxonomy_get_term($key);
356 $edit['name'] = $target_name;
357 db_query("UPDATE {term_data} SET name = '%s' WHERE tid = %d", $edit['name'], $edit['tid']);
358 //module_invoke_all('taxonomy', 'update', 'term', $edit);
359 return TRUE;
360 }
361
362 case FILESERVER_FILE:
363 $node = node_load($key);
364 if (_fileserver_node_access('update', $node) &&
365 _fileserver_revision_node($node, 'rename', array($node->title, $target_name))) {
366 db_query("UPDATE {node} SET title = '%s' WHERE nid = %d", $target_name, $node->nid);
367 db_query("UPDATE {node_revisions} SET title = '%s' WHERE vid = %d", $target_name, $node->vid);
368 //node_invoke($node, 'update');
369 //node_invoke_nodeapi($node, 'update');
370 return TRUE;
371 }
372 }
373
374 return FALSE; // catch-all
375 }
376
377 /**
378 * Implementation of hook_dav_move().
379 */
380 function fileserver_dav_move($resource, $source_path, $target_path) {
381 list($type, $key) = $resource;
382
383 switch ($type) {
384 case FILESERVER_VOCABULARY:
385 // Vocabularies can't be moved (only created, renamed and deleted)
386 return FALSE;
387
388 case FILESERVER_CATEGORY:
389 if (user_access('administer taxonomy')) {
390 $edit = (array)taxonomy_get_term($key);
391 $edit['name'] = $name = array_pop($target_path);
392 list($type, $parent) = _dav_resolve($target_path);
393 switch ($type) {
394 // Convert/promote a category into a vocabulary, reparenting all its
395 // sub-categories to the newly-created vocabulary
396 case DAV_ROOT_COLLECTION:
397 if ($vocabulary = _fileserver_create_vocabulary($name)) {
398 _fileserver_reparent_tree($edit['vid'], $edit['tid'], $vocabulary->vid, 0);
399 taxonomy_del_term($edit['tid']);
400 _fileserver_export_vocabulary($vocabulary->vid);
401 return TRUE; //array(FILESERVER_VOCABULARY, $vocabulary->vid);
402 }
403 return FALSE;
404
405 // Move a category to another vocabulary
406 case FILESERVER_VOCABULARY:
407 _fileserver_reparent_tree($edit['vid'], $edit['tid'], $parent, $edit['tid']);
408 $edit['vid'] = $parent;
409 db_query("UPDATE {term_data} SET name = '%s', vid = %d WHERE tid = %d", $edit['name'], $edit['vid'], $edit['tid']);
410 _fileserver_reparent_category($edit['tid'], 0);
411 module_invoke_all('taxonomy', 'update', 'term', $edit);
412 return TRUE;
413
414 // Move a category to another parent category within the same vocabulary
415 case FILESERVER_CATEGORY:
416 // TODO: Check that the vocabulary IDs actually match?
417 // Prevent moving a term inside one of its own subterms, which
418 // would unlink that entire branch due to a circular reference
419 $tids = array_map('array_shift', array_map('get_object_vars', taxonomy_get_parents_all($parent)));
420 if (in_array($edit['tid'], $tids))
421 return FALSE; // Conflict
422 $edit['parent'] = $parent;
423 taxonomy_save_term($edit);
424 return TRUE;
425 }
426 return NULL; // we don't know how to handle the given parent type
427 }
428
429 case FILESERVER_FILE:
430 $source_name = array_pop($source_path);
431 list($source_type, $source) = _dav_resolve($source_path);
432 $target_name = array_pop($target_path);
433 list($target_type, $target) = _dav_resolve($target_path);
434
435 switch ($target_type) {
436 case DAV_ROOT_COLLECTION:
437 case FILESERVER_VOCABULARY:
438 return FALSE; // No can do, sorry.
439
440 case FILESERVER_CATEGORY:
441 // NB: we need to insert the new relation before deleting the
442 // existing association to prevent possible node deletion if its
443 // reference count reaches zero. The good ol' retain/release
444 // dance, Web 2.0 version.
445 $node = node_load($key);
446 if (_fileserver_node_access('update', $node) &&
447 _fileserver_revision_node($node, 'move', array(taxonomy_get_term($source)->name, taxonomy_get_term($target)->name))) {
448 // associate with the latest node revision
449 db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $key, $node->vid, $target);
450 return fileserver_dav_delete($resource, array($source_type, $source), TRUE);
451 }
452 else {
453 return FALSE;
454 }
455 }
456 return NULL; // we don't know how to handle the given parent type
457 }
458
459 return FALSE; // catch-all
460 }
461
462 /**
463 * Implementation of hook_dav_copy().
464 */
465 function fileserver_dav_copy($resource, $source_path, $target_path) {
466 list($type, $key) = $resource;
467
468 switch ($type) {
469 case FILESERVER_VOCABULARY:
470 // Vocabularies can't be copied (only created, renamed and deleted)
471 return FALSE;
472
473 case FILESERVER_CATEGORY:
474 // FIXME: We'll prevent the copying of categories until the exact semantics are sorted out
475 return FALSE;
476
477 $edit = (array)taxonomy_get_term($key);
478 unset($edit['tid']);
479 $edit['name'] = $name = array_pop($target_path);
480 list($type, $parent) = _dav_resolve($target_path);
481 switch ($type) {
482
483 case DAV_ROOT_COLLECTION:
484 // TODO: convert/promote the taxonomy term into a vocabulary
485 return FALSE;
486
487 case FILESERVER_VOCABULARY:
488 // Reparent a taxonomy term to a vocabulary
489 $edit['vid'] = $parent;
490 $edit['parent'] = 0;
491 if (taxonomy_save_term($edit) == SAVED_NEW)
492 return array(FILESERVER_CATEGORY, (int)$edit['tid']);
493 return FALSE;
494
495 case FILESERVER_CATEGORY:
496 // Reparent a taxonomy term to another term
497 $edit['parent'] = $parent;
498 taxonomy_save_term($edit);
499 return TRUE;
500 }
501 return NULL; // we don't know how to handle the given parent type
502
503 case FILESERVER_FILE:
504 $source_name = array_pop($source_path);
505 list($source_type, $source) = _dav_resolve($source_path);
506 $target_name = array_pop($target_path);
507 list($target_type, $target) = _dav_resolve($target_path);
508
509 switch ($target_type) {
510 case DAV_ROOT_COLLECTION:
511 case FILESERVER_VOCABULARY:
512 return FALSE; // No can do
513
514 case FILESERVER_CATEGORY:
515 // Create a "shallow copy" by simply associating the node with the target category
516 $node = node_load($key);
517 if (_fileserver_node_access('update', $node) &&
518 _fileserver_revision_node($node, 'copy', array(taxonomy_get_term($target)->name))) {
519 // associate with the current node revision
520 db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $key, $node->vid, $target);
521 return TRUE;
522 }
523 else {
524 return FALSE;
525 }
526 }
527 return NULL; // we don't know how to handle the given parent type
528 }
529 }
530
531 //////////////////////////////////////////////////////////////////////////////
532 // HELPERS
533
534 function _fileserver_create_vocabulary($name) {
535 $edit = array('name' => $name, 'description' => '', 'help' => '', 'multiple' => 0, 'required' => 0, 'hierarchy' => 0, 'relations' => 0, 'tags' => 0, 'weight' => 0);
536 $edit['nodes'] = array('file' => 'file');
537 return (taxonomy_save_vocabulary($edit) == SAVED_NEW) ?
538 taxonomy_vocabulary_load($edit['vid']) : FALSE;
539 }
540
541 function _fileserver_create_term($name, $vid, $parent = NULL) {
542 $edit = array('vid' => $vid, 'parent' => $parent, 'name' => $name);
543 return (taxonomy_save_term($edit) == SAVED_NEW) ?
544 taxonomy_get_term($edit['tid']) : FALSE;
545 }
546
547 function _fileserver_reparent_category($tid, $parent = 0) {
548 db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $tid);
549 db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $tid, $parent);
550 }
551
552 function _fileserver_reparent_tree($old_vid, $old_parent, $new_vid, $new_parent = 0) {
553 // TODO: call taxonomy hooks?
554
555 $tids = array();
556 foreach (taxonomy_get_tree($old_vid, $old_parent) as $term) {
557 $tids[] = $term->tid;
558 if (empty($term->depth) && $old_parent != $new_parent) {
559 _fileserver_reparent_category($term->tid, $new_parent);
560 }
561 }
562
563 if (!empty($tids)) {
564 $args = array_merge(array($new_vid), $tids);
565 db_query('UPDATE {term_data} SET vid = %d WHERE tid IN (' . db_placeholders($tids) . ')', $args);
566 }
567 return TRUE;
568 }
569
570 function _fileserver_include_vocabulary($vid) {
571 static $vocabs = NULL;
572 if (is_null($vocabs)) {
573 $vocabs = array_filter(variable_get('fileserver_vocabularies', array()), 'is_string');
574 }
575
576 // Filter by administrator-selected vocabularies
577 if (is_array($vocabs) && array_key_exists($vid, $vocabs)) {
578 return TRUE;
579 }
580
581 // Filter by OG vocabularies, if enabled by administrator
582 if (FILESERVER_OG_VOCAB && module_exists('og_vocab')) {
583 global $user;
584 // NOTE: no need to cache this since og_get_subscriptions() memorizes the result.
585 $groups = og_get_subscriptions($user->uid);
586 if (count($groups)) {
587 foreach ($groups as $group) {
588 $nids[] = (int)$group['nid'];
589 }
590 $args = array_merge(array($vid), $nids);
591 $count = db_result(db_query('SELECT COUNT(nid) FROM {og_vocab} WHERE vid = %d AND nid IN (' . db_placeholders($nids) . ')', $args));
592 return !empty($count);
593 }
594 }
595
596 return FALSE;
597 }
598
599 function _fileserver_export_vocabulary($vid) {
600 $vocabs = array_filter(variable_get('fileserver_vocabularies', array()), 'is_string');
601 $vocabs[$vid] = $vid;
602 variable_set('fileserver_vocabularies', $vocabs);
603 }
604
605 function _fileserver_lookup_vocabulary($name) {
606 foreach (taxonomy_get_vocabularies(FILESERVER_NODE_TYPE) as $vocabulary) {
607 if ($vocabulary->name == $name && _fileserver_include_vocabulary($vocabulary->vid)) {
608 return $vocabulary;
609 }
610 }
611 return NULL;
612 }
613
614 function _fileserver_lookup_term($name, $vid, $tid = NULL) {
615 $terms = !$tid ?
616 taxonomy_get_children(NULL, $vid, 'name') :
617 taxonomy_get_children($tid, NULL, 'name');
618 return array_key_exists($name, $terms) ? $terms[$name] : NULL;
619 }
620
621 function _fileserver_lookup_file($name, $tid) {
622 // TODO: this could be significantly optimized yet...
623 $result = taxonomy_select_nodes(array((int)$tid), 'or', 0, FALSE);
624 while ($node = db_fetch_object($result)) {
625 $node = node_load($node->nid);
626 if ($node->type == FILESERVER_NODE_TYPE && _fileserver_node_access('view', $node) && $node->title == $name) {
627 return $node;
628 }
629 }
630 return NULL;
631 }
632
633 function _fileserver_create_file($tid, $name, $options, $filepath) {
634 global $user;
635
636 // Ensure the user has privileges to create nodes of this type. This is
637 // the last chance for access controls to fail, unless some node module
638 // does weird things in hook_validate(), hook_submit() or hook_save().
639 if (!user_access('create file content'))
640 return STATUS_403; // Forbidden
641
642 // manually create an upload object, saving conversions for the cron step
643 $upload = (object)array(
644 'filename' => $name,
645 'filepath' => $filepath,
646 'filemime' => $options['content_type'],
647 'filesize' => filesize($filepath),
648 'noconvert' => TRUE,
649 );
650
651 // according to FileFramework API, make it 'nosave'
652 $node = (object)array(
653 'nosave' => TRUE,
654 );
655
656 // save into Bitcache and RDF
657 if (file_node_save($node, $upload)) {
658 // Organic group vocabularies (og_vocab.module) integration: implicit
659 // audience selection if the currently-selected taxonomy category is
660 // related to an organic group.
661 $og_groups = array();
662 if (module_exists('og_vocab')) {
663 if ($term = taxonomy_get_term($tid)) {
664 if ($og_group = db_result(db_query('SELECT nid FROM {og_vocab} WHERE vid = %d', $term->vid))) {
665 $og_groups[$og_group] = (string)$og_group;
666 }
667 }
668 }
669
670 // actually create the file node
671 $node = file_node_create(array(
672 'file' => $node->file,
673 'taxonomy' => array($tid),
674 'og_groups' => $og_groups,
675 ));
676
677 return (bool)$node;
678 }
679
680 return FALSE; // Something must've gone wrong
681 }
682
683 function _fileserver_update_file($tid, $name, $options, $filepath) {
684 // fetch the node by this name for this term
685 if (($node = _fileserver_lookup_file($name, $tid)) && _fileserver_node_access('update', $node)) {
686 // manually create an upload object for the new version
687 $upload = (object)array(
688 'filename' => $name,
689 'filepath' => $filepath,
690 'filemime' => $options['content_type'],
691 'filesize' => filesize($filepath),
692 'noconvert' => TRUE,
693 );
694
695 // save a new revision if required; success even if files aren't versioned
696 if (_fileserver_revision_node($node, 'update', array($node->file->size, filesize($filepath)))) {
697 // use FileFramework to update the upload on the revision
698 return (bool)file_node_save($node, $upload);
699 }
700 }
701
702 return FALSE; // couldn't lookup the old node
703 }
704
705 function _fileserver_term_has_contents($tid) {
706 return (bool)(taxonomy_term_count_nodes($tid, 'file') || count(taxonomy_get_children($tid)));
707 }
708
709 function _fileserver_node_access($op, $node) {
710 global $user;
711 switch ($op) {
712 case 'view':
713 return node_access('view', $node);
714 case 'update':
715 if ($node->uid == $user->uid && user_access('edit own file content'))
716 return TRUE;
717 else
718 return user_access('edit any file content');
719 case 'delete':
720 if ($node->uid == $user->uid && user_access('delete own file content'))
721 return TRUE;
722 else
723 return user_access('delete any file content');
724 }
725
726 return FALSE; // catch-all
727 }
728
729 function _fileserver_revision_node(&$node, $op = NULL, $args = array()) {
730 // get default file node options
731 $node_options = variable_get('node_options_' . FILESERVER_NODE_TYPE, array('status', 'promote'));
732 // set node flag
733 $node->revision = in_array('revision', $node_options);
734 // save a new revision (uploaded file stays the same; gets updated by later calls to file_node_save())
735 if ($node->revision) {
736 // save old vid for comparison
737 $old_vid = $node->vid;
738 // create log message
739 switch ($op) {
740 case 'delete':
741 $node->log = t('Tag %tag removed', array('%tag' => $args[0])) . FILESERVER_SIG;
742 break;
743 case 'rename':
744 $node->log = t('Renamed from %old to %new', array('%old' => $args[0], '%new' => $args[1])) . FILESERVER_SIG;
745 break;
746 case 'move':
747 $node->log = t('Tag %src removed; tag %dst added', array('%src' => $args[0], '%dst' => $args[1])) . FILESERVER_SIG;
748 break;
749 case 'copy':
750 $node->log = t('Tag %tag added', array('%tag' => $args[0])) . FILESERVER_SIG;
751 break;
752 case 'update':
753 $node->log = t('Contents edited');
754 if ($args[0] != $args[1])
755 $node->log .= t('; size changed from !old to !new', array('!old' => format_size($args[0]), '!new' => format_size($args[1])));
756 $node->log .= FILESERVER_SIG;
757 break;
758 }
759 // save a new revision
760 node_save($node);
761 // unset the revision flag & log message
762 unset($node->revision);
763 unset($node->log);
764 // return success if revisioned
765 return ($node->vid > $old_vid ? TRUE : FALSE);
766 }
767
768 return TRUE; // no need for revision
769 }
770
771 //////////////////////////////////////////////////////////////////////////////

  ViewVC Help
Powered by ViewVC 1.1.2