Stripping CVS keywords
[project/multiping.git] / multiping.module
1 <?php
2
3 define("_multiping_when_active", 1);
4 define("_multiping_when_taxonomy", 2);
5 define("_multiping_when_nodetype", 4);
6 define("_multiping_when_frontpage", 8);
7
8
9 function _multiping_get_settings($defaults = FALSE) {
10 static $settings = NULL;
11 static $default_settings = array(
12 'global' => array(
13 'time_between_pings' => '10',
14 'pingatonce' => false,
15 ),
16 );
17 // Defaults requested?
18 if ($defaults) {
19 return $default_settings;
20 }
21 // merge settings from variable table with defaults
22 if ($settings == NULL) {
23 $settings = variable_get('multiping', array());
24 foreach ($default_settings as $key => $value) {
25 if (is_array($value)) {
26 $settings[$key] = isset($settings[$key]) ? array_merge($value, $settings[$key]) : $value;
27 }
28 else if (!isset($settings[$key])) {
29 $settings[$key] = $value;
30 }
31 }
32 }
33 // Return result
34 return $settings;
35 }
36
37
38 /**
39 * Implementation of hook_menu().
40 */
41 function multiping_menu() {
42 $items = array();
43 $items['admin/settings/multiping'] = array(
44 'title' => 'Ping services',
45 'description' => 'Configure when to ping which sites',
46 'page callback' => 'multiping_admin',
47 'page arguments' => array(),
48 'access callback' => 'user_access',
49 'access arguments' => array('admin pings'),
50 'type' => MENU_NORMAL_ITEM,
51 );
52 $items['admin/settings/multiping/pingall'] = array(
53 'title' => 'Ping all',
54 'page callback' => 'multiping_pingall',
55 'access arguments' => array('admin pings'),
56 'type' => MENU_NORMAL_ITEM,
57 );
58 $items['admin/settings/multiping/new'] = array(
59 'title' => 'New ping service',
60 'page callback' => 'drupal_get_form',
61 'page arguments' => array('multiping_edit_service', '0'),
62 'access arguments' => array('admin pings'),
63 'type' => MENU_CALLBACK,
64 );
65 $items['admin/settings/multiping/%/edit'] = array(
66 'title' => 'Edit ping service',
67 'page callback' => 'drupal_get_form',
68 'page arguments' => array('multiping_edit_service', 3),
69 'access arguments' => array('admin pings'),
70 'type' => MENU_CALLBACK,
71 );
72 $items['admin/settings/multiping/%/ping'] = array(
73 'title' => 'Ping service',
74 'page arguments' => array(3),
75 'page callback' => 'multiping_ping',
76 'access arguments' => array('admin pings'),
77 'type' => MENU_CALLBACK,
78 );
79 $items['admin/settings/multiping/%/delete'] = array(
80 'title' => 'Delete ping service',
81 'page arguments' => array(3),
82 'page callback' => 'multiping_delete',
83 'access arguments' => array('admin pings'),
84 'type' => MENU_CALLBACK,
85 );
86 return $items;
87 }
88
89 /**
90 * Implementation of hook_perm().
91 */
92 function multiping_perm() {
93 return array('admin pings');
94 }
95
96 function _multiping_doping($pingservice) {
97 global $base_url;
98
99 // Assemble site name
100 $name=variable_get('site_name', '');
101 $slogan=variable_get('site_slogan', '');
102 if (strlen($slogan)>0)
103 $name="$name - $slogan";
104 // Determine rss url
105 if (module_exists('taxonomy')
106 && ($pingservice->whentoping & _multiping_when_taxonomy)
107 && count($pingservice->voc)>0 && $pingservice->submitmainrss==0) {
108 // Taxonomy module exists, "ping only for nodes with taxonomy",
109 // Some taxonomy elements are actually selected, "main rss" override is off
110 $voc=unserialize($pingservice->voc);
111 $rss_url="$base_url/taxonomy/term/".implode("+", $voc)."/0/feed";
112 }
113 else {
114 $rss_url="$base_url/rss.xml";
115 }
116 // Ping! Check service method
117 if ($pingservice->method && strlen($pingservice->method)>0) {
118 //watchdog("Multiping","XML ping: ".$pingservice->url);
119 $result = xmlrpc($pingservice->url, $pingservice->method, $name, $base_url, $rss_url);
120 }
121 else {
122 $pingurl=strtr($pingservice->url, array(
123 "%name" => urlencode($name),
124 "%url" => urlencode("$base_url/"),
125 "%rss" => urlencode($rss_url))
126 );
127 $result = drupal_http_request($pingurl);
128 //watchdog("Multiping","Non-XML ping: ".$pingurl." Result: ".$result->code." Data: '".$result->data."'");
129 if ($result->code==200) {
130 if (strpos($result->data, "1")==0)
131 $result = TRUE; else
132 $result = FALSE;
133 }
134 else $result = FALSE;
135 }
136 // On success: Update timestamp
137 if ($result)
138 db_query("UPDATE {multiping} SET lastping=%d WHERE id=%d", time(), $pingservice->id);
139 return $result;
140 }
141
142
143 function _multiping_checkpings() {
144 $settings=_multiping_get_settings();
145 $result = db_query('SELECT * FROM {multiping}');
146 while ($row = db_fetch_object($result)) {
147 // Ping service active?
148 if (! ($row->whentoping & _multiping_when_active)) {
149 continue;
150 }
151 // Default query: Published nodes only
152 $sql_from="{node} n";
153 $sql_where="n.status>0";
154 // Taxonomy selection
155 if (module_exists('taxonomy') && ($row->whentoping & _multiping_when_taxonomy)) {
156 // Find most recent date for nodes with given taxonomy
157 $voc=unserialize($row->voc);
158 $sql_from="$sql_from, {term_node} t";
159 $sql_where="$sql_where AND t.nid=n.nid AND (t.tid=" . implode(" OR t.tid=", $voc) . ")";
160 }
161 // Nodetype selection
162 if ($row->whentoping & _multiping_when_nodetype) {
163 $nodetype=unserialize($row->nodetypes);
164 $sql_where="$sql_where AND (n.type='".implode("' OR n.type='", $nodetype)."')";
165 }
166 // Promoted to front page?
167 if ($row->whentoping & _multiping_when_frontpage) {
168 $sql_where="$sql_where AND n.promote>0";
169 }
170 //watchdog("debug","Multiping ".$row->name.": SELECT * FROM ".$sql_from." WHERE ".$sql_where);
171 // Get max creation and modification time
172 $max_created = 0;
173 $res_time = db_query('SELECT max(created) FROM {'.$sql_from.'} WHERE '.$sql_where);
174 $row_time = db_fetch_array($res_time);
175 if ($row_time) $max_created=$row_time['max(created)'];
176 $max_changed = 0;
177 $res_time = db_query('SELECT max(changed) FROM {'.$sql_from.'} WHERE '.$sql_where);
178 $row_time = db_fetch_array($res_time);
179 if ($row_time) $max_changed=$row_time['max(changed)'];
180 if ($max_created>$max_changed)
181 $lastmodified = $max_created; else
182 $lastmodified = $max_changed;
183 //watchdog("Multiping","lastmodified=".$lastmodified);
184 // Check wether to ping service
185 if ($lastmodified>$row->lastping) {
186 // With every failure, exponentially increase retry time
187 $retry_time=$settings['global']['time_between_pings']*pow(1.5,$row->failcount);
188 if ($retry_time>24*60) // Retry at least once a day
189 $retry_time=24*60;
190 if ($row->lastping+60*$retry_time>time()) {
191 // Timeout not yet reached
192 watchdog("Multiping", "Timeout for service @name not yet reached", array('@name' => $row->name));
193 } elseif (_multiping_doping($row)) {
194 watchdog("Multiping", 'Successfully notified %site.', array('%site' => $row->name));
195 db_query("UPDATE {multiping} SET failcount='0' WHERE id=%d",$row->id);
196 } else {
197 watchdog("Multiping", 'Failed to notify %site.', array('%site' => $row->name), WATCHDOG_WARNING);
198 db_query("UPDATE {multiping} SET failcount='%d' WHERE id=%d",
199 ($row->failcount)+1,$row->id);
200 }
201 }
202 }
203 }
204
205
206 /**
207 * Implementation of hook_cron().
208 * Check if sites need to be pinged.
209 */
210 function multiping_cron() {
211 watchdog("Multiping","Cron run",array(),WATCHDOG_DEBUG);
212 _multiping_checkpings();
213 }
214
215
216 /**
217 * Implementation of hook_nodeapi().
218 * If pingatonce is set, check pings.
219 */
220 function multiping_nodeapi(&$node, $op, $a3=NULL, $a4=NULL) {
221 //drupal_set_message("op=".$op);
222 //watchdog("Multiping","op=$op");
223 switch ($op) {
224 case 'insert':
225 case 'update':
226 $settings=_multiping_get_settings();
227 if ($settings['global']['pingatonce'])
228 _multiping_checkpings();
229 break;
230 }
231 }
232
233
234 function multiping_edit_service($form_state, $id) {
235 $edit=array();
236 $edit['id']=$id;
237 $edit['name']='';
238 $edit['url']='http://';
239 $edit['method']='weblogUpdates.ping';
240 $edit['when_active']=1;
241 $edit['when_taxonomy']=0;
242 $edit['when_nodetype']=0;
243 $edit['when_frontpage']=0;
244 $edit['submitmainrss']=1;
245 $edit['voc']='';
246 $edit['nodetype']='';
247 if ($id>0) {
248 $result = db_query('SELECT * FROM {multiping} WHERE id=%d',$id);
249 $row = db_fetch_object($result);
250 if ($row) {
251 $edit['name']=$row->name;
252 $edit['url']=$row->url;
253 $edit['method']=$row->method;
254 $edit['when_active']=(($row->whentoping & _multiping_when_active) > 0);
255 $edit['when_taxonomy']=(($row->whentoping & _multiping_when_taxonomy) > 0);
256 $edit['when_nodetype']=(($row->whentoping & _multiping_when_nodetype) > 0);
257 $edit['when_frontpage']=(($row->whentoping & _multiping_when_frontpage) > 0);
258 $edit['submitmainrss']=$row->submitmainrss;
259 $edit['voc']=unserialize($row->voc);
260 $edit['nodetype']=unserialize($row->nodetypes);
261 } else {
262 watchdog("Multiping","Query for id=@id returned 0 rows", array('@id' => $id), WATCHDOG_WARNING);
263 $id=0;
264 }
265 }
266 $form=array();
267 $form['id'] = array(
268 '#type' => 'hidden',
269 '#value' => $id,
270 );
271 $form['name'] = array(
272 '#type' => 'textfield',
273 '#title' => t('Site name'),
274 '#default_value' => $edit['name'],
275 '#size' => 50,
276 '#maxlength' => 100,
277 '#description' => t('Displayed name of the site to be pinged'),
278 '#attributes' => NULL,
279 '#required' => TRUE,
280 );
281 $form['url'] = array(
282 '#type' => 'textfield',
283 '#title' => t('URL'),
284 '#default_value' => $edit['url'],
285 '#size' => 50,
286 '#maxlength' => 255,
287 '#description' => t('URL of the ping service. '.
288 'The following replacements will be done:<br />'.
289 '%name - Site name (and slogan)<br />'.
290 '%url - Site base URL<br />'.
291 '%rss - URL of RSS feed'),
292 '#attributes' => NULL,
293 '#required' => TRUE,
294 );
295 /* Todo: Submit which RSS feed? Whole feed or a feed containing only
296 items of the selected taxonomies? */
297 $form['method'] = array(
298 '#type' => 'textfield',
299 '#title' => t('Method name'),
300 '#default_value' => $edit['method'],
301 '#size' => 50,
302 '#maxlength' => 50,
303 '#description' => t('If this is an XML service: Name of the method to be called'),
304 '#attributes' => NULL,
305 '#required' => FALSE,
306 );
307 $options = array('1' => t('Always ping'), '0' => t('Don\'t ping'));
308 if (module_exists('taxonomy')) {
309 $options['2']=t('Ping only for nodes with the following taxonomy');
310 }
311 $form['when_active'] = array(
312 '#type' => 'checkbox',
313 '#title' => t('Service active'),
314 '#default_value' => $edit['when_active'],
315 '#description' => t('Activate or deactivate this service'),
316 );
317 if (module_exists('taxonomy')) {
318 $form['when_taxonomy'] = array(
319 '#type' => 'checkbox',
320 '#title' => t('Ping only for selected taxonomies'),
321 '#default_value' => $edit['when_taxonomy'],
322 '#description' => t('Send a ping only if the node belongs to one of the follwing categories'),
323 );
324 $form['voc'] = array(
325 '#type' => 'select',
326 '#title' => t('Taxonomy terms when to ping'),
327 '#default_value' => $edit['voc'],
328 '#options' => taxonomy_form_all(),
329 '#multiple' => true,
330 '#description' => t('Ping this service if any of the taxonomy terms selected above matches the respective node'),
331 );
332 $form['submitmainrss'] = array(
333 '#type' => 'checkbox',
334 '#title' => t('Send main RSS feed in ping'),
335 '#default_value' => $edit['submitmainrss'],
336 '#description' => t('If checked, the main RSS feed is sent; otherwise, an RSS feed containing only articles from the selected taxonomies is used.'),
337 );
338 }
339 $form['when_nodetype'] = array(
340 '#type' => 'checkbox',
341 '#title' => t('Ping only for selected node types'),
342 '#default_value' => $edit['when_nodetype'],
343 '#description' => t('Send a ping only if the node is one of the following node types'),
344 );
345 $form['nodetype'] = array(
346 '#type' => 'select',
347 '#title' => t('Node types when to ping'),
348 '#default_value' => $edit['nodetype'],
349 '#options' => node_get_types('names'),
350 '#multiple' => true,
351 '#description' => t('Ping this service if any of the node types selected above matches the respective node'),
352 );
353 $form['when_frontpage'] = array(
354 '#type' => 'checkbox',
355 '#title' => t('Ping only for nodes promoted to front page'),
356 '#default_value' => $edit['when_frontpage'],
357 '#description' => t('Send a ping only if the node is promoted to the front page'),
358 );
359 $form['submit'] = array(
360 '#type' => 'submit',
361 '#value' => t('Submit'),
362 );
363 return $form;
364 }
365
366
367 function multiping_edit_service_submit($form_id, &$form_state) {
368 $edit = $form_state['values']; // TODO: Is this ok? http://drupal.org/node/144132#process-params
369 if ($edit['form_id']=='multiping_edit_service') {
370 $edit['id'] = ($edit['id'] && is_numeric($edit['id'])) ? $edit['id'] : 0;
371 $edit['name'] = ($edit['name']) ? $edit['name'] : '(no name)';
372 $edit['url'] = ($edit['url']) ? $edit['url'] : 'http://localhost/';
373 $edit['method'] = ($edit['method']) ? $edit['method'] : '';
374 $edit['submitmainrss'] = ($edit['submitmainrss']) ? $edit['submitmainrss'] : 0; // Not present if false!
375 if ($edit['submitmainrss']>0) $edit['submitmainrss']=1;
376 $whentoping=$edit['when_active']*_multiping_when_active+
377 $edit['when_taxonomy']*_multiping_when_taxonomy+
378 $edit['when_nodetype']*_multiping_when_nodetype+
379 $edit['when_frontpage']*_multiping_when_frontpage;
380 if ($edit['id']==0) {
381 db_query("INSERT INTO {multiping} (name,url,method,whentoping,submitmainrss,voc,nodetypes) ".
382 "VALUES ('%s','%s','%s','%s','%s','%s','%s')",$edit['name'],$edit['url'],
383 $edit['method'],$whentoping,$edit['submitmainrss'],
384 serialize($edit['voc']),serialize($edit['nodetype']));
385 } else {
386 db_query("UPDATE {multiping} SET name='%s',url='%s',method='%s',whentoping='%s',submitmainrss='%s',voc='%s',nodetypes='%s' ".
387 "WHERE id=%d",$edit['name'],$edit['url'],$edit['method'],$whentoping,$edit['submitmainrss'],serialize($edit['voc']),serialize($edit['nodetype']),$edit['id']);
388 }
389 }
390 drupal_set_message("Changes saved.");
391 #drupal_goto("admin/settings/multiping");
392 $form_state['redirect']="admin/settings/multiping";
393 }
394
395
396 function multiping_settings() {
397 $form=array();
398 $settings=_multiping_get_settings();
399 $form['global']['time_between_pings'] = array(
400 '#type' => 'textfield',
401 '#title' => t('Time between pings'),
402 '#default_value' => $settings['global']['time_between_pings'],
403 '#size' => 8,
404 '#maxlength' => 4,
405 '#description' => t('Minimum number of minutes between two pings to same site'),
406 '#attributes' => NULL,
407 '#required' => TRUE,
408 );
409 $form['pingatonce'] = array(
410 '#type' => 'checkbox',
411 '#title' => t('Ping after post'),
412 '#default_value' => $settings['global']['pingatonce'],
413 '#description' => t('Ping directly after post/update (instead of pinging during the cron job runs)'),
414 );
415 $form['submit'] = array(
416 '#type' => 'submit',
417 '#value' => t('Submit'),
418 );
419 return $form;
420 }
421
422
423 function multiping_settings_submit($form_id, &$form_state) {
424 $edit = $form_state['values']; // TODO: Is this ok? http://drupal.org/node/144132#process-params
425 if ($edit['form_id']=='multiping_settings') {
426 $settings=_multiping_get_settings();
427 $settings['global']['time_between_pings']=$edit['time_between_pings'];
428 $settings['global']['pingatonce']=$edit['pingatonce'];
429 variable_set('multiping', $settings);
430 }
431 drupal_set_message("Changes saved.");
432 #drupal_goto("admin/settings/multiping");
433 $form_state['redirect']="admin/settings/multiping";
434 }
435
436
437 /**
438 * Menu callback: Admin page
439 */
440 function multiping_admin() {
441 $output = "";
442 // General settings
443 $output .= drupal_get_form('multiping_settings', $form);
444 // Table with services
445 $header = array(t('Name'), t('Last update'), array('data' => t('Operations'), 'colspan' => '3'));
446 $result = db_query('SELECT * FROM {multiping} ORDER BY id');
447 while ($row = db_fetch_object($result)) {
448 if ($row->lastping==0)
449 $lastping=t('never'); else
450 $lastping=format_date($row->lastping,'small');
451 $rows[] = array(
452 $row->name,
453 $lastping,
454 l(t('edit'),"admin/settings/multiping/".$row->id."/edit"),
455 l(t('ping'),"admin/settings/multiping/".$row->id."/ping"),
456 l(t('delete'),"admin/settings/multiping/".$row->id."/delete")
457 );
458 }
459 $output .= theme('table',$header,$rows);
460 // Links
461 $output .= "<p>".l(t('Add service'),"admin/settings/multiping/new")."</p>";
462 return $output;
463 }
464
465
466 function multiping_pingall() {
467 $output = "<p>".t("Running all pings...")."</p>";
468 $header = array(t('Name'), t('Status'));
469 $result = db_query('SELECT * FROM {multiping} ORDER BY id');
470 while ($row = db_fetch_object($result)) {
471 $rows[] = array($row->name,_multiping_doping($row) ? t('Ok') : t('Failed'));
472 }
473 $output .= theme('table',$header,$rows);
474 $output .= "<p>".l(t('Return'),"admin/settings/multiping")."</p>";
475 return $output;
476 }
477
478
479 function multiping_ping($id) {
480 watchdog("Multiping","ping @id", array('@id' => $id));
481 if (!is_numeric($id)) {
482 drupal_not_found();
483 return;
484 }
485 drupal_set_message("Not yet implemented","warning");
486 drupal_goto("admin/settings/multiping");
487 }
488
489
490 function multiping_delete($id) {
491 watchdog("Multiping","delete @id", array('@id' => $id));
492 if (!is_numeric($id)) {
493 drupal_not_found();
494 return;
495 }
496 db_query("DELETE FROM {multiping} WHERE id=%d",intval($id));
497 drupal_set_message("Service deleted.");
498 drupal_goto("admin/settings/multiping");
499 }
500
501 ?>