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

Contents of /contributions/modules/twistage/twistage.module

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


Revision 1.3 - (show annotations) (download) (as text)
Tue Oct 7 15:16:56 2008 UTC (13 months, 2 weeks ago) by xmattus
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--1
Changes since 1.2: +7 -7 lines
File MIME type: text/x-php
Adding multi-video posting functionality, and a few bug fixes-
1 <?php
2
3 /**
4 * Twistage Module -- a Drupal module to interact with the Twistage API
5 * Copyright 2008 Matt Johnson, Observer Media Group
6 * License: GPL V.2
7 * Read the license here: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
8 */
9
10 /**
11 * Implementation of hook_menu
12 */
13 function twistage_menu($may_cache) {
14 $items = array();
15
16 if (!$may_cache) {
17 $items[] = array(
18 'path' => 'admin/settings/twistage',
19 'title' => t('Twistage settings'),
20 'description' => t('Manage Twistage integration.'),
21 'type' => MENU_NORMAL_ITEM,
22 'callback' => 'twistage_settings_page',
23 'access' => user_access('administer twistage'),
24 );
25
26 $items[] = array(
27 'path' => 'admin/settings/twistage/profile',
28 'title' => t('Twistage Profile'),
29 'type' => MENU_CALLBACK,
30 'callback' => 'drupal_get_form',
31 'callback arguments' => array('twistage_profile_admin_form', arg(4)),
32 'access' => user_access('administer twistage'),
33 );
34
35 $items[] = array(
36 'path' => 'admin/settings/twistage/order',
37 'title' => t('Twistage Play Order'),
38 'type' => MENU_CALLBACK,
39 'callback' => 'drupal_get_form',
40 'callback arguments' => array('twistage_order_admin_form', arg(4)),
41 'access' => user_access('administer twistage'),
42 );
43
44 $items[] = array(
45 'path' => 'admin/settings/twistage/list',
46 'title' => t('Manage profiles'),
47 'type' => MENU_DEFAULT_LOCAL_TASK,
48 'weight' => -5,
49 );
50
51 $items[] = array(
52 'path' => 'admin/settings/twistage/global',
53 'title' => t('Global settings'),
54 'type' => MENU_LOCAL_TASK,
55 'callback' => 'drupal_get_form',
56 'callback arguments' => array('twistage_global_admin_form'),
57 'weight' => -2,
58 );
59
60 // Workaround for problems with adding MENU_LOCAL_TASKs to this menu in an add-on module...
61 if (module_exists('twistage_syndicate')) {
62 $items[] = array(
63 'path' => 'admin/settings/twistage/synconfig',
64 'title' => t('Syndication settings'),
65 'type' => MENU_LOCAL_TASK,
66 'callback' => 'drupal_get_form',
67 'callback arguments' => array('twistage_syndicate_admin_form'),
68 );
69 }
70
71 $items[] = array(
72 'path' => 'admin/settings/twistage/create',
73 'title' => t('Create profile'),
74 'type' => MENU_LOCAL_TASK,
75 'callback' => 'drupal_get_form',
76 'callback arguments' => array('twistage_profile_admin_form', 0),
77 'access' => user_access('administer twistage'),
78 'weight' => 5,
79 );
80
81 $items[] = array(
82 'path' => 'admin/settings/twistage/sync',
83 'title' => t('Sync Twistage videos'),
84 'type' => MENU_CALLBACK,
85 'callback' => 'twistage_sync_videos',
86 'access' => user_access('administer twistage'),
87 );
88
89 $items[] = array(
90 'path' => 'twistage/hook',
91 'type' => MENU_CALLBACK_ITEM,
92 'callback' => 'twistage_process_hook',
93 'access' => user_access('access content'),
94 );
95
96 $items[] = array(
97 'path' => 'twistage/preroll/hook',
98 'type' => MENU_CALLBACK_ITEM,
99 'callback' => 'twistage_preroll_hook',
100 'access' => user_access('access content'),
101 );
102 }
103
104 twistage_add_page_menu_items($items);
105
106 return $items;
107 }
108
109 /**
110 * Implementation of hook_perm
111 */
112 function twistage_perm() {
113 return array('administer twistage', 'view twistage pages');
114 }
115
116 /**
117 * Implementation of hook_block
118 */
119 function twistage_block($op = 'list', $delta = 0, $edit = array()) {
120 switch ($op) {
121 case 'list':
122 $blocks = array();
123 $result = db_query("SELECT pid, name FROM {twistage_profiles} WHERE type='block'");
124 while ($profile = db_fetch_object($result)) {
125 $blocks[$profile->pid] = array('info' => 'Twistage: ' . $profile->name);
126 }
127 return $blocks;
128 case 'view':
129 $profile = twistage_get_profile($delta);
130 return array('subject' => $profile->name, 'content' => theme('twistage_block', $delta));
131 }
132 }
133
134 /**
135 * Function to generate menu item arrays for page profiles.
136 */
137 function twistage_add_page_menu_items(&$items) {
138 $result = db_query("SELECT * FROM {twistage_profiles} WHERE type='page'");
139
140 while ($profile = db_fetch_object($result)) {
141 $items[] = array(
142 'path' => $profile->path,
143 'title' => $profile->description,
144 'type' => MENU_CALLBACK,
145 'callback' => 'twistage_profile_page',
146 'callback arguments' => array($profile->name),
147 'access' => user_access('view twistage pages'),
148 );
149 }
150 }
151
152 /**
153 * Function to generate output for profiles with 'page' type.
154 * Pipe through a theme function to let themers override this easily if they like.
155 * In the New York Observer's use case, we need most pages to actually be nodes in order
156 * to maintain block control, ads, etc, so it's useful to embed Twistage content by putting the following
157 * into a php filter in the node body...
158 * <?php print theme('twistage_page', 'profile_name'); ?>
159 * ...rather than directing web users directly to $profile->path.
160 */
161 function twistage_profile_page($name) {
162 return theme('twistage_page', $name);
163 }
164
165 /**
166 * Function to generate the admin form
167 */
168 function twistage_profile_admin_form($pid) {
169 $form = array();
170
171 if (!is_numeric($pid)) {
172 drupal_goto('admin/settings/twistage');
173 }
174
175 if ($pid) {
176 $profile = twistage_get_profile($pid);
177 } else {
178 $profile = new stdClass; // will remain empty
179 }
180
181 $form['pid'] = array(
182 '#type' => 'hidden',
183 '#value' => $pid,
184 );
185
186 $form['profile'] = array(
187 '#type' => 'fieldset',
188 '#title' => t('Profile settings'),
189 );
190
191 $form['profile']['name'] = array(
192 '#type' => 'textfield',
193 '#title' => t('Profile name'),
194 '#description' => t('An machine-readable Drupal identifier for this Twistage player.'),
195 '#default_value' => $profile->name,
196 '#required' => TRUE,
197 );
198
199 $form['profile']['description'] = array(
200 '#type' => 'textfield',
201 '#title' => t('Profile description'),
202 '#description' => t('A description of this profile (for public display).'),
203 '#default_value' => $profile->description,
204 '#required' => TRUE,
205 );
206
207 $form['profile']['company'] = array(
208 '#type' => 'textfield',
209 '#title' => t('Company ID'),
210 '#description' => t('This can be found in the "Company Info" section of the "Accounts" tab in the Twistage admin area.'),
211 '#default_value' => $profile->company,
212 '#required' => TRUE,
213 );
214
215 $form['profile']['site_name'] = array(
216 '#type' => 'textfield',
217 '#title' => t('Site name'),
218 '#description' => t('The name of the Twistage site associated with this profile. This can be found in the "Sites" section of the "Settings" tab.'),
219 '#default_value' => $profile->site_name,
220 '#required' => TRUE,
221 );
222
223 $form['profile']['site_key'] = array(
224 '#type' => 'textfield',
225 '#title' => t('Site license key'),
226 '#description' => t('This can be also found in the "Sites" section of the "Settings" tab.'),
227 '#default_value' => $profile->site_key,
228 '#required' => TRUE,
229 );
230
231 $form['profile']['site_username'] = array(
232 '#type' => 'textfield',
233 '#title' => t('Site publishing username'),
234 '#description' => t('The username you use to log in to Twistage. Needed only for publishing videos through your Drupal site.'),
235 '#default_value' => $profile->site_username,
236 );
237
238 $form['profile']['type'] = array(
239 '#type' => 'radios',
240 '#title' => t('Show player as a'),
241 '#options' => array('page' => t('Page'), 'block' => t('Block')),
242 '#default_value' => $profile->type,
243 );
244
245 $form['profile']['page'] = array(
246 '#type' => 'fieldset',
247 '#title' => t('Page settings'),
248 );
249
250 $form['profile']['page']['path'] = array(
251 '#type' => 'textfield',
252 '#title' => t('Path'),
253 '#description' => t('Only valid for page profiles'),
254 '#default_value' => $profile->path,
255 '#weight' => 1,
256 );
257
258 $form['profile']['page']['page_header'] = array(
259 '#type' => 'textarea',
260 '#title' => t('Page header'),
261 '#default_value' => $profile->page_header,
262 '#weight' => 2,
263 );
264
265 $form['profile']['page']['page_header_format'] = filter_form($profile->page_header_format, 3, array('page_header_format'));
266
267 $form['profile']['page']['page_footer'] = array(
268 '#type' => 'textarea',
269 '#title' => t('Page footer'),
270 '#default_value' => $profile->page_footer,
271 '#weight' => 4
272 );
273
274 $form['profile']['page']['page_footer_format'] = filter_form($profile->page_footer_format, 5, array('page_footer_format'));
275
276 $form['profile']['page']['show_table'] = array(
277 '#type' => 'checkbox',
278 '#title' => t('Show table of videos'),
279 '#description' => t('Allow users to select other videos in the profile to view from a table on the page'),
280 '#default_value' => $profile->show_table,
281 '#weight' => 6,
282 );
283
284 if($profile->pid) {
285 $form['profile']['sync'] = array(
286 '#value' => t('<p>If you have set up your site to receive pings from Twistage, the local databse of videos will be updated continuously.</p>
287 <p>You can also <a href="/admin/settings/twistage/sync/' . $profile->pid . '">manually sync now</a>.</p>'),
288 );
289 }
290
291 $form['player'] = array(
292 '#type' => 'fieldset',
293 '#title' => t('Player settings'),
294 );
295
296 $form['player']['width'] = array(
297 '#type' => 'textfield',
298 '#title' => t('Player width'),
299 '#size' => 5,
300 '#default_value' => $profile->width,
301 );
302
303 $form['player']['height'] = array(
304 '#type' => 'textfield',
305 '#title' => t('Player height'),
306 '#size' => 5,
307 '#default_value' => $profile->height,
308 );
309
310 $form['player']['autoplay'] = array(
311 '#type' => 'checkbox',
312 '#title' => t('Autoplay'),
313 '#description' => t('Auto-start video playback once the page loads'),
314 '#default_value' => $profile->autoplay,
315 );
316
317 $form['preroll'] = array(
318 '#type' => 'fieldset',
319 '#title' => t('Pre-roll settings'),
320 );
321
322 $form['preroll']['info'] = array(
323 '#value' => t('Some applications will want to show a mandatory "pre-roll" video before the actual payload video (generally for ads). To use pre-rolls, enter a company ID and site license key from which to draw pre-roll clips. Note that they may be the same as your primary company and key.'),
324 );
325
326 $form['preroll']['preroll_company'] = array(
327 '#type' => 'textfield',
328 '#title' => t('Pre-roll company ID'),
329 '#default_value' => $profile->preroll_company,
330 );
331
332 $form['preroll']['preroll_site_name'] = array(
333 '#type' => 'textfield',
334 '#title' => t('Pre-roll site name'),
335 '#default_value' => $profile->preroll_site_name,
336 );
337
338 $form['preroll']['preroll_site_key'] = array(
339 '#type' => 'textfield',
340 '#title' => t('Pre-roll site license key'),
341 '#default_value' => $profile->preroll_site_key,
342 );
343
344 $form['preroll']['preroll_url'] = array(
345 '#type' => 'textfield',
346 '#title' => t('Pre-roll video link'),
347 '#default_value' => $profile->preroll_url,
348 '#maxlength' => 1024,
349 );
350
351 $form['hook'] = array(
352 '#type' => 'fieldset',
353 '#title' => t('Hook settings'),
354 );
355
356 $form['hook']['info'] = array(
357 '#value' => t('<p>To receive "pings" from Twistage, you must create a username and password that Twistage will use to send data to your site.</p>
358 <p>While it is technically possible to have a hook URL with no authentication, this module does not allow such a configuration for security reasons. If either of these fields are blank, pings will be acknowledged.</p>'),
359 );
360
361
362 if ($profile->username && $profile->password) {
363 $form['hook']['url'] = array(
364 '#value' => t('<p>You have entered a username and password. You should set the following as your Twistage hook URL:</p>
365 <p><i>http://' . $_SERVER['SERVER_NAME'] . '/twistage/hook/' . $profile->name. '/' . $profile->username . '/' . $profile->password . '</i></p>'),
366 );
367 } else {
368 $form['hook']['url'] = array(
369 '#value' => t('<p>You have not saved a username and password yet. A hook URL will be available when you have.</p>'),
370 );
371 }
372
373
374
375 $form['hook']['username'] = array(
376 '#type' => 'textfield',
377 '#title' => t('Username'),
378 '#default_value' => $profile->username,
379 );
380
381 $form['hook']['password'] = array(
382 '#type' => 'textfield',
383 '#title' => t('Password'),
384 '#description' => t('(will be shown in clear text)'),
385 '#default_value' => $profile->password,
386 );
387
388 return system_settings_form($form);
389 }
390
391 /**
392 * Function to validate the admin form
393 */
394 function twistage_profile_admin_form_validate($form_id, $form_values) {
395 if (preg_match('/[^a-zA-Z0-9_]/', $form_values['name'])) {
396 form_set_error('name', 'Name may contain only alphanumeric characters and underscores.');
397 }
398
399 if (!is_numeric($form_values['width'])) {
400 form_set_error('width', 'Non-numeric width entered.');
401 }
402
403 if (!is_numeric($form_values['height'])) {
404 form_set_error('height', 'Non-numeric height entered.');
405 }
406 }
407
408 /**
409 * Function to process the admin form
410 */
411 function twistage_profile_admin_form_submit($form_id, $form_values) {
412 if ($form_values['pid']) {
413 // Update
414 db_query("UPDATE {twistage_profiles} SET name='%s', description='%s', type='%s', path='%s', company='%s', site_name='%s', site_key='%s', site_username='%s', width=%d, height=%d, show_table=%d, autoplay=%d, page_header='%s', page_header_format=%d, page_footer='%s', page_footer_format=%d, preroll_company='%s', preroll_site_name='%s', preroll_site_key='%s', preroll_url='%s', username='%s', password='%s' WHERE pid=%d",
415 $form_values['name'],
416 $form_values['description'],
417 $form_values['type'],
418 $form_values['path'],
419 $form_values['company'],
420 $form_values['site_name'],
421 $form_values['site_key'],
422 $form_values['site_username'],
423 $form_values['width'],
424 $form_values['height'],
425 $form_values['show_table'],
426 $form_values['autoplay'],
427 $form_values['page_header'],
428 $form_values['page_header_format'],
429 $form_values['page_footer'],
430 $form_values['page_footer_format'],
431 $form_values['preroll_company'],
432 $form_values['preroll_site_name'],
433 $form_values['preroll_site_key'],
434 $form_values['preroll_url'],
435 $form_values['username'],
436 $form_values['password'],
437 $form_values['pid']);
438 drupal_set_message('Updated profile "' . $form_values['name'] . '".');
439 twistage_sync_videos($form_values['pid']);
440 } else {
441 // Insert
442 $pid = db_next_id("{twistage_profiles}_pid");
443 db_query("INSERT INTO {twistage_profiles} (pid, name, description, type, path, company, site_name, site_key, site_username, width, height, show_table, autoplay, page_header, page_header_format, page_footer, page_footer_format, preroll_company, preroll_site_name, preroll_site_key, preroll_url, username, password)
444 VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', %d, '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s')",
445 $pid,
446 $form_values['name'],
447 $form_values['description'],
448 $form_values['type'],
449 $form_values['path'],
450 $form_values['company'],
451 $form_values['site_name'],
452 $form_values['site_key'],
453 $form_values['site_username'],
454 $form_values['width'],
455 $form_values['height'],
456 $form_values['show_table'],
457 $form_values['autoplay'],
458 $form_values['page_header'],
459 $form_values['page_header_format'],
460 $form_values['page_footer'],
461 $form_values['page_footer_format'],
462 $form_values['preroll_company'],
463 $form_values['preroll_site_name'],
464 $form_values['preroll_site_key'],
465 $form_values['preroll_url'],
466 $form_values['username'],
467 $form_values['password']);
468 drupal_set_message('Created profile "' . $form_values['name'] . '".');
469 twistage_sync_videos($pid);
470 }
471
472 if ($form_values['type'] == 'page') {
473 menu_rebuild();
474 }
475
476 return 'admin/settings/twistage';
477 }
478
479 /**
480 * Generate the play order form
481 */
482 function twistage_order_admin_form($pid = 0) {
483 if (!$pid) {
484 drupal_goto('admin/settings/twistage');
485 }
486
487 $profile = twistage_get_profile($pid);
488
489 $form = array();
490
491 $form['info'] = array(
492 '#value' => t('Videos not assigned a play order will not be shown in the player.'),
493 );
494
495 $form['pid'] = array(
496 '#type' => 'hidden',
497 '#value' => $profile->pid,
498 );
499
500 $form['header'] = array(
501 '#type' => 'value',
502 '#value' => array(
503 t('Order'),
504 t('Video title'),
505 t('Video ID'),
506 ),
507 );
508
509 $result = db_query("SELECT vid, title, play_order, (play_order = 0) AS i FROM {twistage_videos} WHERE site_key='%s' AND pid=%d AND availability='available' ORDER BY i, play_order", $profile->site_key, $profile->pid);
510
511 while ($row = db_fetch_object($result)) {
512 $elem = "video_" . $row->vid;
513 $form['data']['play_order'][$elem] = array(
514 '#type' => 'textfield',
515 '#size' => 1,
516 '#default_value' => $row->play_order ? $row->play_order : "",
517 );
518
519 $form['data']['title'][$elem] = array(
520 '#value' => $row->title,
521 );
522
523 $form['data']['vid'][$elem] = array(
524 '#value' => $row->vid,
525 );
526 }
527
528 // Preroll, if applicable
529 if ($profile->preroll_company && $profile->preroll_site_name && $profile->preroll_site_key) {
530 $result = db_query("SELECT vid, title, play_order FROM {twistage_videos} WHERE site_key='%s'", $profile->preroll_site_key);
531
532 $prerolls = array(0 => 'None');
533 $default = 0;
534 while ($row = db_fetch_object($result)) {
535 $prerolls[$row->vid] = $row->title . " (" . $row->vid . ")";
536 if ($row->play_order == 1) {
537 $default = $row->vid;
538 }
539 }
540
541 $form['preroll'] = array(
542 '#type' => 'radios',
543 '#title' => t('Select pre-roll video'),
544 '#options' => $prerolls,
545 '#default_value' => $default,
546 );
547 }
548
549 return system_settings_form($form);
550 }
551
552 /**
553 * Theme function for play order form
554 */
555 function theme_twistage_order_admin_form($form) {
556 drupal_add_css(drupal_get_path('module', 'twistage') . '/twistage.css', 'module', 'all', FALSE);
557
558 $rows = array();
559
560 foreach (element_children($form['data']['play_order']) as $key) {
561 $row = array();
562 $row['data'][0] = array('data' => drupal_render($form['data']['play_order'][$key]), 'class' => 'twistage-order-td');
563 $row['data'][1] = drupal_render($form['data']['title'][$key]);
564 $row['data'][2] = drupal_render($form['data']['vid'][$key]);
565 $rows[] = $row;
566 }
567
568 $output = theme('table', $form['header']['#value'], $rows, array('id' => 'twistage-order-table'));
569 $output .= drupal_render($form);
570
571 return $output;
572 }
573
574 /**
575 * Valid play order form
576 */
577 function twistage_order_admin_form_validate($form_id, $form_values) {
578 foreach ($form_values as $key => $val) {
579 if (substr($key, 0, 5) == "video" && $val && !is_numeric($val)) {
580 form_set_error($key, 'Non-numeric order for video ' . substr($key, 6));
581 }
582 }
583 }
584
585 /**
586 * Process play order form
587 */
588 function twistage_order_admin_form_submit($form_id, $form_values) {
589 $profile = twistage_get_profile($form_values['pid']);
590
591 // Store old play orders to compare with new ones
592 $orders = array();
593 $result = db_query("SELECT vid, play_order FROM {twistage_videos} WHERE pid=%d AND site_key='%s'", $profile->pid, $profile->site_key);
594 while ($video = db_fetch_object($result)) {
595 $orders[$video->vid] = $video->play_order;
596 }
597
598 // First, wipe out all existing play orders
599 db_query("UPDATE {twistage_videos} SET play_order=0 WHERE pid=%d AND site_key='%s'", $profile->pid, $profile->site_key);
600
601 foreach ($form_values as $key => $val) {
602 if (substr($key, 0, 5) == "video") {
603 $vid = substr($key, 6);
604 if ($val) {
605 db_query("UPDATE {twistage_videos} SET play_order=%d WHERE vid='%s' AND pid=%d", $val, $vid, $profile->pid);
606 if (!$orders[$vid]) {
607 // Video was not previously in the play order, but it is now. Fire a "hook_twistage_order" to let other modules act
608 foreach (module_implements('twistage_api') as $module) {
609 $func = $module . '_twistage_api';
610 $video = twistage_get_video($vid, $profile->pid);
611 // TODO reenble when syn is working better
612 $func('order add', $video);
613 }
614 }
615 }
616 }
617 }
618
619 // Reset play order on all prerolls
620 db_query("UPDATE {twistage_videos} SET play_order=0 WHERE pid=%d AND site_key='%s'", $profile->pid, $profile->preroll_site_key);
621
622 if ($form_values['preroll']) {
623 // Set 1 on the chosen preroll if we have one
624 db_query("UPDATE {twistage_videos} SET play_order=1 WHERE vid='%s' AND pid=%d", $form_values['preroll'], $profile->pid);
625 }
626
627 drupal_set_message('Play order updated successfully.');
628
629 return 'admin/settings/twistage';
630
631 }
632
633 function twistage_global_admin_form() {
634 $form = array();
635
636 $form['info'] = array(
637 '#value' => '<p>Because the preroll site is meant to be potentially shared among several profiles, a global hook URL is provided for it.<br />
638 Enter a username and password for this hook URL, and it will update preroll videos in all profiles.</p>'
639 );
640
641 if (variable_get('twistage_preroll_hook_username', 0) && variable_get('twistage_preroll_hook_password', 0)) {
642 $form['info2'] = array(
643 '#value' => '<p>Your existing hook URL is: <i>http://' . $_SERVER['SERVER_NAME'] . '/twistage/preroll/hook/' . variable_get('twistage_preroll_hook_username', 0) . '/' . variable_get('twistage_preroll_hook_password', 0) . '</i>',
644 );
645 }
646
647 $form['username'] = array(
648 '#type' => 'textfield',
649 '#title' => t('Username'),
650 '#default_value' => variable_get('twistage_preroll_hook_username', ''),
651 );
652
653 $form['password'] = array(
654 '#type' => 'textfield',
655 '#title' => t('Password'),
656 '#default_value' => variable_get('twistage_preroll_hook_password', ''),
657 '#description' => t('Will be shown in clear text'),
658 );
659
660 return system_settings_form($form);
661 }
662
663 function twistage_global_admin_form_submit($form_id, $form_values) {
664 variable_set('twistage_preroll_hook_username', $form_values['username']);
665 variable_set('twistage_preroll_hook_password', $form_values['password']);
666 drupal_set_message('Preroll hook configuration saved.');
667 return 'admin/settings/twistage';
668 }
669
670
671 /**
672 * Generate the settings screen
673 */
674 function twistage_settings_page($pid = 0) {
675 if ($pid && is_numeric($pid)) {
676 return drupal_get_form('twistage_profile_admin_form', $pid);
677 } else {
678 $result = db_query("SELECT pid, name, type, path FROM {twistage_profiles} WHERE 1");
679 if (!db_num_rows($result)) {
680 $content = '<p>No profiles found. <a href="/admin/settings/twistage/create">Create one now</a>.</p>';
681 return $content;
682 }
683
684 $header = array(
685 t('Name'),
686 t('Type'),
687 t('Path (if page)'),
688 t('Operations'),
689 );
690
691 $rows = array();
692
693 while ($profile = db_fetch_object($result)) {
694 $rows[] = array(
695 $profile->name,
696 $profile->type,
697 $profile->path,
698 '<a href="/admin/settings/twistage/profile/' . $profile->pid . '">edit</a> &nbsp;&nbsp;&nbsp;' .
699 '<a href="/admin/settings/twistage/order/' . $profile->pid . '">set play order</a> &nbsp;&nbsp;&nbsp;' .
700 (module_exists('twistage_syndicate') ? '<a href="/admin/settings/twistage/syndication/' . $profile->pid . '">syndication</a>' : '') . "&nbsp;&nbsp;&nbsp;" .
701 (module_exists('twistage_publish') ? '<a href="/admin/settings/twistage/publishing/' . $profile->pid . '">publishing</a>' : ''),
702 );
703 }
704
705 return theme('table', $header, $rows);
706 }
707 }
708
709 /**
710 * Perform a sync of data from Twistage on all the videos associated with a certain profile.
711 * @param pid
712 * profile ID to sync
713 */
714 function twistage_sync_videos($pid = 0) {
715 if (!$pid) {
716 return;
717 }
718
719 $profile = twistage_get_profile($pid);
720
721 if (!$profile) {
722 return;
723 }
724
725 // Sync videos from the preroll site if one exists
726 if ($profile->preroll_company && $profile->preroll_site_name && $profile->preroll_site_key) {
727 $preroll = TRUE;
728 }
729
730 // Store play orders
731 $result = db_query("SELECT * FROM {twistage_videos} WHERE pid=%d", $profile->pid);
732 $order = array();
733 while ($video = db_fetch_object($result)) {
734 $order[$video->vid] = $video->play_order;
735 $syn[$video->vid] = $video->syndicated;
736 }
737
738 // Get all videos (non-preroll)
739 $main = twistage_fetch_videos($pid, false);
740 // Sleep between API calls to satisfy the 1-req-per-second limit twistage imposes.
741 usleep(1500000);
742 // Get prerolls
743 $prerolls = twistage_fetch_videos($pid, true);
744 // Smash them together
745 $videos = array_merge($main, $prerolls);
746 if ($videos) {
747 // Delete all existing data.
748 db_query("DELETE FROM {twistage_videos} WHERE pid=%d", $pid);
749 } else {
750 // Bail out, and don't delete anything.
751 drupal_set_message('Unable to sync. Please check the error log for more details.', 'error');
752 drupal_goto('admin/settings/twistage');
753 }
754
755 foreach ($videos as $vdata) {
756 $vdata = (array)$vdata; // To make accessing properties easier. PHP is not happy with object properties with dashes in them.
757
758 $video = new stdClass;
759
760 // Check to see if this video belongs to the main or preroll section
761 if ($vdata['site-name'] == $profile->site_name) {
762 $video->site_key = $profile->site_key;
763 } else if ($preroll & ($vdata['site-name'] == $profile->preroll_site_name)) {
764 $video->site_key = $profile->preroll_site_key;
765 } else {
766 // Skip video if no match on either site name
767 continue;
768 }
769
770 $tags = array();
771 foreach ($vdata['tags']->tag as $tag) {
772 $tags[] = strval($tag->name);
773 }
774
775 $video->pid = $pid;
776 $video->vid = $vdata['video-id'];
777 $video->title = $vdata['title'];
778 $video->description = $vdata['description'];
779 $video->tags = $tags;
780 $video->status = $vdata['status']; // different status codes are returned by "Search Videos" and "Video Metadata". For parity we use metadata.
781 $video->availability = $vdata['availability'];
782 $video->created = strtotime($vdata['created-at']);
783 $video->publisher = $vdata['publisher-name'];
784
785 twistage_save_video($video);
786
787 if ($order[$video->vid]) {
788 db_query("UPDATE {twistage_videos} SET play_order=%d WHERE vid='%s'", $order[$video->vid], $video->vid);
789 } else {
790 db_query("UPDATE {twistage_videos} SET play_order=0 WHERE vid='%s'", $video->vid);
791 }
792
793 if ($syn[$video->vid]) {
794 db_query("UPDATE {twistage_videos} SET syndicated=%d WHERE vid='%s'", $syn[$video->vid], $video->vid);
795 } else {
796 db_query("UPDATE {twistage_videos} SET syndicated=0 WHERE vid='%s'", $video->vid);
797 }
798 }
799 drupal_set_message('Video sync complete.');
800 drupal_goto('admin/settings/twistage');
801 }
802
803 /**
804 * Fetch an array of video objects from Twistage that are associated with the given profile. Note that they are returned in no particular order (just as Twistage provides them)
805 * Calling this function when rendering any Drupal page will add notable latency, since it necessarily involves waiting for a reply from Twistage.
806 * Therefore, this should only be called when syncing the local video DB with Twistage (i.e., upon installation or user request).
807 * The form of the data returned is somewhat awkward for PHP (there are dashes in the object property names). For ease of use, when the objects are stored
808 * locally in the databse, some of the properties are renamed. User-facing functions in this module use these local naming conventions, not what comes out of this function.
809 * This function is a wrapper for the Twistage "Video Search API", although it returns an array of objects similar to what twistage_fetch_metadata returns,
810 * since it requests the list of videos with a verbosity level of "high".
811 * @param pid
812 * The profile for which to fetch videos
813 * @param preroll
814 * Fetch videos associated with the preroll site for this profile, not the main site
815 * @param start
816 * Optionally limit the search to videos posted after this timestamp
817 * @param end
818 * Optionally limit the search to videos posted after this timestamp
819 * @param limit
820 * Return only this many videos
821 * @return
822 * array of video objects
823 */
824 function twistage_fetch_videos($pid, $preroll = false, $start = 0, $end = 0, $limit = 0) {
825 $profile = twistage_get_profile($pid);
826
827 $signature = _twistage_authenticate($profile->site_key);
828
829 if ($preroll) {
830 $url = "http://service.twistage.com/companies/" . urlencode($profile->preroll_company) . "/sites/" . urlencode($profile->preroll_site_name) . "/videos.xml";
831 $url .= "?verbosity=high&signature=" . urlencode($signature);
832 } else {
833 $url = "http://service.twistage.com/companies/" . urlencode($profile->company) . "/sites/" . urlencode($profile->site_name) . "/videos.xml";
834 $url .= "?verbosity=high&signature=" . urlencode($signature);
835 }
836
837 if ($start) {
838 $s = getdate($start);
839 $str = $s['year'] . '/' . $s['mon'] . '/' . $s['mday'];
840 $url .= "&from=" . urlencode($str);
841 }
842
843 if ($end) {
844 $s = getdate($end);
845 $str = $s['year'] . '/' . $s['mon'] . '/' . $s['mday'];
846 $url .= "&until=" . urlencode($str);
847 }
848
849 if ($limit) {
850 $url .= "&limit=" . urlencode($limit);
851 }
852
853 $data = file_get_contents($url);
854
855 if (!$data) {
856 watchdog('twistage', 'Error obtaining video list from Twistage. Check your account settings.');
857 return array();
858 }
859
860 $xml = simplexml_load_string($data);
861
862 if ($xml) {
863 foreach ($xml->video as $video) {
864 $videos[] = $video;
865 }
866 return $videos;
867 } else {
868 watchdog('twistage', 'XML parse error in data fetched from Twistage');
869 return array();
870 }
871 }
872
873 /**
874 * Fetch metadata for a video.
875 * @param vid
876 * the video ID
877 * @param pid
878 * profile ID from which to take authentication credentials
879 * @return array
880 * an array of metadata
881 */
882 function twistage_fetch_metadata($vid, $pid) {
883 $profile = twistage_get_profile($pid);
884 $signature = _twistage_authenticate($profile->site_key);
885 $url = "http://console.twistage.com/videos/" . urlencode($vid) . ".xml?signature=" . urlencode($signature);
886 $data = file_get_contents($url);
887 if (!$data) {
888 // No luck with the main site key, so try the preroll site key before failing
889 $signature = _twistage_authenticate($profile->preroll_site_key);
890 $url = "http://console.twistage.com/videos/" . urlencode($vid) . ".xml?signature=" . urlencode($signature);
891 $data = file_get_contents($url);
892 if (!$data) {
893 watchdog('twistage', 'Could not fetch metadata for video ID ' . $vid, WATCHDOG_WARNING);
894 return array();
895 }
896 }
897
898 $xml = simplexml_load_string($data);
899 return (array)$xml;
900 }
901
902
903
904 /**
905 * Save a Twistage video object in the local database.
906 * This doesn't work the same way as Drupal nodes, etc. The vid is determined by Twistage and we must use it since it's what must be passed to render a player.
907 * This function does not touch play order or syndication data.
908 * In no way does this function save anything to your Twistage account.
909 * @param video
910 * Object representing the video to save. Must be a object following Drupal conventions (ie, renamed properties with dashes in them, timestamp instead of string for created time)
911 */
912 function twistage_save_video($video) {
913 if (!$video->vid || !$video->pid) {
914 // Bail out if something is amiss.
915 return;
916 }
917
918 $result = db_query("SELECT vid FROM {twistage_videos} WHERE vid='%s' AND pid=%d", $video->vid, $video->pid);
919 if (db_num_rows($result)) {
920 // Update
921 db_query("UPDATE {twistage_videos} SET site_key='%s', title='%s', description='%s', tags='%s', status='%s', availability='%s', created=%d, publisher='%s' WHERE vid='%s' AND pid=%d",
922 $video->site_key,
923 $video->title,
924 $video->description,
925 serialize($video->tags),
926 $video->status,
927 $video->availability,
928 $video->created,
929 $video->publisher,
930 $video->vid,
931 $video->pid);
932 } else {
933 db_query("INSERT INTO {twistage_videos} (vid, pid, site_key, title, description, tags, status, availability, created, publisher) VALUES ('%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s')",
934 $video->vid,
935 $video->pid,
936 $video->site_key,
937 $video->title,
938 $video->description,
939 serialize($video->tags),
940 $video->status,
941 $video->availability,
942 $video->created,
943 $video->publisher);
944 }
945
946 }
947
948 /**
949 * Retrieve a Twistage video object from the local database. Retrieves all properties.
950 * @param vid
951 * video ID
952 * @return
953 * video object
954 */
955 function twistage_get_video($vid, $pid) {
956 $result = db_query("SELECT * FROM {twistage_videos} WHERE vid='%s' AND pid=%d", $vid, $pid);
957 $video = db_fetch_object($result);
958 // This was a particularly nasty bug which messed up a lot of things downstream that depended on $video being null if the vid didn't exist in the db
959 // setting $video->tags to a blank array registers $video as non-null.
960 if ($video) {
961 $video->tags = unserialize($video->tags);
962 }
963 return $video;
964 }
965
966 /**
967 * Delete a Twistage video.
968 * @param vid
969 * video ID
970 */
971 function twistage_delete_video($vid) {
972 $result = db_query("DELETE FROM {twistage_videos} WHERE vid='%s'", $vid);
973 }
974
975 /**
976 * Return a Twistage profile object -- all properties.
977 * @param pid
978 * profile ID
979 * @return
980 * profile object
981 */
982 function twistage_get_profile($pid) {
983 return db_fetch_object(db_query("SELECT * FROM {twistage_profiles} WHERE pid=%d", $pid));
984 }
985
986 /**
987 * Receives "pings" from Twistage and provide Drupal module hooks appropriately.
988 * We expect the user will have entered a "Hook URL" in their Twistage account which includes a username and password as GET parameters. These must be entered in the admin form.
989 * The actual Twistage hook data is passed as POST parameters.
990 */
991 function twistage_process_hook($profile, $username, $password) {
992 $profile = db_fetch_object(db_query("SELECT * FROM {twistage_profiles} WHERE name='%s'", $profile));
993
994 // Check to see if we have a username/password. If not, we do not process the hook at all.
995 if ($profile->username == '' || $profile->password == '') {
996 return;
997 }
998
999 // Next, check to see if the username/password passed match these.
1000 if ($profile->username != $username || $profile->password != $password) {
1001 return;
1002 }
1003
1004 // Now that we've passed those two tests, process the
1005 switch ($_POST['action']) {
1006 case 'update':
1007 case 'create':
1008 // New video posted.
1009 $video = twistage_get_video($_POST['vid'], $profile->pid);
1010
1011 if (!$video) {
1012 $video = new stdClass;
1013 $video->vid = $_POST['vid'];
1014 $video->pid = $profile->pid;
1015 }
1016 $video->title = $_POST['title'];
1017
1018 $xml = twistage_fetch_metadata($video->vid, $video->pid);
1019
1020 if($xml['site-name'] == $profile->preroll_site_name) {
1021 $video->site_key = $profile->preroll_site_key;
1022 } else {
1023 $video->site_key = $profile->site_key;
1024 }
1025
1026 $tags = array();
1027 foreach ($xml['tags']->tag as $tag) {
1028 $tags[] = strval($tag->name);
1029 }
1030
1031 $video->description = $xml['description'];
1032 $video->tags = $tags;
1033 $video->status = $xml['status'];
1034 $video->availability = $xml['availability'];
1035 $video->created = strtotime($xml['created-at']);
1036 $video->publisher = $xml['publisher-name'];
1037
1038 // If we got a custom nid data field, include that in the video object so that hook implementations can see it
1039 if ($_POST['nid']) {
1040 $video->nid = $_POST['nid'];
1041 }
1042
1043 twistage_save_video($video);
1044
1045 // hook_twistage_api: Tell other modules about this if they're interested. In order to maintain consistenc, we do not pass the $video object by reference or act on any return value.
1046 foreach (module_implements('twistage_api') as $module) {
1047 $func = $module . '_twistage_api';
1048 $func($_POST['action'], $video);
1049 }
1050
1051 break;
1052
1053 case 'delete':
1054 $video = twistage_get_video($_POST['vid'], $profile->pid);
1055 foreach (module_implements('twistage_api') as $module) {
1056 $func = $module . '_twistage_api';
1057 $func('delete', $video);
1058 }
1059 twistage_delete_video($_POST['vid'], $profile->pid);
1060 break;
1061 }
1062
1063 // We don't actually want to render a page here, so stop code execution now.
1064 die();
1065 }
1066
1067 function twistage_preroll_hook($username, $password) {
1068 // Basic security checks
1069 if (!variable_get('twistage_preroll_hook_username', 0) || !variable_get('twistage_preroll_hook_password', 0)) {
1070 return;
1071 }
1072
1073 if ($username != variable_get('twistage_preroll_hook_username', '') || $password != variable_get('twistage_preroll_hook_password', '')) {
1074 return;
1075 }
1076
1077 // Cycle through all profiles and check if the video exists there.
1078 $result = db_query("SELECT * FROM {twistage_profiles}");
1079 while ($profile = db_fetch_object($result)) {
1080 $video = twistage_get_video($_POST['vid'], $profile->pid);
1081 if ($video) {
1082 // We only act if the video is already in the profile; we're not adding videos to profiles that don't already have them.
1083 $xml = twistage_fetch_metadata($video->vid, $video->pid);
1084 $tags = array();
1085 foreach ($xml['tags']->tag as $tag) {
1086 $tags[] = strval($tag->name);
1087 }
1088
1089 $video->description = $xml['description'];
1090 $video->tags = $tags;
1091 $video->status = $xml['status'];
1092 $video->availability = $xml['availability'];
1093 $video->created = strtotime($xml['created-at']);
1094 $video->publisher = $xml['publisher-name'];
1095
1096 twistage_save_video($video);
1097
1098 // Fire a hook_twistage_api for each profile/video combo.
1099 foreach (module_implements('twistage_api') as $module) {
1100 $func = $module . '_twistage_api';
1101 $func($_POST['action'], $video);
1102 }
1103 }
1104 }
1105 }
1106
1107 function twistage_get_first_video($pid) {
1108 $profile = twistage_get_profile($pid);
1109 $video = db_fetch_object(db_query_range("SELECT * FROM {twistage_videos} WHERE pid=%d AND site_key='%s' AND play_order > 0 ORDER BY play_order", $pid, $profile->site_key, 0, 1));
1110 if ($video) {
1111 $video->tags = unserialize($video->tags);
1112 }
1113 return $video;
1114 }
1115
1116 function twistage_get_preroll_video($pid) {
1117 $profile = twistage_get_profile($pid);
1118 $video = db_fetch_object(db_query_range("SELECT * FROM {twistage_videos} WHERE pid=%d AND site_key='%s' AND play_order > 0 ORDER BY play_order", $pid, $profile->preroll_site_key, 0, 1));
1119 if ($video) {
1120 $video->tags = unserialize($video->tags);
1121 }
1122 return $video;
1123 }
1124
1125
1126 /**
1127 * Tool function to request a session signature from Twistage using the site license key.
1128 * @return
1129 * string containing the session signature
1130 */
1131 function _twistage_authenticate($key, $publishUser = '') {
1132 if ($publishUser) {
1133 $url = "http://service.twistage.com/api/publish_key";
1134 $url .= "?authKey=" . urlencode($key) . "&userID=" . urlencode($publishUser);
1135 } else {
1136 $url = "http://service.twistage.com/api/view_key";
1137 $url .= "?authKey=" . urlencode($key);
1138 }
1139
1140 $fp = fopen($url, "r");
1141 if ($fp) {
1142 $signature = fread($fp, 40);
1143 fclose($fp);
1144 } else {
1145 $signature = "";
1146 }
1147
1148 return $signature;