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

Contents of /contributions/modules/simplenews/simplenews.module

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


Revision 1.217 - (show annotations) (download) (as text)
Sat Oct 24 16:49:18 2009 UTC (4 weeks, 3 days ago) by sutharsan
Branch: MAIN
Changes since 1.216: +2 -2 lines
File MIME type: text/x-php
#530232 by jamespharaoh: Fixed Do not allow an empty taxonomy term in the newsletter node form.
1 <?php
2 // $Id: simplenews.module,v 1.216 2009/10/24 16:30:09 sutharsan Exp $
3
4 /**
5 * @defgroup simplenews
6 * Enable nodes to be used as newsletter, manage subscriptions and sent
7 * email newsletter to subscribers.
8 */
9
10 /**
11 * @file
12 * Simplnews node handling, sent email, newsletter block and general hooks
13 *
14 * @ingroup simplenews
15 */
16
17 // TODO: in Drupal 7 replace time() by REQUEST_TIME (requires php 5.1.0)
18
19 /**
20 * NEWSLETTER MAIL PRIORITY
21 */
22 define('SIMPLENEWS_PRIORITY_NONE', 0);
23 define('SIMPLENEWS_PRIORITY_HIGHEST', 1);
24 define('SIMPLENEWS_PRIORITY_HIGH', 2);
25 define('SIMPLENEWS_PRIORITY_NORMAL', 3);
26 define('SIMPLENEWS_PRIORITY_LOW', 4);
27 define('SIMPLENEWS_PRIORITY_LOWEST', 5);
28
29 /**
30 * NEWSLETTER SEND COMMAND
31 */
32 define('SIMPLENEWS_COMMAND_SEND_NONE', 0);
33 define('SIMPLENEWS_COMMAND_SEND_NOW', 1);
34 define('SIMPLENEWS_COMMAND_SEND_TEST', 2);
35
36 /**
37 * NEWSLETTER SUBSCRIPTION STATUS
38 */
39 define('SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED', 1);
40 define('SIMPLENEWS_SUBSCRIPTION_STATUS_UNSUBSCRIBED', 0);
41
42 /**
43 * NEWSLETTER SEND STATUS
44 */
45 define('SIMPLENEWS_STATUS_SEND_NOT', 0);
46 define('SIMPLENEWS_STATUS_SEND_PENDING', 1);
47 define('SIMPLENEWS_STATUS_SEND_READY', 2);
48
49 /**
50 * MAIL SPOOL SEND STATUS
51 */
52 define('SIMPLENEWS_SPOOL_HOLD', 0);
53 define('SIMPLENEWS_SPOOL_PENDING', 1);
54 define('SIMPLENEWS_SPOOL_SEND', 2);
55
56 /**
57 * AFTER EACH 100 NEWSLETTERS
58 * simplenews_mail_spool() CHECKS IF LIMITS ARE EXCEEDED
59 */
60 define('SIMPLENEWS_SEND_CHECK_INTERVAL', 100);
61
62 /**
63 * AT 80% OF PHP MAX EXECUTION TIME EMAIL SENDING IS INTERRUPTED
64 */
65 define('SIMPLENEWS_SEND_TIME_LIMIT', 0.8);
66
67 /**
68 * Implementation of hook_perm().
69 */
70 function simplenews_perm() {
71 return array('administer newsletters', 'administer simplenews subscriptions', 'administer simplenews settings', 'send newsletter', 'subscribe to newsletters');
72 }
73
74 /**
75 * Implementation of hook_init().
76 */
77 function simplenews_init() {
78 drupal_add_css(drupal_get_path('module', 'simplenews') .'/simplenews.css', 'module', 'all', TRUE);
79
80 // Simplenews can not work without this variable.
81 if ((variable_get('simplenews_vid', '')) == '') {
82 drupal_set_message(t('Missing newsletter vocabulary. Please set a vocabulary at <a href="@settings">Simplenews settings</a>.', array('@settings' => url('admin/settings/simplenews/general'))), 'error');
83 }
84
85 }
86
87 /**
88 * Implementation of hook_menu().
89 */
90 function simplenews_menu() {
91 $items['admin/content/simplenews'] = array(
92 'title' => 'Newsletters',
93 'description' => 'Manage newsletters and subscriptions.',
94 'type' => MENU_NORMAL_ITEM,
95 'page callback' => 'system_admin_menu_block_page',
96 'access callback' => 'simplenews_newsletter_access',
97 'file' => 'system.admin.inc',
98 'file path' => drupal_get_path('module', 'system'),
99 );
100 $items['admin/content/simplenews/sent'] = array(
101 'title' => 'Newsletter issues',
102 'description' => 'List of newsletter issues.',
103 'type' => MENU_NORMAL_ITEM,
104 'page callback' => 'drupal_get_form',
105 'page arguments' => array('simplenews_admin_news'),
106 'access arguments' => array('administer newsletters'),
107 'file' => 'simplenews.admin.inc',
108 'weight' => -10,
109 );
110
111 $items['admin/content/simplenews/types'] = array(
112 'title' => 'Newsletters',
113 'description' => 'List, add and edit newsletter series.',
114 'type' => MENU_NORMAL_ITEM,
115 'page callback' => 'simplenews_types_overview',
116 'access arguments' => array('administer newsletters'),
117 'file' => 'simplenews.admin.inc',
118 'weight' => -8,
119 );
120 $items['admin/content/simplenews/types/edit/%'] = array(
121 'title' => 'Newsletters',
122 'type' => MENU_CALLBACK,
123 'page callback' => 'drupal_get_form',
124 'page arguments' => array('simplenews_admin_types_form', 5),
125 'access arguments' => array('administer newsletters'),
126 'file' => 'simplenews.admin.inc',
127 );
128 $items['admin/content/simplenews/types/delete/%'] = array(
129 'title' => 'Newsletters',
130 'type' => MENU_CALLBACK,
131 'page callback' => 'drupal_get_form',
132 'page arguments' => array('simplenews_admin_types_delete', 5),
133 'access arguments' => array('administer newsletters'),
134 'file' => 'simplenews.admin.inc',
135 );
136 $items['admin/content/simplenews/types/list'] = array(
137 'title' => 'List newsletters',
138 'type' => MENU_DEFAULT_LOCAL_TASK,
139 'weight' => -10,
140 );
141 $items['admin/content/simplenews/types/add'] = array(
142 'title' => 'Add newsletter',
143 'type' => MENU_LOCAL_TASK,
144 'page callback' => 'drupal_get_form',
145 'page arguments' => array('simplenews_admin_types_form'),
146 'access arguments' => array('administer newsletters'),
147 'file' => 'simplenews.admin.inc',
148 'weight' => -9,
149 );
150
151 $items['admin/content/simplenews/subscriptions/delete'] = array(
152 'title' => 'Delete',
153 'type' => MENU_CALLBACK,
154 'page callback' => 'drupal_get_form',
155 'page arguments' => array('simplenews_subscription_multiple_delete_confirm'),
156 'access arguments' => array('administer simplenews subscriptions'),
157 'file' => 'simplenews.admin.inc',
158 );
159 $items['admin/content/simplenews/users'] = array(
160 'title' => 'Subscriptions',
161 'description' => 'Newsletter subscription management.',
162 'type' => MENU_NORMAL_ITEM,
163 'page callback' => 'drupal_get_form',
164 'page arguments' => array('simplenews_subscription_admin'),
165 'access arguments' => array('administer simplenews subscriptions'),
166 'file' => 'simplenews.admin.inc',
167 'weight' => -7,
168 );
169 $items['admin/content/simplenews/users/edit/%'] = array(
170 'title' => 'Subscriptions',
171 'type' => MENU_CALLBACK,
172 'page callback' => 'drupal_get_form',
173 'page arguments' => array('simplenews_admin_users_form', 5),
174 'access arguments' => array('administer simplenews subscriptions'),
175 'file' => 'simplenews.subscription.inc',
176 );
177 $items['admin/content/simplenews/users/list'] = array(
178 'title' => 'List',
179 'type' => MENU_DEFAULT_LOCAL_TASK,
180 'weight' => -10,
181 );
182 $items['admin/content/simplenews/users/import'] = array(
183 'title' => 'Mass subscribe',
184 'type' => MENU_LOCAL_TASK,
185 'page callback' => 'drupal_get_form',
186 'page arguments' => array('simplenews_subscription_list_add'),
187 'access arguments' => array('administer simplenews subscriptions'),
188 'file' => 'simplenews.admin.inc',
189 'weight' => -9,
190 );
191 $items['admin/content/simplenews/users/export'] = array(
192 'title' => 'Export',
193 'type' => MENU_LOCAL_TASK,
194 'page callback' => 'drupal_get_form',
195 'page arguments' => array('simplenews_subscription_list_export'),
196 'access arguments' => array('administer simplenews subscriptions'),
197 'file' => 'simplenews.admin.inc',
198 'weight' => -7,
199 );
200 $items['admin/content/simplenews/users/unsubscribe'] = array(
201 'title' => 'Mass unsubscribe',
202 'type' => MENU_LOCAL_TASK,
203 'page callback' => 'drupal_get_form',
204 'page arguments' => array('simplenews_subscription_list_remove'),
205 'access arguments' => array('administer simplenews subscriptions'),
206 'file' => 'simplenews.admin.inc',
207 'weight' => -8,
208 );
209
210 $items['admin/settings/simplenews'] = array(
211 'title' => 'Simplenews',
212 'description' => 'Manage simplenews configuration.',
213 'type' => MENU_NORMAL_ITEM,
214 'page callback' => 'system_admin_menu_block_page',
215 'access arguments' => array('administer simplenews settings'),
216 'file' => 'system.admin.inc',
217 'file path' => drupal_get_path('module', 'system'),
218 );
219
220 $items['admin/settings/simplenews/general'] = array(
221 'title' => 'General',
222 'description' => 'Simplenews content type and vocabulary settings.',
223 'type' => MENU_NORMAL_ITEM,
224 'page callback' => 'drupal_get_form',
225 'page arguments' => array('simplenews_admin_settings'),
226 'access arguments' => array('administer simplenews settings'),
227 'file' => 'simplenews.admin.inc',
228 'weight' => -10,
229 );
230 $items['admin/settings/simplenews/newsletter'] = array(
231 'title' => 'Newsletter',
232 'description' => 'Newsletter default settings and sender data.',
233 'type' => MENU_NORMAL_ITEM,
234 'page callback' => 'drupal_get_form',
235 'page arguments' => array('simplenews_admin_settings_newsletter'),
236 'access arguments' => array('administer simplenews settings'),
237 'file' => 'simplenews.admin.inc',
238 'weight' => -9,
239 );
240 $items['admin/settings/simplenews/subscription'] = array(
241 'title' => 'Subscription',
242 'description' => 'Subscription settings, opt-in/out confirmation email text.',
243 'type' => MENU_NORMAL_ITEM,
244 'page callback' => 'drupal_get_form',
245 'page arguments' => array('simplenews_admin_settings_subscription'),
246 'access arguments' => array('administer simplenews settings'),
247 'file' => 'simplenews.admin.inc',
248 'weight' => -8,
249 );
250 $items['admin/settings/simplenews/mail'] = array(
251 'title' => 'Send mail',
252 'description' => 'Send mail, cron and debug options.',
253 'type' => MENU_NORMAL_ITEM,
254 'page callback' => 'drupal_get_form',
255 'page arguments' => array('simplenews_admin_settings_mail'),
256 'access arguments' => array('administer simplenews settings'),
257 'file' => 'simplenews.admin.inc',
258 'weight' => -7,
259 );
260
261 $items['newsletter/confirm'] = array(
262 'title' => 'Confirm newsletter subscriptions',
263 'type' => MENU_CALLBACK,
264 'page callback' => 'simplenews_confirm_subscription',
265 'access arguments' => array('subscribe to newsletters'),
266 'file' => 'simplenews.subscription.inc',
267 );
268
269 $items['newsletter/subscriptions'] = array(
270 'title' => 'Manage newsletter subscriptions',
271 'type' => MENU_CALLBACK,
272 'page callback' => 'drupal_get_form',
273 'page arguments' => array('simplenews_subscription_manager_form'),
274 'access arguments' => array('subscribe to newsletters'),
275 'file' => 'simplenews.subscription.inc',
276 );
277
278 return $items;
279 }
280
281 /**
282 * Menu item access callback.
283 *
284 * Access for both newsletter and subscriber admins.
285 */
286 function simplenews_newsletter_access() {
287 return user_access('administer newsletters') || user_access('administer simplenews subscriptions');
288 }
289
290 /**
291 * Implementation of hook_node_type().
292 *
293 * Update 'simplenews_content_types' on node update and delete.
294 * Related vocabulary settings are updated by taxonomy module.
295 */
296 function simplenews_node_type($op, $info) {
297 switch ($op) {
298 case 'delete':
299 $simplenews_types = variable_get('simplenews_content_types', array());
300 if (isset($simplenews_types[$info->type])) {
301 unset($simplenews_types[$info->type]);
302 variable_set('simplenews_content_types', $simplenews_types);
303 }
304 break;
305 case 'update':
306 $simplenews_types = variable_get('simplenews_content_types', array());
307 if (isset($info->old_type) && isset($simplenews_types[$info->old_type]) && $info->old_type != $info->type) {
308 unset($simplenews_types[$info->old_type]);
309 $simplenews_types[$info->type] = $info->type;
310 variable_set('simplenews_content_types', $simplenews_types);
311 }
312 break;
313 }
314 }
315
316 /**
317 * Implementation of hook_nodeapi().
318 */
319 function simplenews_nodeapi(&$node, $op, $teaser, $page) {
320 global $user;
321
322 // Operate only on node types set in 'simplenews_content_types' variable.
323 if (!in_array($node->type, variable_get('simplenews_content_types', array('simplenews')))) {
324 return;
325 }
326
327 switch ($op) {
328 case 'alter':
329 // Don't replace the tokens when node alter is called by simplenews_mail.
330 if (!isset($node->simplenews_mail)) {
331 global $language;
332 $context['node'] = $node;
333
334 if (isset($node->body)) {
335 $node->body = token_replace($node->body, 'simplenews', array('node' => $node));
336 }
337 if (isset($node->teaser)) {
338 $node->teaser = token_replace($node->teaser, 'simplenews', array('node' => $node));
339 }
340 }
341 break;
342 case 'validate':
343 $vid = variable_get('simplenews_vid', '');
344 if (!isset($node->taxonomy[$vid]) || empty($node->taxonomy[$vid]) || simplenews_validate_taxonomy($node->taxonomy) == FALSE) {
345 form_set_error('taxonomy', t('No newsletter term is selected, the newsletter taxonomy term is probably not configured correctly.<br /> Check and <strong>save</strong> the <a href="@settings">Simplenews general settings</a>.', array('%name' => $vocabulary->name, '@settings' => url('admin/settings/simplenews/general'))));
346 }
347 elseif (isset($node->simplenews['send']) && $node->simplenews['send'] == SIMPLENEWS_COMMAND_SEND_TEST) {
348 if (!empty($node->simplenews['test_address'])) {
349 $mails = explode(',', $node->simplenews['test_address']);
350 foreach ($mails as $mail) {
351 $mail = trim($mail);
352 if ($mail == '') {
353 form_set_error('simplenews][test_address', t('Test email address is empty.'));
354 }
355 elseif (!valid_email_address($mail)) {
356 form_set_error('simplenews][test_address', t('Invalid email address %mail.', array('%mail' => $mail)));
357 }
358 }
359 }
360 else {
361 form_set_error('simplenews][test_address', t('Missing test email address.'));
362 }
363 }
364 break;
365 case 'presave':
366 $node->simplenews = array_merge(_simplenews_get_node_form_defaults(), is_array($node->simplenews) ? $node->simplenews : array());
367 $term = simplenews_validate_taxonomy($node->taxonomy);
368 $tid = is_array($term) ? array_values($term) : FALSE;
369 $node->simplenews['tid'] = $tid ? $tid[0] : 0;
370 break;
371 case 'insert':
372 case 'update':
373 // Flatten the simplenews 'advanced' settings array.
374 // This makes sure the $node array after insert/update is equal to the
375 // $node array after load.
376 $node->simplenews = _simplenews_flatten_array($node->simplenews);
377
378 $send_with_permission = $node->simplenews['send'] == SIMPLENEWS_COMMAND_SEND_NOW && user_access('send newsletter');
379 if ($op == 'insert') {
380 // Insert node
381 $s_status = $send_with_permission ? SIMPLENEWS_STATUS_SEND_PENDING : SIMPLENEWS_STATUS_SEND_NOT;
382 db_query("INSERT INTO {simplenews_newsletters} (nid, vid, tid, s_status, s_format, priority, receipt)
383 VALUES (%d, %d, %d, %d, '%s', %d, %d)", $node->nid, $node->vid, $node->simplenews['tid'], $s_status, $node->simplenews['s_format'], $node->simplenews['priority'], $node->simplenews['receipt']);
384 }
385 else {
386 // Update node
387 if ($send_with_permission) {
388 db_query("UPDATE {simplenews_newsletters} SET vid = %d, tid = %d, s_status = %d, s_format = '%s', priority = %d, receipt = %d
389 WHERE nid = %d", $node->vid, $node->simplenews['tid'], SIMPLENEWS_STATUS_SEND_PENDING, $node->simplenews['s_format'], $node->simplenews['priority'], $node->simplenews['receipt'], $node->nid);
390 }
391 else {
392 db_query("UPDATE {simplenews_newsletters} SET tid = %d, s_format = '%s', priority = %d, receipt = %d
393 WHERE nid = %d", $node->simplenews['tid'], $node->simplenews['s_format'], $node->simplenews['priority'], $node->simplenews['receipt'], $node->nid);
394 }
395 }
396
397 // When this node is selected for translation all translation of this node
398 // will be send too.
399 // All translated nodes will receive the same send states (priority, confirmation, format).
400 if (module_exists('translation') && translation_supported_type($node->type) && $send_with_permission) {
401 if ($translations = translation_node_get_translations($node->tnid)) {
402 foreach ($translations as $translation) {
403 db_query("UPDATE {simplenews_newsletters} SET s_status = %d, s_format = '%s', priority = %d, receipt = %d
404 WHERE nid = %d",
405 SIMPLENEWS_STATUS_SEND_PENDING, $node->simplenews['s_format'], $node->simplenews['priority'],
406 $node->simplenews['receipt'], $translation->nid);
407 }
408 }
409 }
410
411 // Send newsletter or test newsletter
412 if ($send_with_permission) {
413 // Send newsletter to all subscribers
414 simplenews_send_node($node);
415 }
416 elseif ($node->simplenews['send'] == SIMPLENEWS_COMMAND_SEND_TEST) {
417 // Send test newsletter to test address(es)
418 simplenews_send_test($node);
419 }
420 break;
421 case 'delete':
422 $result = db_query('DELETE FROM {simplenews_newsletters} WHERE nid = %d', $node->nid);
423 if ($result) {
424 drupal_set_message(t('Newsletter %title was deleted.', array('%title' => $node->title)));
425 }
426 break;
427 case 'load':
428 $node->simplenews = db_fetch_array(db_query('SELECT * FROM {simplenews_newsletters} WHERE nid = %d', $node->nid));
429 break;
430 }
431 }
432
433 /**
434 * Validate if selected terms are Newsletter taxonomy terms.
435 *
436 * @param array $taxonomy Taxonomy form array of newsletter node.
437 *
438 * @return
439 * Array of selected Newsletter terms. Example: array(4, 12)
440 * FALSE: no Newsletter term is selected
441 *
442 * NOTE: This function can not handle free tagging tags.
443 * In case of free tagging see taxonomy_node_save() for example code.
444 * Note that free tagging can create new terms at node add/edit. This
445 * contradicts with the current set-up of simpelnews.
446 */
447 function simplenews_validate_taxonomy($taxonomy) {
448 // Get newsletter tids.
449 $vid = variable_get('simplenews_vid', '');
450 $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
451 while ($tid = db_fetch_object($result)) {
452 $newsletter_tids[] = $tid->tid;
453 }
454 // Extract selected tid's from the taxonomy form.
455 if (isset($newsletter_tids) && !empty($taxonomy)) {
456 $selected_terms = array();
457 if (is_array($taxonomy)) {
458 foreach ($taxonomy as $term) {
459 if (is_array($term)) {
460 foreach ($term as $tid) {
461 if ($tid) {
462 $selected_terms[] = $tid;
463 }
464 }
465 }
466 else if (is_object($term)) {
467 $selected_terms[] = $term->tid;
468 }
469 else if ($term) {
470 $selected_terms[] = $term;
471 }
472 }
473 }
474 // Compare selected tid's and newsletter tid's.
475 $valid_terms = array_intersect($newsletter_tids, $selected_terms);
476 return empty($valid_terms) ? FALSE : $valid_terms;
477 }
478
479 return FALSE;
480 }
481
482 /**
483 * Implementation of hook_form_alter().
484 */
485 function simplenews_form_alter(&$form, $form_state, $form_id) {
486 $vid = variable_get('simplenews_vid', '');
487
488 // Newsletter vocabulary form
489 if ($form_id == 'taxonomy_form_vocabulary' && isset($form['vid']) && $form['vid']['#value'] == $vid) {
490 // Hide critical options from newsletter vocabulary.
491 $form['help_simplenews_vocab'] = array(
492 '#value' => t('This is the designated simplenews vocabulary.'),
493 '#weight' => -1,
494 );
495 // We display the current content type settings in a disabled form element
496 // to the user. The real value passed in the form separately because
497 // disabled elements do not get saved at submit.
498 $form['content_types']['display_only'] = $form['content_types']['nodes'];
499 $form['content_types']['display_only']['#disabled'] = TRUE;
500 $form['content_types']['display_only']['#description'] = t('These content type(s) are used as newsletter. They can be set in !simplenews_settings.', array('!simplenews_settings' => l('Simplenews settings', 'admin/settings/simplenews')));
501 $form['content_types']['nodes'] = array(
502 '#type' => 'value',
503 '#value' => $form['content_types']['nodes']['#default_value'],
504 );
505 // Free tagging can not be allowed see: simplenews_validate_taxonomy().
506 $form['settings']['tags'] = array('#type' => 'value', '#value' => FALSE);
507 // Multiple select does not work with simplenews.
508 $form['settings']['multiple'] = array('#type' => 'value', '#value' => FALSE);
509 $form['settings']['required'] = array('#type' => 'value', '#value' => TRUE);
510 }
511
512 // Simplenews newsletter node form
513 elseif (strpos($form_id, '_node_form')) {
514 if (in_array($form['type']['#value'], variable_get('simplenews_content_types', array('simplenews')))) {
515
516 // Available variables are based on user_mail_tokens().
517 // But uses only those which can be used with uid = 0 since simplenews also sends to anonymous users.
518 if (isset($form['body_field'])) {
519 $form['body_field']['body']['#description'] = t("This will be the body of your newsletter. See 'Replacement patterns' for available variables.)");
520 }
521 $form['simplenews_subscription']['subscription_mail']['token_help'] = array(
522 '#title' => t('Replacement patterns'),
523 '#type' => 'fieldset',
524 '#collapsible' => TRUE,
525 '#collapsed' => TRUE,
526 '#weight' => -20,
527 '#description' => t('These tokens can be used in all text fields and will be replaced on-screen and in the email. Note that simplenews-receiver tokens are not suitable for on-screen use.')
528 );
529 $form['simplenews_subscription']['subscription_mail']['token_help']['help'] = array(
530 '#value' => theme('token_help', 'simplenews'),
531 );
532
533 if (isset($form['#node']->simplenews)) {
534 $simplenews_values = $form['#node']->simplenews;
535 }
536 $vocabulary = taxonomy_vocabulary_load(variable_get('simplenews_vid', ''));
537 if (!empty($vocabulary) && !isset($vocabulary->nodes[$form['type']['#value']])) {
538 drupal_set_message(t('Invalid vocabulary setting detected. Check and <strong>save</strong> the <a href="@settings">Simplenews general settings</a>.', array('%name' => $vocabulary->name, '@settings' => url('admin/settings/simplenews'))), 'error');
539 }
540 $form['simplenews'] = array(
541 '#type' => 'fieldset',
542 '#title' => t('Send newsletter'),
543 '#collapsible' => TRUE,
544 '#collapsed' => FALSE,
545 '#tree' => TRUE,
546 );
547
548 // Translations of newsletters don't have send and format options. Only the
549 // translation source (and non translated) newsletters will get these options.
550 if (module_exists('translation') && translation_supported_type($form['#node']->type) &&
551 (isset($form['#node']->translation_source) || ($form['#node']->tnid && $form['#node']->tnid != $form['#node']->nid))) {
552 $form['simplenews']['#description'] = t('This newsletter issue is part of a translation set. Sending this set is controlled from the <a href="@link">translation source newsletter</a>.', array('@link' => '/node/'. $form['tnid']['#value']));
553 // Send option of translated newsletters are not used, but a default is
554 // required to prevent errors when data is stored in the database.
555 $form['simplenews']['send'] = array('#type' => 'hidden', '#value' => SIMPLENEWS_COMMAND_SEND_NONE);
556 $form['simplenews']['advanced']['s_format'] = array('#type' => 'hidden', '#value' => variable_get('simplenews_format', 'plain'));
557 $form['simplenews']['advanced']['priority'] = array('#type' => 'hidden', '#value' => variable_get('simplenews_priority', SIMPLENEWS_PRIORITY_NONE));
558 $form['simplenews']['advanced']['receipt'] = array('#type' => 'hidden', '#value' => variable_get('simplenews_receipt', 0));
559 }
560 else {
561 // Show newsletter sending options if newsletter has not been send yet.
562 // If send a nodification is shown.
563 if (!isset($simplenews_values['s_status']) || (isset($simplenews_values['s_status']) && $simplenews_values['s_status'] == SIMPLENEWS_STATUS_SEND_NOT)) {
564
565 // Add dynamic text for send button.
566 drupal_add_js(drupal_get_path('module', 'simplenews') .'/simplenews.js', 'module');
567
568 if (user_access('send newsletter')) {
569 $options[SIMPLENEWS_COMMAND_SEND_NONE] = t("Don't send now");
570 $options[SIMPLENEWS_COMMAND_SEND_TEST] = t('Send one test newsletter to the test address');
571 $options[SIMPLENEWS_COMMAND_SEND_NOW] = t('Send newsletter');
572 $form['simplenews']['send'] = array(
573 '#type' => 'radios',
574 '#title' => t('Send action'),
575 '#default_value' => isset($simplenews_values['send']) ? $simplenews_values['send'] : variable_get('simplenews_send', SIMPLENEWS_COMMAND_SEND_NONE),
576 '#options' => $options,
577 '#attributes' => array('class' => 'simplenews-command-send'),
578 );
579 }
580 else {
581 $options[SIMPLENEWS_COMMAND_SEND_NONE] = t("Don't send now");
582 $options[SIMPLENEWS_COMMAND_SEND_TEST] = t('Send one test newsletter to the test address');
583 $form['simplenews']['send'] = array(
584 '#type' => 'radios',
585 '#title' => t('Sending'),
586 '#default_value' => isset($simplenews_values['send']) ? $simplenews_values['send'] : SIMPLENEWS_COMMAND_SEND_NONE,
587 '#options' => $options,
588 '#description' => t('You have no privileges to send this newsletter'),
589 '#attributes' => array('class' => 'simplenews-command-send'),
590 );
591 }
592
593 $address_default = variable_get('site_mail', ini_get('sendmail_from'));
594 if (variable_get('simplenews_test_address_override', 0)) {
595 $form['simplenews']['test_address'] = array(
596 '#type' => 'textfield',
597 '#title' => t('Test email addresses'),
598 '#description' => t('Supply a comma-separated list of email addresses to be used as test addresses.'),
599 '#default_value' => isset($simplenews_values['test_address']) ? $simplenews_values['test_address'] : variable_get('simplenews_test_address', $address_default),
600 '#size' => 60,
601 '#maxlength' => 128,
602 );
603 }
604 else {
605 $form['simplenews']['test_address'] = array(
606 '#type' => 'hidden',
607 '#value' => variable_get('simplenews_test_address', $address_default),
608 );
609 }
610
611 $form['simplenews']['advanced'] = array(
612 '#type' => 'fieldset',
613 '#title' => t('Email options'),
614 '#collapsible' => TRUE,
615 '#collapsed' => TRUE,
616 );
617 // Hide format selection if there is nothing to choose.
618 // The default format is plain text.
619 $format_options = _simplenews_format_options();
620 if (count($format_options) > 1) {
621 $form['simplenews']['advanced']['s_format'] = array(
622 '#type' => 'select',
623 '#title' => t('Email format'),
624 '#default_value' => isset($simplenews_values['s_format']) ? $simplenews_values['s_format'] : variable_get('simplenews_format', 'plain'),
625 '#options' => _simplenews_format_options(),
626 );
627 }
628 else {
629 $form['simplenews']['advanced']['s_format'] = array(
630 '#type' => 'hidden',
631 '#value' => key($format_options),
632 );
633 }
634
635 $form['simplenews']['advanced']['priority'] = array(
636 '#type' => 'select',
637 '#title' => t('Email priority'),
638 '#default_value' => isset($simplenews_values['priority']) ? $simplenews_values['priority'] : variable_get('simplenews_priority', SIMPLENEWS_PRIORITY_NONE),
639 '#options' => array(
640 SIMPLENEWS_PRIORITY_NONE => t('none'),
641 SIMPLENEWS_PRIORITY_HIGHEST => t('highest'),
642 SIMPLENEWS_PRIORITY_HIGH => t('high'),
643 SIMPLENEWS_PRIORITY_NORMAL => t('normal'),
644 SIMPLENEWS_PRIORITY_LOW => t('low'),
645 SIMPLENEWS_PRIORITY_LOWEST => t('lowest'),
646 ),
647 );
648 $form['simplenews']['advanced']['receipt'] = array(
649 '#type' => 'checkbox',
650 '#title' => t('Request receipt'),
651 '#return_value' => 1,
652 '#default_value' => isset($simplenews_values['receipt']) ? $simplenews_values['receipt'] : variable_get('simplenews_receipt', 0),
653 );
654 }
655 else {
656 $form['simplenews']['none'] = array(
657 '#type' => 'checkbox',
658 '#title' => t('This newsletter has been sent'),
659 '#return_value' => 0,
660 '#attributes' => array('checked' => 'checked', 'disabled' => 'disabled'),
661 );
662 $form['simplenews']['advanced']['s_format'] = array('#type' => 'hidden', '#value' => $simplenews_values['s_format']);
663 $form['simplenews']['advanced']['priority'] = array('#type' => 'hidden', '#value' => $simplenews_values['priority']);
664 $form['simplenews']['advanced']['receipt'] = array('#type' => 'hidden', '#value' => $simplenews_values['receipt']);
665 }
666 }
667 $form['simplenews']['s_status'] = array(
668 '#type' => 'hidden',
669 '#value' => isset($simplenews_values['s_status']) ? $simplenews_values['s_status'] : SIMPLENEWS_STATUS_SEND_NOT,
670 );
671 }
672 }
673 }
674
675 /**
676 * Implementation of hook_cron().
677 */
678 function simplenews_cron() {
679 simplenews_mail_spool();
680 simplenews_clear_spool();
681 }
682
683 /**
684 * Implementation of hook_taxonomy().
685 *
686 * Deletes subscriptions to term when term is deleted, and cleans the blocks
687 * table.
688 */
689 function simplenews_taxonomy($op, $type, $term = NULL) {
690 if ($op == 'delete' && $term['vid'] == variable_get('simplenews_vid', '')) {
691 switch ($type) {
692 case 'term':
693 db_query('DELETE FROM {simplenews_snid_tid} WHERE tid = %d', $term['tid']);
694 db_query("DELETE FROM {blocks} WHERE module = '%s' AND delta = '%s'", 'simplenews', $term['tid']);
695 drupal_set_message(t('All subscriptions to newsletter %newsletter have been deleted.', array('%newsletter' => $term['name'])));
696 break;
697 }
698 }
699 }
700
701 /**
702 * Implementation of hook_user().
703 *
704 * Checks whether an email address is subscribed to the newsletter when a new
705 * user signs up. If so, changes uid from 0 to the new uid in
706 * simplenews_subscriptions so that the user's subscription status is known when
707 * he logs in.
708 */
709 function simplenews_user($op, &$edit, &$account, $category = NULL) {
710 switch ($op) {
711 case 'insert':
712 if ($edit['mail']) {
713 $query = "SELECT snid FROM {simplenews_subscriptions} WHERE mail = '%s'";
714 if ($result = db_fetch_object(db_query($query, $edit['mail']))) {
715 db_query("UPDATE {simplenews_subscriptions} SET uid = %d, language = '%s' WHERE snid = %d", $edit['uid'], $edit['language'], $result->snid);
716 }
717 }
718 break;
719 case 'update':
720 if ($category == 'account' && $edit['mail']) {
721 $query = "SELECT snid FROM {simplenews_subscriptions} WHERE uid = %d";
722 if ($result = db_fetch_object(db_query($query, $account->uid))) {
723 db_query("DELETE FROM {simplenews_subscriptions} WHERE mail = '%s' AND uid = %d", $edit['mail'], 0);
724 db_query("UPDATE {simplenews_subscriptions} SET mail = '%s', language = '%s' WHERE snid = %d", $edit['mail'], $edit['language'], $result->snid);
725 }
726 else {
727 $query = "SELECT snid FROM {simplenews_subscriptions} WHERE mail = '%s'";
728 if ($result = db_fetch_object(db_query($query, $edit['mail']))) {
729 db_query("UPDATE {simplenews_subscriptions} SET uid = %d, language = '%s' WHERE snid = %d", $account->uid, $account->language, $result->snid);
730 }
731 }
732 }
733
734 // Activate/deactivate subscription when account is blocked/unblocked
735 if ($category == 'account' && isset($edit['status'])) {
736 if (variable_get('simplenews_sync_account', TRUE)) {
737 db_query("UPDATE {simplenews_subscriptions} SET activated = %d WHERE uid = %d", $edit['status'], $account->uid);
738 }
739 }
740
741 if ($category == 'newsletter' && user_access('subscribe to newsletters')) {
742 foreach ($edit['newsletters'] as $tid => $checked) {
743 if ($checked) {
744 simplenews_subscribe_user($account->mail, $tid, FALSE, 'website');
745 }
746 else {
747 simplenews_unsubscribe_user($account->mail, $tid, FALSE, 'website');
748 }
749 }
750 }
751 break;
752 case 'delete':
753 if (variable_get('simplenews_sync_account', TRUE)) {
754 // Delete subscription and all newsletter subscriptions when account is removed.
755 // We don't use simplenews_get_subscription() here because the user is already
756 // deleted from the {user} table.
757 $snid = db_result(db_query("SELECT s.snid FROM {simplenews_subscriptions} s WHERE s.mail = '%s'", $account->mail));
758 db_query('DELETE FROM {simplenews_snid_tid} WHERE snid = %d', $snid);
759 db_query('DELETE FROM {simplenews_subscriptions} WHERE snid = %d', $snid);
760 }
761 else {
762 // Only remove uid from subscription data when account is removed
763 db_query("UPDATE {simplenews_subscriptions} SET uid = 0 WHERE uid = %d", $account->uid);
764 }
765 break;
766 case 'form':
767 if ($category == 'newsletter' && user_access('subscribe to newsletters')) {
768 $subscription = simplenews_get_subscription($account);
769 $form = _simplenews_subscription_manager_form($subscription);
770 $form['subscriptions']['#title'] = t('Current newsletter subscriptions');
771 unset($form['update'], $form['subscriptions']['mail']);
772 return $form;
773 }
774 break;
775 case 'categories':
776 $output[] = array(
777 'name' => 'newsletter',
778 'title' => t('My newsletters'),
779 'weight' => 10,
780 'access callback' => 'user_access',
781 'access arguments' => array('subscribe to newsletters'),
782 );
783 return $output;
784 break;
785 case 'view':
786 global $user;
787 if ($user->uid == $account->uid || user_access('administer users')) {
788 $account->content['simplenews'] = array(
789 '#type' => 'user_profile_category',
790 '#title' => t('Newsletters'),
791 );
792 $tree = taxonomy_get_tree(variable_get('simplenews_vid', ''));
793 foreach ($tree as $newsletter) {
794 if (db_result(db_query('SELECT COUNT(s.uid) FROM {simplenews_subscriptions} s
795 INNER JOIN {simplenews_snid_tid} t ON s.snid = t.snid
796 WHERE s.uid = %d AND t.tid = %d AND t.status = %d',
797 $account->uid, $newsletter->tid, SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED))) {
798 $subscriptions[] = l($newsletter->name, 'taxonomy/term/'. $newsletter->tid);
799 }
800 }
801 if (isset($subscriptions)) {
802 $subscriptions = implode(', ', $subscriptions);
803 }
804 else {
805 $subscriptions = t('None');
806 }
807 // When a user has no permission to subscribe and is not subscribed
808 // we do not display the 'no subscriptions' message.
809 if (user_access('subscribe to newsletters') || $subscriptions != t('None')) {
810 $account->content['simplenews']['subscriptions'] = array(
811 '#type' => 'user_profile_item',
812 '#title' => t('Current subscriptions'),
813 '#value' => $subscriptions,
814 );
815 }
816 if (user_access('subscribe to newsletters')) {
817 $account->content['simplenews']['my_newsletters'] = array(
818 '#type' => 'user_profile_item',
819 '#value' => t('Manage !my_subscriptions', array('!my_subscriptions' => l(t('my subscriptions'), 'user/'. $account->uid .'/edit/newsletter'))),
820 '#weight' => -1,
821 );
822 }
823 }
824 break;
825 }
826 }
827
828 /**
829 * Implementation of hook_block().
830 */
831 function simplenews_block($op = 'list', $delta = 0, $edit = array()) {
832 switch ($op) {
833 case 'list':
834 $blocks = array();
835 foreach (taxonomy_get_tree(variable_get('simplenews_vid', '')) as $newsletter) {
836 //TODO: 1. without form -> by role; 2. with form -> user caching with refresh on subscribe/unsubscribe (option as setting) or no caching
837 $blocks[$newsletter->tid] = array(
838 'info' => t('Newsletter: @title', array('@title' => $newsletter->name)),
839 'cache' => variable_get('simplenews_block_f_'. $newsletter->tid, 1) ? BLOCK_NO_CACHE : BLOCK_CACHE_PER_ROLE,
840 );
841 }
842 return $blocks;
843 case 'configure':
844 $form['simplenews_block_'. $delta]['simplenews_block_m_'. $delta] = array(
845 '#type' => 'textfield',
846 '#title' => t('Block message'),
847 '#size' => 60,
848 '#maxlength' => 128,
849 '#default_value' => variable_get('simplenews_block_m_'. $delta, t('Stay informed on our latest news!')),
850 );
851 $form['simplenews_block_'. $delta]['simplenews_block_f_'. $delta] = array(
852 '#type' => 'radios',
853 '#title' => t('Subscription interface'),
854 '#options' => array('1' => t('Subscription form'), '0' => t('Link to form')),
855 '#description' => t("Note: this requires permission 'subscribe to newsletters'."),
856 '#default_value' => variable_get('simplenews_block_f_'. $delta, 1),
857 );
858 $form['simplenews_block_'. $delta]['simplenews_block_l_'. $delta] = array(
859 '#type' => 'checkbox',
860 '#title' => t('Display link to previous issues'),
861 '#return_value' => 1,
862 '#default_value' => variable_get('simplenews_block_l_'. $delta, 1),
863 );
864 $form['simplenews_block_'. $delta]['simplenews_block_i_status_'. $delta] = array(
865 '#type' => 'checkbox',
866 '#title' => t('Display previous issues'),
867 '#return_value' => 1,
868 '#default_value' => variable_get('simplenews_block_i_status_'. $delta, 0),
869 );
870 $form['simplenews_block_'. $delta]['simplenews_block_i_'. $delta] = array(
871 '#type' => 'select',
872 '#title' => t('Number of issues to display'),
873 '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
874 '#default_value' => variable_get('simplenews_block_i_'. $delta, 5),
875 );
876 $form['simplenews_block_'. $delta]['simplenews_block_r_'. $delta] = array(
877 '#type' => 'checkbox',
878 '#title' => t('Display RSS-feed icon'),
879 '#return_value' => 1,
880 '#default_value' => variable_get('simplenews_block_r_'. $delta, 1),
881 );
882 return $form;
883 case 'save':
884 variable_set('simplenews_block_m_'. $delta, $edit['simplenews_block_m_'. $delta]);
885 variable_set('simplenews_block_f_'. $delta, $edit['simplenews_block_f_'. $delta]);
886 variable_set('simplenews_block_l_'. $delta, $edit['simplenews_block_l_'. $delta]);
887 variable_set('simplenews_block_i_status_'. $delta, $edit['simplenews_block_i_status_'. $delta]);
888 variable_set('simplenews_block_i_'. $delta, $edit['simplenews_block_i_'. $delta]);
889 variable_set('simplenews_block_r_'. $delta, $edit['simplenews_block_r_'. $delta]);
890 break;
891 case 'view':
892 global $language;
893
894 if ($tree = taxonomy_get_tree(variable_get('simplenews_vid', ''))) {
895 // Only display a block if $delta is a valid newsletter term id.
896 foreach ($tree as $taxonomy) {
897 $tids[] = $taxonomy->tid;
898 }
899 if (in_array($delta, $tids)) {
900 // $delta is validated, the block can be displayed.
901 $newsletter = (array) taxonomy_get_term($delta);
902 // Translate newsletter name if required.
903 if (module_exists('i18ntaxonomy') && i18ntaxonomy_vocabulary(variable_get('simplenews_vid', '')) == I18N_TAXONOMY_LOCALIZE) {
904 $newsletter['name'] = tt('taxonomy:term:'. $newsletter['tid'] .':name', $newsletter['name'], $language->language);
905 }
906 $block = array(
907 'subject' => check_plain($newsletter['name']),
908 'content' => theme(array('simplenews_block__'. $newsletter['tid'], 'simplenews_block'), $newsletter['tid']),
909 );
910 return $block;
911 }
912 }
913 break;
914 }
915 }
916
917 /**
918 * Implementation of hook_forms().
919 *
920 * All form blocks are build using simplenews_block_form().
921 * hook_forms() is required to provide unique form id for each block form.
922 */
923 function simplenews_forms() {
924 if ($taxonomies = taxonomy_get_tree(variable_get('simplenews_vid', ''))) {
925 foreach ($taxonomies as $newsletter) {
926 $forms['simplenews_block_form_'. $newsletter->tid] = array(
927 'callback' => 'simplenews_block_form',
928 'callback arguments' => array($newsletter->tid),
929 );
930 }
931 }
932 return $forms;
933 }
934
935 /**
936 * Load a user or creates a dummy anonymous user.
937 *
938 * @return account
939 * object (
940 * mail, email address
941 * uid, uid or 0 for anonymous
942 * )
943 */
944 function _simplenews_user_load($mail) {
945 $account = user_load(array('mail' => $mail));
946 if ($account === FALSE) {
947 // Construct anonymous user since we don't have a user that matches that e-amil.
948 $account = new stdClass();
949 $account->uid = 0;
950 $account->mail = $mail;
951 }
952 return $account;
953 }
954
955 /**
956 * Subscribe a user to a newsletter or send a confirmation mail.
957 *
958 * The $confirm parameter determines the action:
959 * FALSE = The user is subscribed
960 * TRUE = User receives an email to verify the address and complete the subscription
961 * A new subscription account is created when the user is subscribed to the first newsletter
962 *
963 * @param string $mail
964 * The email address to subscribe to the newsletter.
965 * @param integer $tid
966 * The term ID of the newsletter.
967 * @param boolean $confirm
968 * TRUE = send confirmation mail; FALSE = subscribe immediate to the newsletter
969 * @param string $preferred_language
970 * The language code (i.e. 'en', 'nl') of the user preferred language.
971 * Use '' for the site default language.
972 * Use NULL for the language of the current page.
973 * @param string $source
974 * Indication for source of subscription. Simplenews uses these sources:
975 * website: via any website form (with or without confirmation email)
976 * mass subscribe: mass admin UI
977 * mass unsubscribe: mass admin UI
978 * action: Drupal actions
979 */
980 function simplenews_subscribe_user($mail, $tid, $confirm = TRUE, $source = 'unknown', $preferred_language = NULL) {
981 global $language;
982
983 // Get current subscriptions if any.
984 $account = (object) array('mail' => $mail);
985 $subscription = simplenews_get_subscription($account);
986
987 // If user is not subscribed to ANY newsletter, create a subscription account
988 if ($subscription->snid == 0) {
989 // To subscribe a user:
990 // - Fetch the users uid.
991 // - Determine the user preferred language.
992 // - Add the user to the database.
993 // - Get the full subscription object based on the mail address.
994 // Note that step 3 gets subscription data based on mail address because the uid can be 0 (for anonymous users)
995 $account = _simplenews_user_load($mail);
996
997 // If the site is multilingual:
998 // - Anonymous users are subscribed with their preferred language
999 // equal to the language of the current page.
1000 // - Registered users will be subscribed with their default language as
1001 // set in their account settings.
1002 // By default the preferred language is not set.
1003 if (variable_get('language_count', 1) > 1) {
1004 if ($account->uid) {
1005 $preferred_language = $account->language;
1006 }
1007 else {
1008 $preferred_language = isset($preferred_language) ? $preferred_language : $language->language;
1009 }
1010 }
1011
1012 db_query("INSERT INTO {simplenews_subscriptions} (mail, uid, language, activated) VALUES ('%s', %d, '%s', %d)", $mail, $account->uid, $preferred_language, 1);
1013 $subscription = simplenews_get_subscription($account);
1014 }
1015
1016 if ($confirm) {
1017 // Send confirmation email to user to complete subscription or to tell
1018 // them that he or she is already subscribed.
1019 // Confirmation mail is in the user preferred language which is by default the language_default().
1020 $params['from'] = _simplenews_set_from();
1021 $params['context']['newsletter'] = taxonomy_get_term($tid);
1022 $params['context']['account'] = $subscription;
1023 drupal_mail('simplenews', 'subscribe', $mail, $subscription->language, $params, $params['from']['address']);
1024 }
1025 elseif (!isset($subscription->tids[$tid])) {
1026 // Add user to newsletter relationship if not already subscribed.
1027
1028 // Check if user is (un)subscribed to this newsletter.
1029 // Resubscribe or add new subscription.
1030 if (isset($subscription->newsletter_subscription[$tid])) {
1031 db_query("UPDATE {simplenews_snid_tid}
1032 SET status = %d, timestamp = %d, source = '%s'
1033 WHERE snid = %d AND tid = %d",
1034 SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED, time(), $source,
1035 $subscription->snid, $tid);
1036 }
1037 else {
1038 db_query("INSERT INTO {simplenews_snid_tid} (snid, tid, status, timestamp, source)
1039 VALUES (%d, %d, %d, %d, '%s')",
1040 $subscription->snid, $tid, SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED, time(), $source);
1041 }
1042
1043 // Execute simplenews subscribe trigger.
1044 simplenews_call_actions('subscribe', $subscription);
1045 }
1046 return TRUE;
1047 }
1048
1049 /**
1050 * Unsubscribe a user from a newsletter or send a confirmation mail.
1051 *
1052 * The $confirm parameter determines the action:
1053 * FALSE = The user is unsubscribed
1054 * TRUE = User receives an email to verify the address and complete the unsubscription
1055 * The subscription account is deleted when the user is unsubscribed to the last newsletter
1056 *
1057 * @param string $mail The email address to unsubscribe from the newsletter.
1058 * @param integer $tid The term ID of the newsletter.
1059 * @param boolean $confirm TRUE = send confirmation mail; FALSE = unsubscribe immediate from the newsletter
1060 * @param string $source
1061 * Indication for source of unsubscription. Simplenews uses these sources:
1062 * website: via any website form (with or without confirmation email)
1063 * mass subscribe: mass admin UI
1064 * mass unsubscribe: mass admin UI
1065 * action: Drupal actions
1066 */