/[drupal]/contributions/modules/media_mover/media_mover_api.module
ViewVC logotype

Contents of /contributions/modules/media_mover/media_mover_api.module

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


Revision 1.27 - (show annotations) (download) (as text)
Thu Apr 10 00:29:54 2008 UTC (19 months, 2 weeks ago) by bdragon
Branch: MAIN
CVS Tags: HEAD
Changes since 1.26: +15 -1 lines
File MIME type: text/x-php
Drupal 6.2 compatibility.
1 <?php
2 // $Id: media_mover_api.module,v 1.26 2008/02/29 22:10:26 bdragon Exp $
3
4 /**
5 * @file
6 * This file contains all the generic functions for media_moving.
7 * Specific functions for the media_mover hook are located in the
8 * media_mover.inc file.
9 */
10
11 /**
12 * @ TODO actually theme files added to nodes
13 * @ TODO make configuration delete actually delete files.
14 * Some thoughts about this: 1) do a form alter on the node delete
15 * that allows an admin to choose to delete files during a node delete
16 * 2) have options ala the file attachements on node edit
17 * @ TODO Document Document Document
18 * @ TODO cleanup the function naming
19 *
20 *
21 */
22
23 /* ************************************************ */
24 /* Definitions */
25 /* ************************************************ */
26
27 // files directory
28 define('MMA_FILES_DIR', 'media_mover');
29
30 // defines the configuration status
31 define('MMA_CONFIG_STATUS_RUNNING', 'running');
32 define('MMA_CONFIG_STATUS_STOPPED', 'stopped');
33 define('MMA_CONFIG_STATUS_ENABLED', 'enabled');
34 define('MMA_CONFIG_STATUS_DISABLED', 'disabled');
35
36 /* ************************************************ */
37 /* Drupal Hooks */
38 /* ************************************************ */
39
40 /**
41 * Implementation of hook_menu().
42 */
43 function media_mover_api_menu() {
44 // settings page
45 $items['admin/settings/media_mover'] = array(
46 'title' => 'Media Mover',
47 'description' => 'Configure default settings for Media Mover modules.',
48 'page callback' => 'drupal_get_form',
49 'page arguments' => array('media_mover_admin_settings'),
50 'access callback' => 'user_access',
51 'access arguments' => array('administer media_mover'),
52 'file' => 'media_mover_api_admin.inc',
53 'type' => MENU_NORMAL_ITEM, // optional
54 );
55
56 // main page
57 // Old path: 'admin/media_mover'
58 $items['admin/content/media_mover'] = array(
59 'title' => 'Media Mover',
60 'description' => 'Configure the Media Mover system.',
61 'page callback' => 'drupal_get_form',
62 'page arguments' => array('media_mover_api_overview'),
63 'file' => 'media_mover_api_admin.inc',
64 'access callback' => 'user_access',
65 'access arguments' => array('administer media_mover'),
66 'type' => MENU_NORMAL_ITEM,
67 );
68
69 // AHAH callback
70 $items['admin/content/media_mover/js'] = array(
71 'page callback' => 'media_mover_api_config_js',
72 'file' => 'media_mover_api_admin.inc',
73 'access arguments' => array('administer media_mover'),
74 'type' => MENU_CALLBACK,
75 );
76
77 // main page, tab
78 $items['admin/content/media_mover/default'] = array(
79 'title' => 'Overview',
80 'type' => MENU_DEFAULT_LOCAL_TASK,
81 'weight' => -10,
82 );
83
84 // settings as a tab
85 $items['admin/content/media_mover/settings'] = array(
86 'title' => 'Settings',
87 'page callback' => 'drupal_get_form',
88 'page arguments' => array('media_mover_admin_settings'),
89 'access arguments' => array('administer media_mover'),
90 'file' => 'media_mover_api_admin.inc',
91 'type' => MENU_LOCAL_TASK,
92 'weight' => -9,
93 );
94
95 // add a new configuration
96 $items['admin/content/media_mover/add'] = array(
97 'title' => 'Add Config',
98 'page callback' => 'drupal_get_form',
99 'page arguments' => array('media_mover_config_form'),
100 'access arguments' => array('administer media_mover'),
101 'file' => 'media_mover_api_admin.inc',
102 'type' => MENU_LOCAL_TASK,
103 'weight' => -9,
104 );
105
106 // show all files
107 $items['admin/content/media_mover/files'] = array(
108 'title' => 'Files',
109 'page callback' => 'media_mover_api_list_all_files',
110 'page arguments' => array(NULL),
111 'access arguments' => array('administer media_mover'),
112 'type' => MENU_LOCAL_TASK,
113 'weight' => -9,
114 );
115
116 $items['admin/content/media_mover/%media_mover_configuration/files'] = array(
117 'title' => 'Files',
118 'page callback' => 'media_mover_api_list_all_files',
119 'page arguments' => array(3),
120 'access arguments' => array('administer media_mover'),
121 'type' => MENU_LOCAL_TASK,
122 'weight' => -9,
123 );
124
125 // edit an existing configuration
126 $items['admin/content/media_mover/%media_mover_configuration/edit'] = array(
127 'title' => 'Edit Configuration',
128 'page callback' => 'drupal_get_form',
129 'page arguments' => array('media_mover_config_form', 3),
130 'access arguments' => array('administer media_mover'),
131 'file' => 'media_mover_api_admin.inc',
132 'type' => MENU_CALLBACK,
133 );
134
135 // change a configuration status
136 $items['admin/content/media_mover/%media_mover_configuration/status/%'] = array(
137 'title' => 'Update configuration status',
138 'page callback' => 'drupal_get_form',
139 'page arguments' => array('media_mover_status_update_confirm', 3, 5),
140 'access arguments' => array('administer media_mover'),
141 'file' => 'media_mover_api_admin.inc',
142 'type' => MENU_CALLBACK,
143 );
144
145 // delete an existing configuration
146 $items['admin/content/media_mover/%media_mover_configuration/delete'] = array(
147 'title' => 'Confirm Configuration Deletion',
148 'page callback' => 'drupal_get_form',
149 'page arguments' => array('media_mover_config_delete_confirm', 3),
150 'access arguments' => array('administer media_mover'),
151 'file' => 'media_mover_api_admin.inc',
152 'type' => MENU_CALLBACK,
153 );
154
155 // reset an existing configuration
156 $items['admin/content/media_mover/%media_mover_configuration/stop'] = array(
157 'title' => 'Confirm Configuration Stop',
158 'page callback' => 'drupal_get_form',
159 'page arguments' => array('media_mover_api_config_stop_confirm', 3),
160 'access arguments' => array('administer media_mover'),
161 'file' => 'media_mover_api_admin.inc',
162 'type' => MENU_CALLBACK,
163 );
164
165 // reset an existing configuration and remove files
166 $items['admin/content/media_mover/%media_mover_configuration/reset/full'] = array(
167 'title' => 'Confirm Configuration Deletion',
168 'page callback' => 'drupal_get_form',
169 'page arguments' => array('media_mover_config_reset_full_confirm', 3),
170 'access arguments' => array('administer media_mover'),
171 'file' => 'media_mover_api_admin.inc',
172 'type' => MENU_CALLBACK,
173 );
174
175 // Test run ALL configurations
176 $items['admin/content/media_mover/run'] = array(
177 'title' => 'Test',
178 'page callback' => 'media_mover_api_run_configuration',
179 'page arguments' => array(NULL),
180 'access arguments' => array('administer media_mover'),
181 'type' => MENU_LOCAL_TASK,
182 'weight' => -10,
183 );
184
185 // test run configurations
186 $items['admin/content/media_mover/%media_mover_configuration/run'] = array(
187 'title' => 'Running Media Mover configuration',
188 'page callback' => 'media_mover_api_run_configuration',
189 'page arguments' => array(3),
190 'access arguments' => array('administer media_mover'),
191 'type' => MENU_CALLBACK,
192 );
193
194 // edit a file
195 $items['admin/content/media_mover/file/%media_mover_file/edit'] = array(
196 'title' => 'Edit File',
197 'page callback' => 'drupal_get_form',
198 'page arguments' => array('media_mover_edit_file_form', 4),
199 'access arguments' => array('administer media_mover'),
200 'type' => MENU_CALLBACK,
201 );
202
203 // delete a file
204 $items['admin/content/media_mover/file/%media_mover_file/delete'] = array(
205 'title' => 'Delete File',
206 'page callback' => 'drupal_get_form',
207 'page arguments' => array('media_mover_delete_file_form', 4),
208 'access arguments' => array('administer media_mover'),
209 'file' => 'media_mover_api_admin.inc',
210 'type' => MENU_CALLBACK,
211 );
212
213 return $items;
214 }
215
216 /**
217 * Implementation of hook_perm().
218 */
219 function media_mover_api_perm() {
220 return array('administer media_mover');
221 }
222
223 /**
224 * Implementation of hook_cron().
225 * Runs all active configurations
226 */
227 function media_mover_api_cron() {
228 // check to see if we should run on cron
229 if (!variable_get('mma_no_cron_run', false)) {
230 $configurations = _mm_get_active_configurations();
231 foreach ($configurations as $config) {
232 media_mover_api_run_config($config);
233 }
234 }
235 }
236
237 /**
238 * Implementation of hook_nodeapi().
239 */
240 function media_mover_api_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
241 switch ($op) {
242 case 'load':
243 if ($media_mover = _mm_files_db_fetch($node->nid)) {
244 return array('media_mover' => $media_mover);
245 }
246 break;
247
248 case 'view':
249 // Add the attachments list to node body
250 if (!empty($node->media_mover)) {
251 $node->content['media_mover_files'] = array(
252 '#value' => theme('media_mover_files', $node->media_mover),
253 );
254 }
255 break;
256
257 case 'delete':
258 // invoke MM modules here and delete files associated with this node
259 if (variable_get('mma_node_file_delete', false)) {
260 media_mover_api_node_file_delete($node);
261 }
262 break;
263 }
264 }
265
266 /**
267 * Implementation of hook_form_alter().
268 */
269 function media_mover_api_form_alter(&$form, $form_state, $form_id) {
270 // check and see if this is a node delete form
271 if ($form_id == 'node_delete_confirm') {
272 // load the node
273 $node = node_load($form['nid']['#value']);
274
275 // check if there is any media mover content
276 if ($node->media_mover) {
277
278 $files = array();
279 foreach ($node->media_mover as $cid => $mmfiles) {
280 $files[] = basename($mmfiles[0]['complete_file']);
281 }
282 $form['media_mover'] = array(
283 '#type' => 'markup',
284 '#title' => t('Media Mover files'),
285 '#value' => t('If you delete this node, you will also delete these files: %files', array('%files' => implode(', ', $files))),
286 );
287 }
288 }
289
290 // Display MM files on node edit page.
291 if (isset($form['type']) && variable_get('mma_node_edit_file_show', FALSE)) {
292 if ($html = theme('media_mover_api_files_node_edit', $form['#node'])) {
293 $form['media_mover'] = array(
294 '#type' => 'fieldset',
295 '#title' => t('Media Mover files'),
296 );
297 $form['media_mover']['media_mover_files'] = array(
298 '#type' => 'markup',
299 '#value' => $html,
300 );
301 }
302 }
303 }
304
305 /* ************************************************ */
306 /* Media Mover Hooks */
307 /* ************************************************ */
308
309 /**
310 * Default implementation of hook_media_mover().
311 * @param $op is the operator to return
312 * @param $action_id is which action is being called
313 * @param $configuration is the specific configuration saved for the action for this configuration
314 * @param $item The media mover item being processed.
315 * @param $running_config is the full configuration data currently running
316 */
317 function media_mover_api_media_mover($op, $action = null, $configuration = null, &$item = array(), $running_config = null) {
318 switch ($op) {
319 case 'name':
320 return "Media Mover defaults";
321 break;
322
323 // defines the actions that this module does
324 case 'actions':
325 return array(
326 'harvest' => array(1 => t('Bypass this operation')),
327 'process' => array(1 => t('Bypass this operation')),
328 'storage' => array(1 => t('Bypass this operation')),
329 'complete' => array(1 => t('Bypass this operation')),
330 );
331 break;
332
333 // functions called on process op
334 case 'process':
335 return media_mover_api_no_action('process', 'harvest', $item);
336 break;
337 case 'storage':
338 return media_mover_api_no_action('storage', 'process', $item);
339 break;
340 case 'complete':
341 return media_mover_api_no_action('complete', 'storage', $item);
342 break;
343
344 // admin options for the API setup
345 case 'admin':
346 return media_mover_api_admin_form();
347 break;
348 }
349 }
350
351 /**
352 * Fallback action.
353 * Uses file from previous step.
354 * @param $verb Media mover verb.
355 * @param $previous_verb Previous media mover verb.
356 * @param $item Media mover item.
357 *
358 */
359 function media_mover_api_no_action($verb, $previous_verb, &$item) {
360 $item[$verb]['file'] = $item[$previous_verb]['file'];
361 $item[$verb]['module'] = 'media_mover_api';
362 $item[$verb]['action'] = 1;
363 }
364
365 /* ************************************************ */
366 /* Media Mover internal functions */
367 /* ************************************************ */
368
369 /**
370 * defines the verb types for all media mover actions
371 * probably should abstract these a bit.
372 */
373 function _mm_verbs() {
374 return array('harvest', 'process', 'storage', 'complete');
375 }
376
377 /**
378 * Run a media_mover hook.
379 * We can't use module_invoke because $item is by reference.
380 */
381 function media_mover_invoke($module, $verb, $action, $config, &$item = NULL) {
382 if (function_exists($function = $module .'_media_mover')) {
383 return $function($verb, $action, $config->$verb->configuration, $item, $config);
384 }
385 return FALSE;
386 }
387
388 /**
389 * lets admin run all configurations, displays debug info
390 * @ TODO would be nice to provide a set of comprehensive tools here
391 * @param $cid is a configuration id
392 */
393 function media_mover_api_run_configuration($cid = null) {
394 if (!$cid) {
395 $configurations = _mm_get_active_configurations();
396 $output = t('Running all configurations') .'<br /><hr><br />';
397 foreach ($configurations as $config) {
398 $output .= media_mover_api_run_config($config, true);
399 }
400 $output .= '<hr><br />'. t('All configurations run. Check your !logs for additional data.', array('!logs' => l(t('logs'), 'admin/reports/dblog')));
401 }
402 else {
403 if (is_numeric($cid)) {
404 $config = media_mover_configuration_load($cid);
405 }
406 else {
407 $config = $cid;
408 }
409
410 if ($config->status == MMA_CONFIG_STATUS_RUNNING) {
411 $output = t('This configuration is already running. Please wait for it to finish.');
412 }
413 else {
414 $output = t('Running !config configuration', array('!config' => $config->name)) .'<br /><hr><br />';
415 $output .= media_mover_api_run_config($config, true);
416 $output .= '<hr><br />'. t('Configuration run. Check your !logs for additional data.', array('!logs' => l(t('logs'), 'admin/reports/dblog')));
417 }
418 }
419 return $output;
420 }
421
422 /**
423 * runs one complete configuration command
424 * this is the main controller of the media mover functions
425 */
426 function media_mover_api_run_config($config, $debug = true) {
427 $output = '';
428 // check to see if this config is running
429 if (!_mm_configuration_is_running($config->cid)) {
430 watchdog('media_mover', 'Running config <a href="@config_link">@name.</a>', array('@config_link' => url('admin/content/media_mover/'. $config->cid), '@name' => $config->name), WATCHDOG_DEBUG);
431
432 // check directories
433 media_mover_api_check_config_dirs($config);
434
435 if (!isset($config->harvest) || empty($config->harvest)) {
436 // No point in continuing...
437 return;
438 }
439
440 // Run harvest routine.
441 $items = media_mover_invoke($config->harvest->module, 'harvest', $config->harvest->action, $config);
442 // Assign module, action.
443 foreach ($items as $k => $item) {
444 $item['harvest']['module'] = $config->harvest->module;
445 $item['harvest']['action'] = $config->harvest->action;
446 $item['process']['module'] = $config->process->module;
447 $item['process']['action'] = $config->process->action;
448 $item['process']['file'] = '';
449 $item['storage']['module'] = $config->storage->module;
450 $item['storage']['action'] = $config->storage->action;
451 $item['storage']['file'] = '';
452 $item['complete']['module'] = $config->complete->module;
453 $item['complete']['action'] = $config->complete->action;
454 $item['complete']['file'] = '';
455 $item['url'] = '';
456 $item['mime'] = '';
457 $item['harvest']['local'] = file_exists($item['harvest']['file']);
458 if (!isset($item['status'])) {
459 $item['status'] = TRUE;
460 }
461 // Save to database.
462 _mm_file_db_add($item, $config, 'harvest', $config->harvest->module, $config->harvest->action, $config->harvest->configuration);
463 if ($item['status'] === 0) {
464 $output .= t('Error in harvesting') .'<br />'; // @@@
465 }
466 $items[$k] = $item;
467 }
468
469 // Run process action.
470 foreach ($items as $k => $item) {
471 if ($item['status'] === 0) {
472 continue;
473 }
474
475 media_mover_invoke($config->process->module, 'process', $config->process->action, $config, $item);
476 $item['process']['local'] = file_exists($item['process']['file']);
477 _mm_file_db_update($item);
478
479 if ($item['status'] === 0 ) {
480 $output .= t('Error in processing') .'<br />'; // @@@
481 }
482 $items[$k] = $item;
483 }
484
485 // Run storage action.
486 foreach ($items as $k => $item) {
487 if ($item['status'] === 0) {
488 continue;
489 }
490
491 media_mover_invoke($config->storage->module, 'storage', $config->storage->action, $config, $item);
492 $item['storage']['local'] = file_exists($item['storage']['file']);
493
494 _mm_file_db_update($item);
495
496 if ($item['status'] === 0 ) {
497 $output .= t('Error in storing') .'<br />'; // @@@
498 }
499 $items[$k] = $item;
500 }
501
502 // Run complete action.
503 foreach ($items as $item) {
504 if ($item['status'] === 0) {
505 continue;
506 }
507
508 media_mover_invoke($config->complete->module, 'complete', $config->complete->action, $config, $item);
509 $item['complete']['local'] = file_exists($item['complete']['file']);
510 _mm_file_db_update($item);
511
512 if ($item['status'] === 0 ) {
513 $output .= t('Error in complete') .'<br />'; // @@@
514 }
515 }
516
517 // ends the configuration run
518 _mm_configuration_stop_run($config->cid);
519 }
520
521 // if in debug mode.....
522 if ($debug) {
523 return $output;
524 }
525
526 }
527
528 /* ************************************************ */
529 /* Media Mover directory and file functions */
530 /* ************************************************ */
531
532 /**
533 * check each of the configuration modules directories
534 * @param $config is a mm configuration
535 */
536 function media_mover_api_check_config_dirs($config) {
537 foreach (_mm_verbs() as $verb) {
538 if (isset($config->{$verb})) {
539 $dirs = module_invoke($config->{$verb}->module, 'media_mover', 'directories');
540 if ($dirs) {
541 media_mover_api_check_dirs($dirs);
542 }
543 }
544 }
545 }
546
547 /**
548 * checks to make sure a directories exits, creates them
549 * @param $dirs is an array of directory names, names can be single directories or nested
550 */
551 function media_mover_api_check_dirs($dirs) {
552 $parent = file_directory_path() .'/'. MMA_FILES_DIR .'/';
553 if ($dirs) {
554 foreach ($dirs as $dir) {
555 $path = $parent . $dir;
556 // check and see if we can create the directory
557 if (!file_check_directory($path, FILE_CREATE_DIRECTORY)) {
558 // call the whole path recurisvely
559 media_mover_api_build_dirs($path);
560 }
561 }
562 }
563 }
564
565 /**
566 * this is a recursive function that builds out the complete
567 * path being called in
568 * returns true when it finally creates a directory
569 * @param $path is a path to create directories on
570 * @return true when it completes
571 */
572 function media_mover_api_build_dirs($path) {
573 if (!file_check_directory($path, FILE_CREATE_DIRECTORY)) {
574 // couldn't create the directory, try it higher up
575 // the path
576 if (media_mover_api_build_dirs(dirname($path))) {
577 file_check_directory($path, FILE_CREATE_DIRECTORY);
578 }
579 }
580 return true;
581 }
582
583 /**
584 * this is a place holder function so that directory paths
585 * can be more flexible in the future without needing to
586 * rewrite indidivudual modules. Relies on drupal's files
587 * path for now. Note, this should produce a path that is
588 * executable to the file, relative to drupal. We do not
589 * need to store this path.
590 *
591 * @param $filepath is a file path to check against to return
592 * a good path
593 *
594 */
595 function media_mover_api_dir_path($filepath = false) {
596 $path = file_directory_path() .'/'. MMA_FILES_DIR .'/';
597 if ($filepath) {
598 $path = $path . $filepath;
599 // make sure the directories exist
600 media_mover_api_check_dirs(array($filepath));
601 }
602 return $path;
603 }
604
605 /**
606 * this is a handler to check file permissions. It lets an admin set
607 * global file perms and then the modules don't have to worry about it
608 */
609 function media_mover_api_set_file_perm($filepath) {
610 if (variable_get('mma_file_perm', true)) {
611 if ($oct = variable_get('mma_file_mask', null)) {
612 $perms = octdec($oct);
613 }
614 else {
615 $perms = 0644;
616 }
617 // @@@ Investigate this further!
618 // @chmod($filepath, $perms);
619 @chmod($_SERVER['DOCUMENT_ROOT'] .'/'. $filepath, $perms);
620 }
621 }
622
623 /* ************************************************ */
624 /* Media Mover internal db functions */
625 /* ************************************************ */
626
627 /**
628 * checks to see if a configuration is running, prevents
629 * overlapping condition
630 *
631 * @cid is a configuration id
632 * @return false if cofnig is not running, true if so
633 */
634 function _mm_configuration_is_running($cid) {
635 $output = '';
636 // lock the tables to prevent over run
637 db_lock_table('media_mover_config_list');
638
639 $config = db_fetch_array(db_query('SELECT * FROM {media_mover_config_list} WHERE cid = %d', $cid));
640
641 if ($config['status'] == MMA_CONFIG_STATUS_RUNNING) {
642 $output = true;
643 }
644 else {
645 // set status to running, and set the start run time.
646 db_query('UPDATE {media_mover_config_list} SET status = "%s", last_start_time = %d, start_time = %d WHERE cid = %d', MMA_CONFIG_STATUS_RUNNING, $config['start_time'], time(), $cid );
647 }
648 db_unlock_tables('media_mover_config_list');
649
650 if ($output) {
651 // Figure out how long ago the job started.
652 $time = intval( (time() - $config['start_time']) / 60);
653
654 // Log to watchdog.
655 watchdog(
656 'media_mover',
657 'Media Mover detected a MM job (@name) that has been running for @time minute(s). You may want to consider decreasing your cron.php frequency or increase your PHP timeout.',
658 array('@name' => $config['name'], '@time' => $time)
659 );
660
661 // Should we alert an admin?
662 if (variable_get('mma_cron_notify', false) && ($time >= variable_get('mma_cron_notify_time', 10))) {
663 drupal_mail('media_mover_api', 'job_stuck', variable_get('mma_cron_notify_email', NULL), language_default(), array('job' => $config['name'], 'time' => $time));
664 }
665 }
666 return $output;
667 }
668
669 /**
670 * Implementation of hook_mail().
671 */
672 function media_mover_api_mail($key, &$message, $params) {
673 $language = $message['language'];
674 $variables = array(
675 '!site' => variable_get('site_name', 'Drupal'),
676 '!job' => $params['job'],
677 '!time' => format_plural($params['time'], '1 minute', '@count minutes', array(), $language),
678 );
679 if ($key == 'job_stuck') {
680 $message['subject'] = t('[!sitename] Media Mover Job Stuck', $variables, $language->language);
681 $message['body'] = t(
682 "Media Mover deteced a MM job (!job) that has been running for !time.\n\nYou may want to consider decreasing your cron.php frequency or increase your PHP timeout.",
683 $variables,
684 $language->language
685 );
686 }
687 }
688
689 /**
690 * ends a configuration run, sets the status flag to off
691 * @cid configuration id
692 *
693 */
694 function _mm_configuration_stop_run($cid) {
695 // get the config
696 $config = db_fetch_array(db_query('SELECT * FROM {media_mover_config_list} WHERE cid = %d', $cid));
697
698 // we know that if the last_start_time == 0 then this was the first time
699 // through. This is a special case. Updated the last_start_time with start_time
700 if ($config['last_start_time'] == 0 ) {
701 db_query('UPDATE {media_mover_config_list} SET status = "stopped", last_start_time = start_time WHERE cid = %d', $cid);
702 }
703 else {
704 db_query('UPDATE {media_mover_config_list} SET status = "stopped" WHERE cid = %d', $cid);
705 }
706 }
707
708 /**
709 * adds a newly created media_mover file to the media_mover db table
710 *
711 * @param $file is a file array with all the data harvested
712 * @param $config is a MM config
713 * @param $verb is what MM processs is calling this
714 * @param $module is the name of the module that did the harvest
715 * @param $action is the action that did the harvest
716 * @param $action_config is the specific configuration for the action that was called
717 * @return $mmfid for this files ID
718 */
719 function _mm_file_db_add(&$file, $config, $verb = 'harvest', $module = null, $action = null, $action_config) {
720
721 if (!isset($file['fid']) || !$file['fid']) {
722 // This file came from somewhere external.
723 // We need to add to the files table ourselves.
724 $f = new stdClass();
725 $f->uid = isset($file['uid']) ? $file['uid'] : variable_get('mma_default_uid', 0);
726 $f->filename = $file['filename'];
727 $f->filepath = $file['filepath'];
728 $f->filemime = $file['filemime'];
729 $f->filesize = $file['filesize'];
730 $f->status = FILE_STATUS_PERMANENT;
731 $f->timestamp = time();
732 drupal_write_record('files', $f);
733 $file['fid'] = $f->fid;
734 }
735
736 $mmf = new stdClass();
737 $mmf->nid = $file['nid']; // @@@ THIS NO LONGER EXISTS! $file['uid'] ?
738 $mmf->fid = $file['fid'];
739 $mmf->cid = $config->cid;
740 $mmf->harvest_file = $file['harvest']['file'];
741 $mmf->harvest_module = $file['harvest']['module'];
742 $mmf->harvest_action = $file['harvest']['action'];
743 $mmf->status = (isset($file['status']) && $file['status']) ? $file['status'] : FILE_STATUS_PERMANENT;
744 $mmf->date = time();
745 $mmf->data = serialize($file);
746
747 drupal_write_record('media_mover_files', $mmf);
748 $file['mmfid'] = $mmf->mmfid;
749 }
750
751 /**
752 * fetches files from files db
753 * invokes media_mover fetch hook to get additional data that media mover
754 * modules may have saved in seperate tables regarding a node
755 *
756 * @param nid node id value
757 * @return files array
758 */
759 function _mm_files_db_fetch($nid) {
760 $return_files = array();
761 $results = db_query("SELECT * FROM {media_mover_files} WHERE nid = %d ORDER BY cid", $nid);
762 while ($file = db_fetch_array($results)) {
763 foreach (_mm_verbs() as $verb) {
764 // @@@ Byref?
765 $merge = module_invoke($file["{$verb}_module"], 'media_mover', 'fetch', $file["{$verb}_action"], $file );
766 if ($merge) {
767 array_merge($file, $merge);
768 }
769 }
770 $return_files[$file['cid']][] = $file;
771 }
772 return $return_files;
773 }
774
775 /**
776 * updates the database with new data from a file that has been acted upon
777 * @param $file is a complete file array
778 */
779 function _mm_file_db_update(&$item) {
780 db_query("UPDATE {media_mover_files} SET ".
781 "nid = %d, ".
782 "harvest_module = '%s', harvest_action = '%s', harvest_file = '%s', ".
783 "process_module = '%s', process_action = '%s', process_file = '%s', ".
784 "storage_module = '%s', storage_action = '%s', storage_file = '%s', ".
785 "complete_module = '%s', complete_action = '%s', complete_file = '%s', ".
786 "url = '%s' ".
787 "WHERE mmfid = %d ",
788 $item['nid'],
789 $item['harvest']['module'], $item['harvest']['action'], $item['harvest']['file'],
790 $item['process']['module'], $item['process']['action'], $item['process']['file'],
791 $item['storage']['module'], $item['storage']['action'], $item['storage']['file'],
792 $item['complete']['module'], $item['complete']['action'], $item['complete']['file'],
793 $item['url'], $item['mmfid']
794 );
795 foreach (_mm_verbs() as $verb) {
796 // @@@ Byref?
797 module_invoke($item[$verb]['module'], 'media_mover', 'update', $item[$verb]['action'], null, $item);
798 }
799 }
800
801 /**
802 * fetches all files associated with a specific $cid
803 *
804 * @param $cid is a configuration id
805 * @return is an array of file arrays
806 */
807 function media_mover_api_fetch_files($cid) {
808 $files = array();
809 $results = db_query('SELECT * FROM {media_mover_files} WHERE cid = %d', $cid);
810 while ($file = db_fetch_array($results)) {
811 // unserailize the stored data
812 $file['data'] = unserialize($file['data']);
813 $files[] = $file;
814 }
815 return $files;
816 }
817
818 /**
819 * run a delete function for harvest and storage modules to delete
820 * files they've handled
821 *
822 * @param $config A media mover configuration.
823 */
824 function media_mover_api_config_delete_files($config) {
825 if (is_numeric($config)) {
826 $config = media_mover_configuration_load($config);
827 }
828
829 // get all the files
830 $files = media_mover_api_fetch_files($config->cid);
831 foreach ($files as $file) {
832 // run the delete on each of the files for each module
833 foreach (_mm_verbs() as $verb) {
834 // @@@ Byref?
835 module_invoke($config->{$verb}->module, 'media_mover', 'delete', $config->{$verb}->action, $config->{$verb}->configuration, $file, $config);
836 }
837 }
838 // now we remove all db references to these files
839 db_query('DELETE FROM {media_mover_files} WHERE cid = %d', $config->cid);
840 }
841
842 /**
843 * Get the active configurations from the db.
844 *
845 * @return array of configuration objects
846 */
847 function _mm_get_active_configurations() {
848 $configurations = array();
849 // get all active configuration cids.
850 $result = db_query("SELECT cid FROM {media_mover_config_list} WHERE status <> '%s'", 'disabled');
851 while ($row = db_fetch_object($result)) {
852 $configurations[] = media_mover_configuration_load($row->cid);
853 }
854 return $configurations;
855 }
856
857 /**
858 * load() function for a media_mover configuration.
859 * Used in menu callbacks, etc.
860 */
861 function media_mover_configuration_load($cid) {
862 if (!is_numeric($cid)) {
863 return FALSE; // Nonsensical: For menu 404s.
864 }
865 $config = db_fetch_object(db_query('SELECT * FROM {media_mover_config_list} WHERE cid = %d', $cid));
866 if (!$config) {
867 return FALSE; // Nonexistant: For menu 404s.
868 }
869
870 $result = db_query('SELECT * FROM {media_mover_configurations} WHERE cid = %d', $config->cid);
871 while ($row = db_fetch_object($result)) {
872 $vconf = $row;
873 $vconf->configuration = unserialize($vconf->configuration);
874 $vconf->configuration['cid'] = $config->cid; // ???
875 $config->{$vconf->verb} = $vconf;
876 }
877 return $config;
878 }
879
880 /**
881 * load() function for a media_mover configuration.
882 * Used in menu callbacks, etc.
883 */
884 function media_mover_file_load($mmfid) {
885 if (!is_numeric($mmfid)) {
886 return FALSE; // For menu 404s.
887 }
888
889 $item = db_fetch_object(db_query('SELECT * FROM {media_mover_files} WHERE mmfid = %d', $mmfid));
890 $item['data'] = unserialize($item['data']);
891 foreach (_mm_verbs() as $verb) {
892 // @@@ Byref?
893 $merge = module_invoke($item["{$verb}_module"], 'media_mover', 'fetch', $item["{$verb}_action"], $item);
894 if ($merge) {
895 array_merge($item, $merge);
896 }
897 }
898 return $item;
899 }
900
901 /* ****************************************** */
902 /* media mover ADMIN PAGES */
903 /* ****************************************** */
904
905 /**
906 * Remap parents to fit into our configuration form nicely.
907 * Based on form_builder().
908 *
909 * OK, so what's happening here is we are attempting to stick multiple pieces
910 * of forms together.
911 * We want $form_state['values'] to keep *ALL* the keys of $form under
912 * $form_state['values'][$verb]['conf'].
913 * We can build the #parents by hand to make sure that $verb][conf is always in
914 * front.
915 *
916 * @@@ This doesn't handle assigning form errors to the correct element.
917 */
918 function _media_mover_remap_action_form($form, $verb) {
919 // Recurse through all child elements.
920 foreach (element_children($form) as $key) {
921 // Don't squash an existing tree value.
922 if (!isset($form[$key]['#tree'])) {
923 $form[$key]['#tree'] = $form['#tree'];
924 }
925
926 // Don't squash existing parents value.
927 if (!isset($form[$key]['#parents'])) {
928 // Check to see if a tree of child elements is present. If so,
929 // continue down the tree if required.
930 $form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ? array_merge($form['#parents'], array($key)) : array($verb, 'conf', $key);
931 $array_parents = isset($form['#array_parents']) ? $form['#array_parents'] : array($verb, 'conf');
932 $array_parents[] = $key;
933 $form[$key]['#array_parents'] = $array_parents;
934 }
935 else {
936 // But tack ours on first!
937 array_unshift($form[$key]['#parents'], 'conf');
938 array_unshift($form[$key]['#parents'], $verb);
939 }
940
941 $form[$key] = _media_mover_remap_action_form($form[$key], $verb);
942 }
943 return $form;
944 }
945
946 /* ****************************************** */
947 /* media mover ADMIN PAGES individual files */
948 /* ****************************************** */
949
950 /**
951 * List all the files in the files table
952 */
953 function media_mover_api_list_all_files($config = NULL) {
954 if ($config) {
955 $results = pager_query('SELECT * FROM {media_mover_files} WHERE cid = %d ORDER BY date DESC', 10, 0, NULL, $config->cid);
956 drupal_set_title(t('Files for: @config', array('@config' => $config->name)));
957 }
958 else {
959 $results = pager_query('SELECT f.*, c.name AS config_name FROM {media_mover_files} f LEFT JOIN {media_mover_config_list} c ON f.cid = c.cid ORDER BY f.date DESC');
960 }
961
962 $header = array(t('MMfid'), t('Configuration'), t('File'), t('Edit'), t('Delete') );
963 $rows = array();
964 while ($file = db_fetch_array($results)) {
965 if ($config) {
966 $file['config_name'] = $config->name;
967 }
968 // @@@ TODO make this match up with the media_mover_files db
969
970 $node = l('node/'. $file['nid'], 'node/'. $file['nid']);
971
972 $rows[] = array(
973 $file['mmfid'],
974 l($file['config_name'], 'admin/content/media_mover/'. $file['cid'] .'/edit') .'<br />'. $node,
975 $file['harvest_file'],
976 l(t('Edit @file', array('@file' => $file['mmfid'])), 'admin/content/media_mover/file/'. $file['mmfid'] .'/edit'),
977 l(t('Delete @file', array('@file' => $file['mmfid'])), 'admin/content/media_mover/file/'. $file['mmfid'] .'/delete'),
978 );
979 }
980 $output = theme('table', $header, $rows);
981 $output .= theme('pager');
982 return $output;
983 }
984
985 /**
986 * creates the edit the file form
987 * @file is a db fetch
988 *
989 */
990 function media_mover_edit_file_form($form_state, $file = NULL) {
991 $header = array(t('Module and Type'), t('Action'), t('File'));
992 $data[] = array("Harvest: " . $file->harvest_module, "Harvest : " . $file->harvest_action, "Harvest: " . $file->harvest_file);
993 $data[] = array("Process: " . $file->process_module, "Process : " . $file->process_action, "Process: " . $file->process_file);
994 $data[] = array("Storage: " . $file->storage_module, "Storage : " . $file->storage_action, "Storage: " . $file->storage_file);
995 $data[] = array("Complete: " . $file->complete_module, "Complete : " . $file->complete_action, "Complete: " . $file->complete_file);
996
997 $form['fid'] = array(
998 '#type' => 'value',
999 '#value' => $file->fid,
1000 );
1001
1002 $form['filedata'] = array(
1003 '#type' => 'fieldset',
1004 '#title' => t('File Data'),
1005 '#collapsible' => true,
1006 '#collapsed' => true,
1007 );
1008
1009 $form['filedata']['data'] = array(
1010 '#type' => 'markup',
1011 '#value' => theme('table', $header, $data),
1012 );
1013
1014 // @@@ This won't work in D6 without tweaking.
1015 $form['file']['nid'] = array(
1016 '#title' => t('Node ID'),
1017 '#type' => 'textfield',
1018 '#default_value' => $file->nid,
1019 '#description' => t('File is associated with this Node ID.'),
1020 );
1021
1022 $form['file']['url'] = array(
1023 '#title' => t('URL to final file'),
1024 '#type' => 'textfield',
1025 '#default_value' => $file->url,
1026 '#description' => t('File is accessible via this url.'),
1027 );
1028
1029 $options = array("Off", "On");
1030 $form['file']['status'] = array(
1031 '#title' => t('File Status'),
1032 '#type' => 'radios',
1033 '#options' => $options,
1034 '#default_value' => $file->status,
1035 '#description' => t('Links file to node.'),
1036 );
1037
1038 $form['submit'] = array(
1039 '#type' => 'submit',
1040 '#value' => t('Save'),
1041 );
1042
1043 return $form;
1044 }
1045
1046 /**
1047 * implementation of hook form_submit
1048 * nid, url, status, fid are incoming vars
1049 *
1050 */
1051 function media_mover_edit_file_form_submit($form, &$form_state) {
1052 db_query("UPDATE {media_mover_files} SET nid = %d, status = %d, url = '%s' WHERE fid = %d", $form_state['values']['nid'], $form_state['values']['status'], $form_state['values']['url']);
1053 drupal_set_message(t('Media mover file ID: !id was updated.', array('!id' => $form_state['values']['fid'])));
1054 $form_state['redirect'] = 'admin/content/media_mover/files';
1055 }
1056
1057 /**
1058 * deletes all the files associated with a specified NID
1059 * @param node is a full node object
1060 */
1061 function media_mover_api_node_file_delete($node) {
1062 if ($files = $node->media_mover) {
1063 foreach ($files as $cid => $file) {
1064 // call module hooks for this file
1065 // @ TODO
1066 db_query('DELETE FROM {media_mover_files} WHERE mmfid = %d AND nid = %d', $file['mmfid'], $file['nid']);
1067 drupal_set_message(t('Deleted: %file', array('%file' => basename($file['complete_file']))));
1068 }
1069 }
1070 }
1071
1072 /* *************************************************************** */
1073 /* XSPF Playlist functions */
1074 /* *************************************************************** */
1075
1076 /**
1077 * implementation of hook_xspf_playlist_thumbnail
1078 *
1079 */
1080 function media_mover_api_xspf_playlist_thumbnail($op, $node = null, $config = null) {
1081 switch ($op) {
1082 // defines what options xspf can use
1083 case 'define':
1084 $configurations = _mm_get_active_configurations();
1085 foreach ($configurations as $configuration) {
1086 $define['media_mover_api--'. $configuration->cid] = t('Media Mover: ') . $configuration->name;
1087 }
1088 return $define;
1089 break;
1090
1091 case 'return':
1092 if ($file = $node->media_mover[$config][0]['complete_file']) {
1093 return url($file, array('absolute' => TRUE));
1094 }
1095 break;
1096 }
1097 }
1098
1099 /**
1100 * implemenation of hook_xspf_playlist_use
1101 */
1102 function media_mover_api_xspf_playlist_use($op, $node, $config) {
1103 switch ($op) {
1104 case 'define':
1105 $configurations = _mm_get_active_configurations();
1106 foreach ($configurations as $configuration ) {
1107 $define['media_mover_api--'. $configuration->cid] = t('Media Mover: ') . $configuration->name;
1108 }
1109 return $define;
1110 break;
1111
1112 case 'return':
1113 // get files for the running configuration
1114 if ($mmfiles = $node->media_mover[$config]) {
1115 $items = array();
1116 foreach ($mmfiles as $mmfile) {
1117 $filepath = $mmfile['complete_file'];
1118 // make sure that the file path is complete
1119 if (! strstr($filepath, 'http://')) {
1120 // we're using base_url here because things get messy otherwise
1121 $filepath = $GLOBALS['base_url'] .'/'. $filepath;
1122 }
1123 // only add the file if we have an incoming item
1124 if ($xspf_item = xspf_playlist_create_file_item($node, $filepath, $node->type)) {
1125 $items[] = $xspf_item;
1126 }
1127 }
1128 return $items;
1129 }
1130 break;
1131 }
1132 }
1133
1134 /* *************************************************************** */
1135 /* THEME functions */
1136 /* *************************************************************** */
1137
1138 /**
1139 * Implementation of hook_theme().
1140 */
1141 function media_mover_api_theme() {
1142 return array(
1143 'media_mover_files' => array(
1144 'arguments' => array('files' => array()),
1145 ),
1146 'media_mover_file' => array(
1147 'arguments' => array('file' => array()),
1148 ),
1149 // @@@ This one doesn't look kosher to me...
1150 'media_mover_api_files_node_edit' => array(
1151 'arguments' => array('node' => NULL),
1152 ),
1153 );
1154 }
1155
1156 /**
1157 * Theme the files associated with a node.
1158 */
1159 function theme_media_mover_files($files) {
1160 $output = '';
1161 foreach ($files as $f) {
1162 foreach ($f as $file) {
1163 $output .= theme(array(
1164 'media_mover_file_'. $file['storage_module'] .'_'. $file['storage_action'],
1165 'media_mover_file_'. $file['storage_module'],
1166 'media_mover_file'), $file);
1167 }
1168 }
1169 return $output;
1170 }
1171
1172 /**
1173 * Default implementation for theming a file.
1174 */
1175 function theme_media_mover_file($file) {
1176 // @@@
1177 // return var_export($file, TRUE);
1178 }
1179
1180 /**
1181 * basic theming for the node/edit screen
1182 * @param $node is drupal node object
1183 */