summaryrefslogtreecommitdiffstats
path: root/multiping.module
blob: f50be5f6a4849626b8e9de316034b87cd57dec1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
<?php

/**
 * @file
 * Main file for the Multiping module, which allows pinging multiple sites.
 */

// HOOKS

/**
 * Implements hook_menu().
 */
function multiping_menu() {
  $items['admin/config/services/multiping'] = array(
    'title' => 'Multiping',
    'description' => 'Configure when to ping services.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('multiping_settings_form'),
    'access arguments' => array('administer multiping'),
  );
  $items['admin/config/services/multiping/settings'] = array(
    'title' => 'Settings',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 10,
  );
  return $items;
}

/**
 * Implements hook_ctools_plugin_type().
 */
function multiping_ctools_plugin_type() {
  return array(
    'ping_methods' => array(),
  );
}

/**
 * Implements hook_permission().
 */
function multiping_permission() {
  return array(
    'administer multiping' => array(
      'title' => t('Administer ping services'),
      'description' => t('Create, edit, delete, and perform ping for ping services.'),
    ),
  );
}

/**
 * Implements hook_cron_queue_info().
 */
function multiping_cron_queue_info() {
  $queues['multiping_items'] = array(
   'worker callback' => '_multiping_ping_queue_item',
   'time' => 60,
 );
 return $queues;
}

/**
 * Implements hook_node_insert().
 */
function multiping_node_insert($node) {
  multiping_queue_node($node, 'insert');
}

/**
 * Implements hook_node_update().
 */
function multiping_node_update($node) {
  multiping_queue_node($node, 'update');
}

// API

/**
 * Fetch metadata on a specific ping_method plugin.
 *
 * @param $ping_method
 *   Name of a ping method.
 *
 * @return
 *   An array with information about the requested ping method.
 */
function multiping_get_ping_method($ping_method) {
  ctools_include('plugins');
  return ctools_get_plugins('multiping', 'ping_methods', $ping_method);
}

/**
 * Fetch metadata for all ping_method plugins.
 *
 * @return
 *   An array of arrays with information about all available ping methods.
 */
function multiping_get_ping_methods() {
  ctools_include('plugins');
  return ctools_get_plugins('multiping', 'ping_methods');
}

function multiping_queue_node($node, $reason) {
  $to_queue = array();

  foreach (module_implements('multiping_queue_node') as $module) {
    $module_queue = module_invoke($module, 'multiping_queue_node', $node, $reason);

    $to_queue = array_merge($to_queue, array_map(function ($item) use ($module, $node, $reason) {
      $item += array(
        'module' => $module,
        'node' => $node->nid,
        'reason' => $reason,
      );
      return $item;
    }, $module_queue));
  }

  drupal_alter('multiping_queue_node', $to_queue);

  // If the settings say to ping at once rather than queue, then do so!
  if (variable_get('multiping_pingatonce', TRUE)) {
    foreach ($to_queue as $item) {
      _multiping_ping_queue_item($item);
    }
  } else {
    $queue = DrupalQueue::get('multiping_items');

    foreach ($to_queue as $item) {
      $queue->createItem($item);
    }
  }
}

// Internal methods

function _multiping_ping_queue_item($item) {
  // Fetch additional data

  if (is_numeric($item['node'])) {
    $item['node'] = node_load($item['node']);
  }

  // If the triggering module has been disabled or the node removed, just chill
  if (!module_exists($item['module']) || !$item['node']) {
    return;
  }

  $target = module_invoke($item['module'], 'multiping_target', $item, $item['node']);

  if ($target) {
    $item = array_merge($item, $target);

    unset($item['node']);
    $item['target'] = url($item['target'], array('absolute' => TRUE));

    multiping_ping_item($item);
  }
}

function multiping_ping_item($item, $force = FALSE) {
  // Avoid the same ping from being repeated within the same cron run

  $fingerprint = drupal_hash_base64(implode('::', array(
    $item['method'],
    $item['endpoint'],
    $item['target'],
  )));

  $last_ping = cache_get('multiping_ping::' . $fingerprint);
  $cron_last = variable_get('cron_last');

  if (!$force && $last_ping && $last_ping->data == $cron_last) {
    // We have already performed this ping this cron round
    return;
  }

  cache_set('multiping_ping::' . $fingerprint, $cron_last);

  // Do the ping

  $method = multiping_get_ping_method($item['method']);

  if ($method) {
    $result = call_user_func($method['ping callback'], $item);
  }

  $name = empty($item['name']) ? $item['endpoint'] : $item['name'];

  if (empty($result)) {
    watchdog('multiping', 'Failed to notify %site about %target.', array(
      '%site' => $name,
      '%target' => $item['target'],
    ), WATCHDOG_ERROR);
  } else {
    //TODO: This is _very_ verbose for larger sites! Find a better way!

    watchdog('multiping', 'Successfully notified %site about %target.', array(
      '%site' => $name,
      '%target' => $item['target'],
    ), WATCHDOG_INFO);
  }

  //TODO: Create a retry mechanism!
}

function multiping_cache_clear() {
  cache_clear_all('multiping', 'cache', TRUE);
}

function multiping_path_endpoints($path = NULL, $method = null) {
  if (!$path) {
    $path = current_path();
  }

  $router_item = menu_get_item($path);

  if (!$router_item) {
    return array();
  }

  $result = array();

  foreach (module_implements('multiping_path_endpoints') as $module) {
    $result = array_merge($result, module_invoke($module, 'multiping_path_endpoints', $router_item['path'], $method));
  }

  return $result;
}

function _multiping_get_terms($nid) {
  $terms = &drupal_static(__FUNCTION__);

  if (empty($terms)) {
    $terms = array();
  }

  if (!isset($terms[$nid])) {
     $terms[$nid] = db_query('SELECT tid FROM {taxonomy_index} WHERE nid = :nid', array(
         ':nid' => $nid
       ))->fetchCol();
  }

  return $terms[$nid];
}

function _multiping_get_vocabularies($nid) {
  $data = &drupal_static(__FUNCTION__);

  if (empty($data)) {
    $data = array();
  }

  if (!isset($data[$nid])) {
    $data[$nid] = db_query(
      'SELECT DISTINCT tv.vid, tv.machine_name FROM {taxonomy_vocabulary} tv ' .
      'INNER JOIN {taxonomy_term_data} td  ON td.vid = tv.vid ' .
      'INNER JOIN {taxonomy_index} ti ON ti.tid = td.tid ' .
      'WHERE ti.nid = :nid',
      array(':nid' => $nid)
    )->fetchAll(PDO::FETCH_ASSOC);
  }

  return $data[$nid];
}

function _multiping_module_invoke_until_non_null($hook) {
  $args = func_get_args();

  foreach (module_implements($hook) as $module) {
    $result = call_user_func_array('module_invoke', array_merge(array($module), $args));

    if ($result !== NULL) {
      return $result;
    }
  }
}

function multiping_settings_form($form, &$form_state) {
  $form['multiping_pingatonce'] = array(
    '#type' => 'checkbox',
    '#title' => t('Ping after post'),
    '#default_value' => variable_get('multiping_pingatonce', TRUE),
    '#description' => t('Ping directly after post/update (instead of pinging during the cron job runs). Use this if possible to avoid the added delay a cron job can result.'),
  );
  $form['multiping_timeout'] = array(
    '#type' => 'select',
    '#title' => t('Timeout for each ping'),
    '#default_value' => variable_get('multiping-timeout', 3),
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
    '#description' => t('Time to ping each service in seconds')
  );
  return system_settings_form($form);
}