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

Contents of /contributions/modules/ljxp/ljxp.module

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


Revision 1.16 - (show annotations) (download) (as text)
Wed Oct 8 21:47:09 2008 UTC (13 months, 2 weeks ago) by karpuz
Branch: MAIN
CVS Tags: DRUPAL-6--1-5, HEAD
Changes since 1.15: +2 -2 lines
File MIME type: text/x-php
SA-2008-063 http://drupal.org/node/318739
1 <?php
2 // $Id: ljxp.module,v 1.15 2008/08/29 17:35:01 valthebald Exp $
3 /**
4 * @file ljxp.module
5 *
6 * This module cross-posts from Drupal to LiveJournal. It is modelled after
7 * the LJXP WordPress plugin of the same name, written by Evan Broder. See
8 * http://www.ebroder.net.
9 */
10
11 /*
12 * Implementation of hook_perm
13 * @see http://api.drupal.org/api/function/hook_perm/6
14 */
15 function ljxp_perm() {
16 return array('can crosspost to livejournal');
17 }
18
19 /*
20 * Implementation of hook_menu
21 * @see http://api.drupal.org/api/function/hook_menu/6
22 */
23 function ljxp_menu() {
24 $items = array();
25 $items['admin/settings/ljxp'] = array(
26 'title' => t('LiveJournal crossposter'),
27 'description' => t('Allows you or your users to automatically crosspost entries to a LiveJournal account.'),
28 'page callback' => 'drupal_get_form',
29 'page arguments' => array('ljxp_admin_settings'),
30 'access arguments' => array('administer site configuration'),
31 'type' => MENU_NORMAL_ITEM, // optional
32 );
33 return $items;
34 }
35
36 /*
37 * Implementation of hook_form_alter
38 * @see http://api.drupal.org/api/function/hook_form_alter/6
39 */
40 function ljxp_form_alter(&$form, $form_state, $form_id) {
41 global $user;
42 if ($form['#id'] != 'node-form' ||
43 !user_access('can crosspost to livejournal') ||
44 $user->uid != $form['uid']['#value']) {
45 return;
46 }
47 $default_state = variable_get('ljxp_type_' . $form['type']['#value'], 0);
48
49 if (!$default_state) {
50 return;
51 }
52
53 $ljxp = ljxp_load_settings($user->uid);
54 if (!$ljxp->active) {
55 return;
56 }
57
58 if ($form['nid']['#value']) {
59 $lj = db_fetch_array(db_query("SELECT ljid FROM {ljxp_node} WHERE nid = %d", $form['nid']['#value']));
60 $form['ljid'] = array(
61 '#type' => 'value',
62 '#value' => $lj['ljid'],
63 );
64 }
65 $form['ljxp'] = array('#type' => 'value', '#value' => true);
66
67 $checked = $lj['ljid'] ? TRUE : ($default_state == 'yes');
68 $form['ljxp_crosspost'] = array(
69 '#type' => 'checkbox',
70 '#title' => t('Crosspost to LiveJournal'),
71 '#default_value' => $checked,
72 '#description' => t('Crosspost this to LiveJournal; if already crossposted and you uncheck this, it will be deleted from LiveJournal.'),
73 );
74
75 return $form;
76 }
77
78 /*
79 * hook_nodeapi implementation
80 * @see http://api.drupal.org/api/function/hook_nodeapi/6
81 */
82 function ljxp_nodeapi(&$node, $op, $arg1, $arg2) {
83 // only let people with access; also skip people editing other people's posts.
84 if (!user_access('can crosspost to livejournal') || $GLOBALS['user']->uid != $node->uid) {
85 return;
86 }
87 switch ($op) {
88 case 'insert':
89 case 'update':
90 if ($node->ljxp) {
91 if ($node->ljid && (!$node->ljxp_crosspost || !$node->status)) {
92 // delete nodes that are crossposted and set to crosspost as well
93 // as nodes that are unpublished
94 ljxp_delete($node);
95 }
96 elseif ($node->status && $node->ljxp_crosspost) {
97 ljxp_post($node);
98 }
99 }
100 break;
101 case 'delete':
102 if ($node->ljid) {
103 ljxp_delete($node);
104 }
105 }
106 }
107
108 /*
109 * hook_user implementation
110 * @see http://api.drupal.org/api/function/hook_user/6
111 */
112 function ljxp_user($op, &$edit, &$account, $category = NULL) {
113 // hooks: form, insert, validate, update, view
114 switch ($op) {
115 case 'form':
116 if ($category == 'account' && user_access('can crosspost to livejournal', $account)) {
117 $ljxp = ljxp_load_settings($account->uid);
118 $form['ljxp'] = array(
119 '#type' => 'fieldset',
120 '#tree' => 'true',
121 '#title' => t('LiveJournal cross-posting'),
122 '#weight' => 1,
123 );
124 $form['ljxp']['active'] = array(
125 '#type' => 'checkbox',
126 '#title' => t('Use Livejournal crossposting'),
127 '#description' => t('Check this box to enable crossposting to livejournal.'),
128 '#default_value' => $ljxp->active,
129 );
130 $form['ljxp']['host'] = array(
131 '#type' => 'textfield',
132 '#default_value' => $ljxp->host,
133 '#title' => t('Host'),
134 '#size' => 30,
135 '#description' => t('If you are using a LiveJournal-compliant site other than LiveJournal (like DeadJournal), enter the domain name here. LiveJournal users can use the default value'),
136 // hack -- inline style
137 '#prefix' => '<div style="width: 49%; margin: 0 .5em 0 0; padding: 0; float: left;">'
138 );
139 $form['ljxp']['name'] = array(
140 '#type' => 'textfield',
141 '#default_value' => $ljxp->name,
142 '#title' => t('Username'),
143 '#size' => 20,
144 '#description' => t('Your username on LiveJournal'),
145 );
146 $form['ljxp']['pass'] = array(
147 '#type' => 'password',
148 '#title' => t('Password'),
149 '#size' => 15,
150 '#description' => t('Only enter a value if you wish to change the stored password. Leaving this field blank will not erase any passwords already stored.'),
151 );
152 $form['ljxp']['community'] = array(
153 '#type' => 'textfield',
154 '#default_value' => $ljxp->community,
155 '#title' => t('Community'),
156 '#size' => 20,
157 '#description' => t('If you wish your posts to be copied to a community, enter the community name here. Leaving this space blank will copy the posts to the specified user\'s journal instead'),
158 );
159 $form['ljxp']['site_name'] = array(
160 '#type' => 'textfield',
161 '#default_value' => $ljxp->site_name,
162 '#title' => t('Your site name'),
163 '#size' => 20,
164 '#description' => t('Enter the name of your blog to use in the cross-posting header; if blank the name of this site (@s) will be used. This field will only be used if you do not specify a custom header', array('@s' => variable_get('site_name', 'drupal'))),
165 );
166 $form['ljxp']['tags'] = array(
167 '#type' => 'checkbox',
168 '#title' => t('Tag posts with categories'),
169 '#default_value' => $ljxp->tags,
170 '#description' => t('Check this box to attempt to translate local categories to LiveJournal tags. This option may be troublesome if you use non-Roman characters, as LJ has trouble with that.'),
171 '#suffix' => '</div>',
172 );
173 $form['ljxp']['privacy'] = array(
174 '#type' => 'radios',
175 '#title' => t('Livejournal posts will be'),
176 '#options' => array('public' => t('Public'), 'friends' => t('Friends only')),
177 '#default_value' => $ljxp->privacy,
178 '#prefix' => '<div style="width: 49%; margin: 0 0 0 .5em; padding: 0; float: left;">'
179 );
180 $form['ljxp']['comments'] = array(
181 '#type' => 'radios',
182 '#title' => t('Comments'),
183 '#options' => array('1' => t('Allow comments on LiveJournal'), '0' => t('Require users to comment here')),
184 '#default_value' => $ljxp->comments,
185 );
186 $form['ljxp']['post_type'] = array(
187 '#type' => 'radios',
188 '#title' => t('How to handle teasers'),
189 '#options' => array(
190 'teaser' => t('Post only the teaser'),
191 'lj-cut' => t('Post the teaser with the rest behind a cut'),
192 'full post' => t('Post the entire post'),
193 ),
194 '#default_value' => $ljxp->post_type,
195 '#description' => t('If your posts do not have automatically generated teasers, the cut option may work improperly.'),
196 );
197 $form['ljxp']['cut_text'] = array(
198 '#type' => 'textfield',
199 '#default_value' => $ljxp->cut_text,
200 '#title' => t('Cut Text'),
201 '#size' => 50,
202 '#description' => t('If using an LJ cut, enter the text to use for the cut link'),
203 '#suffix' => '</div>',
204 );
205 $form['ljxp']['default'] = array(
206 '#type' => 'value',
207 '#value' => $ljxp->default,
208 );
209 $form['ljxp']['clear'] = array('#value' => '<br clear="left" />');
210 $form['ljxp']['custom_header'] = array(
211 '#type' => 'textarea',
212 '#title' => t('Custom header'),
213 '#default_value' => $ljxp->custom_header,
214 '#description' => t('This header will appear on cross-posted posts; if left blank, a default custom header will be put into place. You may use the following variables: %blog_name, %permalink, %comments_link.'),
215 );
216 $form['ljxp']['custom_header_format'] = filter_form($ljxp->custom_header_format, NULL, array('ljxp', 'custom_header_format'));
217 return $form;
218 }
219 break;
220 case 'update':
221 case 'insert':
222 if (user_access('can crosspost to livejournal', $account) && $category == 'account' && $edit['ljxp']) {
223 $ljxp = (object) $edit['ljxp'];
224 $ljxp->uid = $account->uid;
225 ljxp_save_settings($ljxp);
226 }
227 break;
228 case 'view':
229 global $user;
230 if (user_access('can crosspost to livejournal', $account) && ($user->uid == $account->uid || user_access('administer site configuration'))) {
231 $ljxp = ljxp_load_settings($account->uid);
232 if (!$ljxp->active) {
233 $output = t('Not active');
234 }
235 else {
236 $output = theme('ljxp_user_info', $ljxp, $account);
237 }
238 return array(t('Livejournal Crossposting') => array(array('value' => $output)));
239 }
240 }
241 }
242
243 function theme_ljxp_user_info($ljxp, $account) {
244 $output .= '<div>' . t('Username') . ': ' . $ljxp->name . '</div>';
245 if ($ljxp->community) {
246 $output .= '<div>' . t('Community') . ': ' . $ljxp->community . '</div>';
247 }
248 return $output;
249 }
250 // --------------------------------------------------------------------------
251 // Displayable pages -- primarily for settings et al.
252
253 function ljxp_admin_settings() {
254 $form = array();
255 $options = array(
256 '0' => t('Cannot crosspost to LJ'),
257 'yes' => t('Crosspost to LJ by default'),
258 'no' => t('Can crosspost to LJ, but not by default'),
259 );
260 $form['blurb'] = array(
261 '#value' => t('<p>Choose which types to allow for LiveJournal crossposting; if allowing a type to be crossposted, you must also select whether or not new posts will be crossposted by default. Each user must set up their own LJ account data from the normal "my account" edit screen.</p>')
262 );
263 foreach (node_get_types('names') as $type => $label) {
264 $form['ljxp_type_' . $type] = array(
265 '#type' => 'select',
266 '#options' => $options,
267 '#default_value' => variable_get('ljxp_type_' . $type, '0'),
268 '#title' => $label
269 );
270 }
271 /*$form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration') );
272 $form['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset to defaults') );
273 $form['#submit'][]='ljxp_admin_settings_submit';*/
274
275 return system_settings_form($form);
276 }
277
278 function ljxp_admin_settings_submit($form, &$form_state) {
279
280 }
281
282 // --------------------------------------------------------------------------
283 // Database interaction
284
285 function ljxp_load_settings($uid = NULL, $get_defaults = false) {
286 if (!$uid) {
287 global $user;
288 $uid = $user->uid;
289 }
290
291 static $cache = array();
292 if (!$cache[$uid]) {
293 $result = db_query("SELECT * FROM {ljxp} WHERE uid = %d", $uid);
294 $ljxp = db_fetch_object($result);
295 if (!$ljxp) {
296 $cache[$uid] = ljxp_load_defaults($uid);
297 }
298 else {
299 $cache[$uid] = $ljxp;
300 }
301 }
302
303 return $cache[$uid];
304 }
305
306 function ljxp_load_defaults($uid) {
307 $ljxp = new stdClass();
308 $ljxp->host = 'http://www.livejournal.com';
309 $ljxp->privacy = 'public';
310 $ljxp->comment = TRUE;
311 $ljxp->tags = TRUE;
312 $ljxp->post_type = 'teaser';
313 $ljxp->default = TRUE;
314 $ljxp->uid = $uid;
315 return $ljxp;
316 }
317
318 function ljxp_save_settings($ljxp) {
319 if ($ljxp->default) {
320 db_query("INSERT INTO {ljxp} (uid, active, host, name, pass, community, tags, privacy, comments, post_type, cut_text, site_name, custom_header, custom_header_format) VALUES (%d, %d, '%s', '%s', '%s', '%s', %d, '%s', %d, '%s', '%s', '%s', '%s', '%s')",
321 $ljxp->uid,
322 $ljxp->active,
323 $ljxp->host,
324 $ljxp->name,
325 md5($ljxp->pass),
326 $ljxp->community,
327 $ljxp->tags,
328 $ljxp->privacy,
329 $ljxp->comments,
330 $ljxp->post_type,
331 $ljxp->cut_text,
332 $ljxp->site_name,
333 $ljxp->custom_header,
334 $ljxp->custom_header_format
335 );
336 }
337 else {
338 db_query("UPDATE {ljxp} SET active = %d, host = '%s', name = '%s', community = '%s', tags = '%s', privacy = '%s', comments = %d, post_type = '%s', cut_text = '%s', site_name = '%s', custom_header = '%s', custom_header_format = '%s' WHERE uid = %d",
339 $ljxp->active,
340 $ljxp->host,
341 $ljxp->name,
342 $ljxp->community,
343 $ljxp->tags,
344 $ljxp->privacy,
345 $ljxp->comments,
346 $ljxp->post_type,
347 $ljxp->cut_text,
348 $ljxp->site_name,
349 $ljxp->custom_header,
350 $ljxp->custom_header_format,
351 $ljxp->uid
352 );
353 if ($ljxp->pass) {
354 db_query("UPDATE {ljxp} SET pass = '%s' WHERE uid = %d", md5($ljxp->pass), $ljxp->uid);
355 }
356 }
357 }
358
359 // --------------------------------------------------------------------------
360 // Actual cross-posting code
361
362 function ljxp_destination($ljxp) {
363 return $ljxp->host . '/interface/xmlrpc';
364 }
365
366 function ljxp_get_challenge(&$ljxp) {
367 $response = xmlrpc(ljxp_destination($ljxp), 'LJ.XMLRPC.getchallenge');
368 if (!$response) {
369 return false;
370 }
371 $ljxp->challenge = $response['challenge'];
372 return TRUE;
373 }
374
375 function ljxp_new_message(&$ljxp) {
376 if (!$ljxp->challenge) {
377 $result = ljxp_get_challenge($ljxp);
378 if (!$result) {
379 return false;
380 }
381 }
382
383 $message = array(
384 'username' => $ljxp->name,
385 'auth_method' => 'challenge',
386 'auth_challenge' => $ljxp->challenge,
387 'auth_response' => md5($ljxp->challenge . $ljxp->pass),
388 'ver' => '1', // Makes LJ expect UTF-8 text instead of ISO-8859-1
389 );
390 return $message;
391 }
392
393 function ljxp_post($node) {
394 $ljxp = ljxp_load_settings($node->uid);
395
396 $message = ljxp_new_message($ljxp);
397
398 if (!$ljxp->site_name) {
399 $ljxp->site_name = variable_get('site_name', 'drupal');
400 }
401
402 $dest = drupal_get_path_alias("node/$node->nid");
403 $comment_dest = drupal_get_path_alias("comment/reply/$node->nid");
404 if (!$ljxp->custom_header) {
405 $post_header = '<p style="border: 1px solid black; padding: 3px;"><b>';
406
407 $post_header .= t('Originally published at !link. ', array('!link' => l($ljxp->site_name, $dest, array('absolute'=>TRUE))));
408
409 if ($ljxp->comments) {
410 $post_header .= t('You can comment here or !link.', array('!link' => l(t('there'), $comment_dest, array('absolute'=>TRUE))));
411 }
412 else {
413 $post_header .= t('Please leave any !comments there.', array('!comments' => l(t('comments'), $comment_dest, array('absolute'=>TRUE))));
414 }
415 $post_header .= '</b></p>';
416 }
417 else {
418 $post_header = $ljxp->custom_header;
419
420 $find = array('%blog_name', '%permalink', '%comments_link');
421 $replace = array(
422 $ljxp->site_name,
423 $dest,
424 l(t('comment'), $comment_dest, array('absolute'=>TRUE))
425 );
426 $post_header = check_markup(str_replace($find, $replace, $post_header), $ljxp->custom_header_format, TRUE);
427 }
428
429 switch ($ljxp->post_type) {
430 case 'lj-cut':
431 $body = str_replace($node->teaser, '', $node->body);
432 $cut = $ljxp->cut_text ? ('<lj-cut text="' . $ljxp->cut_text . '">') : '<lj-cut>';
433 $message['event'] = check_markup($node->teaser, $node->format, TRUE) . $cut . check_markup($body, $node->format, true);
434 break;
435 case 'full post':
436 $message['event'] = check_markup($node->body, $node->format, TRUE);
437 break;
438 case 'teaser':
439 default:
440 $message['event'] = check_markup($node->teaser, $node->format, TRUE);
441 if (strlen($node->teaser < $node->body)) {
442 $message['event'] .= l(t('Read the rest of this post'), $dest, array('absolute' => TRUE));
443 }
444 break;
445 $message['event'] = check_markup($node->teaser, $node->format, TRUE);
446 }
447
448 $message['event'] = $post_header . $message['event'];
449
450 if ($ljxp->tags) {
451 $terms = taxonomy_node_get_terms($node);
452 foreach ($terms as $tid => $term) {
453 if ($tags) {
454 $tags .= ', ';
455 }
456 $tags .= check_plain($term->name);
457 }
458 }
459
460 $message['subject'] = check_plain($node->title);
461
462 $message['year'] = format_date($node->created, 'custom', 'Y');
463 $message['mon'] = format_date($node->created, 'custom', 'n');
464 $message['day'] = format_date($node->created, 'custom', 'j');
465 $message['hour'] = format_date($node->created, 'custom', 'G');
466 $message['min'] = format_date($node->created, 'custom', 'i');
467
468 $message['props'] = array("opt_nocomments" => !$ljxp->comments,
469 // Tells LJ to not run it's formatting (replacing \n
470 // with <br>, etc) because it's already been done by
471 // the texturization
472 "opt_preformatted" => true,
473 // backdate all posts -- this is really just me being lazy
474 // "opt_backdated" => TRUE,
475 );
476
477 // If tagging is enabled,
478 if ($tags) {
479 // Set tags
480 $message['props']['taglist'] = $tags;
481 }
482
483 // Set the privacy level according to the settings
484 switch ($ljxp->privacy) {
485 case "public":
486 $message['security'] = "public";
487 break;
488 case "friends":
489 $message['security'] = "usemask";
490 $message['allowmask'] = 1;
491 }
492
493 // If crossposting to a community, specify that
494 if($ljxp->community) {
495 $message['usejournal'] = $ljxp->community;
496 }
497
498 if ($node->ljid) {
499 // If there is, add the itemid attribute and change from posting to editing
500 $message['itemid'] = $node->ljid;
501 $method = 'LJ.XMLRPC.editevent';
502 }
503 else {
504 $method = 'LJ.XMLRPC.postevent';
505 }
506
507 if (!$response = xmlrpc(ljxp_destination($ljxp), $method, $message)) {
508 drupal_set_message(t('LiveJournal crossposting error: ') . xmlrpc_errno() . ': ' . xmlrpc_error_msg());
509 return FALSE;
510 }
511 else {
512 drupal_set_message(t('LiveJournal crosspost successful.'));
513 if (!$node->ljid) {
514 $node->ljid = $response['itemid'];
515 db_query("INSERT INTO {ljxp_node} (nid, ljid) VALUES (%d, %d)", $node->nid, $node->ljid);
516 }
517 return $node->ljid;
518 }
519 }
520
521 function ljxp_delete($node) {
522 $ljxp = ljxp_load_settings($node->uid);
523
524 $message = ljxp_new_message($ljxp);
525 $message['itemid'] = $node->ljid;
526 $message['event'] = "";
527 $message['subject'] = "Delete this entry";
528
529 if (!$response = xmlrpc(ljxp_destination($ljxp), 'LJ.XMLRPC.editevent', $message)) {
530 drupal_set_message(t('LiveJournal crossposting error: ') . xmlrpc_errno() . ': ' . xmlrpc_error_msg());
531 return FALSE;
532 }
533 else {
534 drupal_set_message(t('LiveJournal crosspost successful.'));
535 db_query("DELETE FROM {ljxp_node} WHERE nid = %d", $node->nid);
536 }
537 }

  ViewVC Help
Powered by ViewVC 1.1.2