simplenews.module 97 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940
  1. <?php
  2. /**
  3. * @file
  4. * Simplenews node handling, sent email, newsletter block and general hooks
  5. */
  6. /**
  7. * NEWSLETTER MAIL PRIORITY
  8. */
  9. define('SIMPLENEWS_PRIORITY_NONE', 0);
  10. define('SIMPLENEWS_PRIORITY_HIGHEST', 1);
  11. define('SIMPLENEWS_PRIORITY_HIGH', 2);
  12. define('SIMPLENEWS_PRIORITY_NORMAL', 3);
  13. define('SIMPLENEWS_PRIORITY_LOW', 4);
  14. define('SIMPLENEWS_PRIORITY_LOWEST', 5);
  15. /**
  16. * NEWSLETTER SEND COMMAND
  17. */
  18. define('SIMPLENEWS_COMMAND_SEND_TEST', 0);
  19. define('SIMPLENEWS_COMMAND_SEND_NOW', 1);
  20. define('SIMPLENEWS_COMMAND_SEND_PUBLISH', 3);
  21. /**
  22. * NEWSLETTER SUBSCRIPTION STATUS
  23. */
  24. define('SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED', 1);
  25. define('SIMPLENEWS_SUBSCRIPTION_STATUS_UNCONFIRMED', 2);
  26. define('SIMPLENEWS_SUBSCRIPTION_STATUS_UNSUBSCRIBED', 0);
  27. /**
  28. * NEWSLETTER SENT STATUS
  29. */
  30. define('SIMPLENEWS_STATUS_SEND_NOT', 0);
  31. define('SIMPLENEWS_STATUS_SEND_PENDING', 1);
  32. define('SIMPLENEWS_STATUS_SEND_READY', 2);
  33. define('SIMPLENEWS_STATUS_SEND_PUBLISH', 3);
  34. /**
  35. * MAIL SPOOL SENT STATUS
  36. */
  37. define('SIMPLENEWS_SPOOL_HOLD', 0);
  38. define('SIMPLENEWS_SPOOL_PENDING', 1);
  39. define('SIMPLENEWS_SPOOL_DONE', 2);
  40. define('SIMPLENEWS_SPOOL_IN_PROGRESS', 3);
  41. /**
  42. * AFTER EACH 100 NEWSLETTERS
  43. * simplenews_mail_spool() CHECKS IF LIMITS ARE EXCEEDED
  44. */
  45. define('SIMPLENEWS_SEND_CHECK_INTERVAL', 100);
  46. /**
  47. * AT 80% OF PHP MAX EXECUTION TIME EMAIL SENDING IS INTERRUPTED
  48. */
  49. define('SIMPLENEWS_SEND_TIME_LIMIT', 0.8);
  50. /**
  51. * SUBSCRIPTION STATUS
  52. */
  53. define('SIMPLENEWS_SUBSCRIPTION_INACTIVE', 0);
  54. define('SIMPLENEWS_SUBSCRIPTION_ACTIVE', 1);
  55. define('SIMPLENEWS_OPT_INOUT_HIDDEN', 'hidden');
  56. define('SIMPLENEWS_OPT_INOUT_SINGLE', 'single');
  57. define('SIMPLENEWS_OPT_INOUT_DOUBLE', 'double');
  58. /**
  59. * Used when sending an unlimited amount of mails from the spool.
  60. */
  61. define('SIMPLENEWS_UNLIMITED', -1);
  62. /**
  63. * Implements hook_permission().
  64. *
  65. * @todo Change sort order where required: http://drupal.org/node/224333#sorting_permissions
  66. */
  67. function simplenews_permission() {
  68. return array(
  69. 'administer newsletters' => array(
  70. 'title' => t('Administer newsletters'),
  71. ),
  72. 'administer simplenews subscriptions' => array(
  73. 'title' => t('Administer simplenews subscriptions'),
  74. ),
  75. 'administer simplenews settings' => array(
  76. 'title' => t('Administer simplenews settings'),
  77. ),
  78. 'send newsletter' => array(
  79. 'title' => t('Send newsletter'),
  80. ),
  81. 'subscribe to newsletters' => array(
  82. 'title' => t('Subscribe to newsletters'),
  83. ),
  84. );
  85. }
  86. /**
  87. * Implements hook_init().
  88. *
  89. * @todo move this to a specific form theme function ?
  90. */
  91. function simplenews_init() {
  92. drupal_add_css(drupal_get_path('module', 'simplenews') . '/simplenews.css', array('every_page' => TRUE));
  93. }
  94. /**
  95. * Implements hook_menu().
  96. */
  97. function simplenews_menu() {
  98. $items['admin/content/simplenews'] = array(
  99. 'title' => 'Newsletters',
  100. 'description' => 'List newsletters and newsletter sent status.',
  101. 'type' => MENU_LOCAL_TASK,
  102. 'page callback' => 'drupal_get_form',
  103. 'page arguments' => array('simplenews_admin_newsletter_issues'),
  104. 'access arguments' => array('administer newsletters'),
  105. 'file' => 'includes/simplenews.admin.inc',
  106. );
  107. $items['admin/config/services/simplenews'] = array(
  108. 'title' => 'Newsletters',
  109. 'description' => 'Configure your sites newsletters.',
  110. 'page callback' => 'drupal_get_form',
  111. 'page arguments' => array('simplenews_admin_categories'),
  112. 'access arguments' => array('administer newsletters'),
  113. 'file' => 'includes/simplenews.admin.inc',
  114. );
  115. $items['admin/config/services/simplenews/categories'] = array(
  116. 'title' => 'Newsletters',
  117. 'type' => MENU_DEFAULT_LOCAL_TASK,
  118. );
  119. $items['admin/config/services/simplenews/categories/%simplenews_category/edit'] = array(
  120. 'title' => 'Newsletters',
  121. 'page callback' => 'drupal_get_form',
  122. 'page arguments' => array('simplenews_admin_category_form', 5),
  123. 'access arguments' => array('administer newsletters'),
  124. 'file' => 'includes/simplenews.admin.inc',
  125. );
  126. $items['admin/config/services/simplenews/categories/%simplenews_category/delete'] = array(
  127. 'title' => 'Newsletters',
  128. 'page callback' => 'drupal_get_form',
  129. 'page arguments' => array('simplenews_admin_category_delete', 5),
  130. 'access arguments' => array('administer newsletters'),
  131. 'file' => 'includes/simplenews.admin.inc',
  132. );
  133. $items['admin/config/services/simplenews/add'] = array(
  134. 'title' => 'Add newsletter category',
  135. 'type' => MENU_LOCAL_ACTION,
  136. 'page callback' => 'drupal_get_form',
  137. 'page arguments' => array('simplenews_admin_category_form'),
  138. 'access arguments' => array('administer newsletters'),
  139. 'file' => 'includes/simplenews.admin.inc',
  140. 'weight' => -9,
  141. );
  142. $items['admin/content/simplenews/subscriptions/delete'] = array(
  143. 'title' => 'Delete',
  144. 'type' => MENU_CALLBACK,
  145. 'page callback' => 'drupal_get_form',
  146. 'page arguments' => array('simplenews_subscription_multiple_delete_confirm'),
  147. 'access arguments' => array('administer simplenews subscriptions'),
  148. 'file' => 'includes/simplenews.admin.inc',
  149. );
  150. $items['admin/people/simplenews'] = array(
  151. 'title' => 'Newsletter subscriptions',
  152. 'description' => 'Newsletter subscription management.',
  153. 'type' => MENU_LOCAL_TASK,
  154. 'page callback' => 'drupal_get_form',
  155. 'page arguments' => array('simplenews_admin_subscription'),
  156. 'access arguments' => array('administer simplenews subscriptions'),
  157. 'file' => 'includes/simplenews.admin.inc',
  158. );
  159. $items['admin/people/simplenews/users/edit/%'] = array(
  160. 'title' => 'Subscriptions',
  161. 'page callback' => 'drupal_get_form',
  162. 'page arguments' => array('simplenews_subscriptions_admin_form', 5),
  163. 'access arguments' => array('administer simplenews subscriptions'),
  164. 'file' => 'includes/simplenews.subscription.inc',
  165. );
  166. $items['admin/people/simplenews/import'] = array(
  167. 'title' => 'Mass subscribe',
  168. 'type' => MENU_LOCAL_ACTION,
  169. 'page callback' => 'drupal_get_form',
  170. 'page arguments' => array('simplenews_subscription_list_add'),
  171. 'access arguments' => array('administer simplenews subscriptions'),
  172. 'file' => 'includes/simplenews.admin.inc',
  173. 'weight' => 8,
  174. );
  175. $items['admin/people/simplenews/unsubscribe'] = array(
  176. 'title' => 'Mass unsubscribe',
  177. 'type' => MENU_LOCAL_ACTION,
  178. 'page callback' => 'drupal_get_form',
  179. 'page arguments' => array('simplenews_subscription_list_remove'),
  180. 'access arguments' => array('administer simplenews subscriptions'),
  181. 'file' => 'includes/simplenews.admin.inc',
  182. 'weight' => 9,
  183. );
  184. $items['admin/people/simplenews/export'] = array(
  185. 'title' => 'Export',
  186. 'type' => MENU_LOCAL_ACTION,
  187. 'page callback' => 'drupal_get_form',
  188. 'page arguments' => array('simplenews_subscription_list_export'),
  189. 'access arguments' => array('administer simplenews subscriptions'),
  190. 'file' => 'includes/simplenews.admin.inc',
  191. 'weight' => 10,
  192. );
  193. $items['admin/config/services/simplenews/settings'] = array(
  194. 'title' => 'Settings',
  195. 'description' => 'Simplenews settings.',
  196. 'page callback' => 'drupal_get_form',
  197. 'weight' => 20,
  198. 'page arguments' => array('simplenews_admin_settings_newsletter'),
  199. 'access arguments' => array('administer simplenews settings'),
  200. 'file' => 'includes/simplenews.admin.inc',
  201. 'file path' => drupal_get_path('module', 'simplenews'),
  202. 'type' => MENU_LOCAL_TASK,
  203. );
  204. $items['admin/config/services/simplenews/settings/newsletter'] = array(
  205. 'title' => 'Newsletter',
  206. 'weight' => -15,
  207. 'type' => MENU_DEFAULT_LOCAL_TASK,
  208. );
  209. $items['admin/config/services/simplenews/settings/subscription'] = array(
  210. 'title' => 'Subscription',
  211. 'description' => 'Subscription settings, opt-in/out confirmation email text.',
  212. 'page callback' => 'drupal_get_form',
  213. 'weight' => -10,
  214. 'page arguments' => array('simplenews_admin_settings_subscription'),
  215. 'access arguments' => array('administer simplenews settings'),
  216. 'file' => 'includes/simplenews.admin.inc',
  217. 'file path' => drupal_get_path('module', 'simplenews'),
  218. 'type' => MENU_LOCAL_TASK,
  219. );
  220. $items['admin/config/services/simplenews/settings/mail'] = array(
  221. 'title' => 'Send mail',
  222. 'description' => 'Send mail, cron and debug options.',
  223. 'page callback' => 'drupal_get_form',
  224. 'weight' => -5,
  225. 'page arguments' => array('simplenews_admin_settings_mail'),
  226. 'access arguments' => array('administer simplenews settings'),
  227. 'file' => 'includes/simplenews.admin.inc',
  228. 'file path' => drupal_get_path('module', 'simplenews'),
  229. 'type' => MENU_LOCAL_TASK,
  230. );
  231. $items['newsletter/confirm'] = array(
  232. 'title' => 'Confirm newsletter subscriptions',
  233. 'type' => MENU_CALLBACK,
  234. 'page callback' => 'simplenews_confirm_subscription',
  235. 'access arguments' => array('subscribe to newsletters'),
  236. 'file' => 'includes/simplenews.subscription.inc',
  237. );
  238. $items['newsletter/subscriptions'] = array(
  239. 'title' => 'Your newsletter subscriptions',
  240. 'type' => MENU_CALLBACK,
  241. 'page callback' => 'drupal_get_form',
  242. 'page arguments' => array('simplenews_subscriptions_page_form'),
  243. 'access arguments' => array('subscribe to newsletters'),
  244. 'file' => 'includes/simplenews.subscription.inc',
  245. );
  246. $items['node/%node/simplenews'] = array(
  247. 'title' => 'Newsletter',
  248. 'type' => MENU_LOCAL_TASK,
  249. 'access callback' => 'simplenews_node_tab_access',
  250. 'access arguments' => array(1),
  251. 'page callback' => 'simplenews_node_tab_page',
  252. 'page arguments' => array(1),
  253. 'context' => MENU_LOCAL_TASK,
  254. 'file' => 'includes/simplenews.admin.inc',
  255. 'weight' => 2,
  256. );
  257. return $items;
  258. }
  259. /**
  260. * Implements hook_admin_paths().
  261. */
  262. function simplenews_admin_paths() {
  263. $paths = array(
  264. 'node/*/simplenews' => TRUE,
  265. );
  266. return $paths;
  267. }
  268. /**
  269. * Implements hook_node_type_delete().
  270. */
  271. function simplenews_node_type_delete($info) {
  272. drupal_static_reset('simplenews_get_content_types');
  273. }
  274. /**
  275. * Implements hook_node_type_update().
  276. */
  277. function simplenews_node_type_update($info) {
  278. drupal_static_reset('simplenews_get_content_types');
  279. if (simplenews_check_node_types($info->type)) {
  280. simplenews_add_term_field($info);
  281. }
  282. else {
  283. // Don't remove the field. This leads to data loss.
  284. // simplenews_remove_term_field($info);
  285. }
  286. }
  287. /**
  288. * Implements hook_node_type_insert().
  289. */
  290. function simplenews_node_type_insert($info) {
  291. // Avoid using simplenews_check_node_type() because the node type cache
  292. // has not been cleared yet.
  293. if (variable_get('simplenews_content_type_' . $info->type, FALSE)) {
  294. simplenews_add_term_field($info);
  295. }
  296. }
  297. /**
  298. * Implements hook_node_view().
  299. */
  300. function simplenews_node_view($node, $view_mode) {
  301. if (!simplenews_check_node_types($node->type)) {
  302. return;
  303. }
  304. // Only do token replacements for view modes other than the our own email view
  305. // modes. Token replacements for them will happen later on.
  306. if (strpos($view_mode, 'email_') !== FALSE) {
  307. return;
  308. }
  309. // Build up content, add as much as there is.
  310. $context = array(
  311. 'node' => $node,
  312. );
  313. if ($newsletter = simplenews_newsletter_load($node->nid)) {
  314. if ($category = simplenews_category_load($newsletter->tid)) {
  315. $context['category'] = $category;
  316. }
  317. }
  318. // If the current user is a subscriber, extend context.
  319. if ($GLOBALS['user']->uid > 0 && $subscriber = simplenews_subscriber_load_by_mail($GLOBALS['user']->mail)) {
  320. $context['simplenews_subscriber'] = $subscriber;
  321. }
  322. // Loop over all render array elements.
  323. foreach (element_children($node->content) as $key) {
  324. $element = &$node->content[$key];
  325. // Make sure this is a field.
  326. if (!isset($element['#field_type'])) {
  327. continue;
  328. }
  329. // Loop over all field values.
  330. foreach (element_children($element) as $field_key) {
  331. $item = &$element[$field_key];
  332. // Only fields which result in simple markup elements are supported for
  333. // token replacements for now.
  334. if (isset($item['#markup'])) {
  335. $item['#markup'] = token_replace($item['#markup'], $context, array());
  336. }
  337. }
  338. }
  339. }
  340. /**
  341. * Get simplenews category term values from a node object.
  342. *
  343. * @ingroup issue
  344. */
  345. function simplenews_get_term_values($node) {
  346. $category_field = simplenews_get_category_field($node->type);
  347. $taxonomy = $node->{$category_field['field_name']};
  348. if (isset($taxonomy[$node->language])) {
  349. return $taxonomy[$node->language];
  350. }
  351. $term = current($taxonomy);
  352. return ($term) ? $term : FALSE;
  353. }
  354. /**
  355. * Implements hook_node_validate().
  356. */
  357. function simplenews_node_validate($node, $form) {
  358. if (!simplenews_check_node_types($node->type)) {
  359. return;
  360. }
  361. // Check if a taxonomy term field is present in the node.
  362. $field = simplenews_get_category_field($node->type);
  363. if (!$field) {
  364. form_set_error('', t('No newsletter category field is configured. Check ... @todo'));
  365. }
  366. else {
  367. // Check if a newsletter category term is selected.
  368. $terms = simplenews_get_term_values($node);
  369. if (!$terms) {
  370. form_set_error($field['field_name'], t('A newsletter category term is required.'));
  371. watchdog('simplenews', '@todo ... newsletter taxonomy is required, change settings.', array(), WATCHDOG_ERROR);
  372. }
  373. elseif (count($terms) > 1) {
  374. form_set_error($field['field_name'], t('Only one newsletter category term is allowed.'));
  375. watchdog('simplenews', '@todo ... newsletter taxonomy must be single value, change settings.', array(), WATCHDOG_ERROR);
  376. }
  377. }
  378. }
  379. /**
  380. * Implements hook_node_presave().
  381. */
  382. function simplenews_node_presave($node) {
  383. if (!simplenews_check_node_types($node->type)) {
  384. return;
  385. }
  386. if (!empty($node->nid) && !isset($node->simplenews)) {
  387. $node->simplenews = simplenews_newsletter_load($node->nid);
  388. }
  389. if (empty($node->simplenews)) {
  390. $newsletter = (object) simplenews_newsletter_defaults($node);
  391. }
  392. }
  393. /**
  394. * Implements hook_node_insert().
  395. */
  396. function simplenews_node_insert($node) {
  397. if (!simplenews_check_node_types($node->type)) {
  398. return;
  399. }
  400. $node->simplenews = (object) simplenews_newsletter_defaults($node);
  401. simplenews_newsletter_save($node->simplenews);
  402. }
  403. /**
  404. * Return default newsletter.
  405. */
  406. function simplenews_newsletter_defaults($node = NULL) {
  407. $newsletter = array(
  408. 'nid' => NULL,
  409. 'tid' => NULL,
  410. 'status' => SIMPLENEWS_STATUS_SEND_NOT,
  411. 'sent_subscriber_count' => 0,
  412. );
  413. if ($node) {
  414. $newsletter['nid'] = isset($node->nid) ? $node->nid : NULL;
  415. $terms = simplenews_get_term_values($node);
  416. $newsletter['tid'] = $terms[0]['tid'];
  417. }
  418. return $newsletter;
  419. }
  420. /**
  421. * Implements hook_node_update().
  422. */
  423. function simplenews_node_update($node) {
  424. if (!simplenews_check_node_types($node->type)) {
  425. return;
  426. }
  427. $node->simplenews = simplenews_newsletter_load($node->nid);
  428. if (!$node->simplenews) {
  429. $node->simplenews = (object) simplenews_newsletter_defaults($node);
  430. }
  431. else {
  432. // Update tid.
  433. $terms = simplenews_get_term_values($node);
  434. $node->simplenews->tid = $terms[0]['tid'];
  435. }
  436. // Check if the newsletter is set to send on publish and needs to be send.
  437. if ($node->simplenews->status == SIMPLENEWS_STATUS_SEND_PUBLISH && $node->status == NODE_PUBLISHED) {
  438. module_load_include('inc', 'simplenews', 'includes/simplenews.mail');
  439. simplenews_add_node_to_spool($node);
  440. }
  441. else {
  442. // simplenews_update_sent_status() already saves the node, only need to do
  443. // it when not sending.
  444. simplenews_newsletter_save($node->simplenews);
  445. }
  446. }
  447. /**
  448. * Implements hook_node_delete().
  449. */
  450. function simplenews_node_delete($node) {
  451. if (!simplenews_check_node_types($node->type)) {
  452. return;
  453. }
  454. simplenews_newsletter_delete($node);
  455. // Check if pending emails of this newsletter issue exist and delete these too.
  456. module_load_include('inc', 'simplenews', 'includes/simplenews.mail');
  457. $count = simplenews_delete_spool(array('nid' => $node->nid));
  458. if ($count) {
  459. drupal_set_message(t('Newsletter %title was deleted but had @count pending emails. They can no longer be sent.', array(
  460. '%title' => $node->title,
  461. '@count' => $count,
  462. )), 'warning');
  463. watchdog('simplenews', 'Newsletter %title deleted with @count pending emails. Emails can no longer be sent.', array(
  464. '%title' => $node->title,
  465. '@count' => $count,
  466. ), WATCHDOG_ALERT);
  467. }
  468. drupal_set_message(t('Newsletter %title was deleted.', array('%title' => $node->title)));
  469. }
  470. /**
  471. * Implements hook_node_load().
  472. */
  473. function simplenews_node_load($nodes, $types) {
  474. // We only support Simplenews enabled content types.
  475. if (!simplenews_check_node_types($types)) {
  476. return;
  477. }
  478. $newsletters = simplenews_newsletter_load_multiple(array_keys($nodes));
  479. foreach ($nodes as $nid => $node) {
  480. // We can have multiple nodes where not all of them are simplenews enabled
  481. // content type. So we need to check for each individual node is it
  482. // simplenews enabled.
  483. if (!simplenews_check_node_types($node->type)) {
  484. continue;
  485. }
  486. // Make sure every $node has valid newsletter data as object
  487. $newsletter = array();
  488. if (isset($newsletters[$nid])) {
  489. $newsletter = $newsletters[$nid];
  490. }
  491. if (!$newsletter) {
  492. $newsletter = (object) simplenews_newsletter_defaults($node);
  493. }
  494. $nodes[$nid]->simplenews = $newsletter;
  495. }
  496. }
  497. /**
  498. * Check if content type(s) is enabled for use as Simplenews newsletter.
  499. *
  500. * @param $types
  501. * Array of content types or single content type string.
  502. * @return boolean
  503. * TRUE if at least one of $types is enabled for Simplenews.
  504. *
  505. * @ingroup issue
  506. */
  507. function simplenews_check_node_types($types) {
  508. if (!is_array($types)) {
  509. $types = array($types);
  510. }
  511. if ($sn_types = simplenews_get_content_types()) {
  512. foreach ($types as $type) {
  513. if (in_array($type, $sn_types)) {
  514. return TRUE;
  515. }
  516. }
  517. }
  518. return FALSE;
  519. }
  520. /**
  521. * Get all node types supported by Simplenews.
  522. *
  523. * @return
  524. * Array of node-types which can be used a simplenews newsletter issue.
  525. *
  526. * @ingroup issue
  527. */
  528. function simplenews_get_content_types() {
  529. $simplenews_types = &drupal_static(__FUNCTION__, array());
  530. if (!$simplenews_types) {
  531. foreach (node_type_get_types() as $name => $type) {
  532. if (variable_get('simplenews_content_type_' . $name, FALSE)) {
  533. $simplenews_types[] = $name;
  534. }
  535. }
  536. }
  537. return $simplenews_types;
  538. }
  539. /**
  540. * Add the taxonomy term field for the newsletter category.
  541. *
  542. * @param $type
  543. * A node type object.
  544. *
  545. * @ingroup issue
  546. */
  547. function simplenews_add_term_field($type) {
  548. $field_name = variable_get('simplenews_category_field', 'field_simplenews_term');
  549. $field = field_info_field($field_name);
  550. $instance = field_info_instance('node', $field_name, $type->type);
  551. if (empty($field)) {
  552. $field = array(
  553. 'field_name' => $field_name,
  554. 'type' => 'taxonomy_term_reference',
  555. 'cardinality' => 1,
  556. //'entity_types' => array(),
  557. 'translatable' => TRUE,
  558. 'settings' => array(
  559. 'allowed_values' => array(
  560. array(
  561. 'parent' => 0,
  562. 'vocabulary' => 'newsletter',
  563. ),
  564. ),
  565. ),
  566. );
  567. $field = field_create_field($field);
  568. }
  569. if (empty($instance)) {
  570. $instance = array(
  571. 'label' => t('Newsletter category'),
  572. 'field_name' => $field_name,
  573. 'bundle' => $type->type,
  574. 'entity_type' => 'node',
  575. 'required' => TRUE,
  576. 'widget' => array(
  577. 'type' => 'options_buttons',
  578. ),
  579. //'settings' => array(),
  580. 'display' => array(
  581. 'default' => array(
  582. 'label' => 'hidden',
  583. 'type' => 'taxonomy_term_reference_link',
  584. ),
  585. 'teaser' => array(
  586. 'label' => 'hidden',
  587. 'type' => 'hidden',
  588. ),
  589. 'email_plain' => array(
  590. 'label' => 'hidden',
  591. 'type' => 'hidden',
  592. ),
  593. 'email_html' => array(
  594. 'label' => 'hidden',
  595. 'type' => 'hidden',
  596. ),
  597. 'email_textalt' => array(
  598. 'label' => 'hidden',
  599. 'type' => 'hidden',
  600. ),
  601. ),
  602. );
  603. field_create_instance($instance);
  604. }
  605. }
  606. /**
  607. * Remove the taxonomy term field for the newsletter category.
  608. *
  609. * @param $type
  610. * A node type object.
  611. *
  612. * @ingroup issue
  613. */
  614. function simplenews_remove_term_field($type) {
  615. $field_name = variable_get('simplenews_category_field', 'field_simplenews_term');
  616. $instance = field_info_instance('node', $field_name, $type->type);
  617. if ($instance) {
  618. field_delete_instance($instance);
  619. }
  620. }
  621. /**
  622. * Get the fieldname(s) from a content type that hold the newsletter category term.
  623. *
  624. * @param $bundle_name
  625. * The content type of which to return the field.
  626. * @return field definition
  627. * Field name of the field containing the newsletter category term.
  628. * FALSE if no field is selected.
  629. *
  630. * @ingroup issue
  631. */
  632. function simplenews_get_category_field($bundle_name) {
  633. $fields_info = field_info_fields();
  634. $field_name = variable_get('simplenews_category_field', 'field_simplenews_term');
  635. $instances = field_info_instances('node', $bundle_name);
  636. if (isset($instances[$field_name])) {
  637. return $fields_info[$field_name];
  638. }
  639. else {
  640. return FALSE;
  641. }
  642. }
  643. /**
  644. * Implements hook_form_FORM_ID_alter().
  645. *
  646. * Add checkbox to the content type form to use the content type as newsletter.
  647. */
  648. function simplenews_form_node_type_form_alter(&$form, $form_state) {
  649. // Add option to use content type as simplenews newsletter.
  650. $form['workflow']['simplenews_content_type'] = array(
  651. '#type' => 'checkbox',
  652. '#title' => t('Use as simplenews newsletter'),
  653. '#default_value' => variable_get('simplenews_content_type_' . $form['#node_type']->type, 0),
  654. );
  655. }
  656. /**
  657. * Implements hook_form_FORM_ID_alter().
  658. *
  659. * Add a warning message to taxonomy term delete form.
  660. */
  661. function simplenews_form_taxonomy_form_term_alter(&$form, $form_state) {
  662. if (isset($form_state['confirm_delete']) && $form_state['confirm_delete']) {
  663. if ($form['#term']->vocabulary_machine_name == 'newsletter') {
  664. $category = simplenews_category_load($form['#term']->tid);
  665. $form['description']['#markup'] = '<p>' . t('This taxonomy term is part of simplenews newsletter category %category_name. Deleting this term will delete the newsletter category and <strong>all subscriptions to category %category_name</strong>. This action cannot be undone.', array('%category_name' => _simplenews_newsletter_name($category))) . '</p>' . $form['description']['#markup'];
  666. }
  667. }
  668. }
  669. /**
  670. * Implements hook_taxonomy_term_delete().
  671. *
  672. * Delete simplenews category if taxonomy term is delete.
  673. */
  674. function simplenews_taxonomy_term_delete($term) {
  675. // A simplenews newsletter category can not exist without the associated
  676. // taxonomy term. So we delete the category. simplenews_category_delete()
  677. // will also delete the subscriptions to the category.
  678. if ($term->vocabulary_machine_name == 'newsletter') {
  679. // Make sure we only try to delete if not already deleted.
  680. if (($category = simplenews_category_load($term->tid)) !== FALSE) {
  681. // Add name and description from $term object.
  682. $category->name = $term->name;
  683. $category->description = $term->description;
  684. simplenews_category_delete($category);
  685. }
  686. }
  687. }
  688. /**
  689. * Implements hook_form_alter().
  690. */
  691. function simplenews_form_alter(&$form, &$form_state, $form_id) {
  692. // Add Simplenews settings to simplenews newsletter node form.
  693. if (!empty($form['#node_edit_form'])) {
  694. if (in_array($form['type']['#value'], simplenews_get_content_types())) {
  695. _simplenews_node_form($form, $form_state);
  696. }
  697. }
  698. }
  699. /**
  700. * @todo
  701. */
  702. function _simplenews_node_form(&$form, $form_state) {
  703. // Display warning if the node is currently being sent.
  704. if (!empty($form['#node']->nid)) {
  705. $newsletter = simplenews_newsletter_load($form['#node']->nid);
  706. if ($newsletter && $newsletter->status == SIMPLENEWS_STATUS_SEND_PENDING) {
  707. drupal_set_message(t('This newsletter issue is currently being sent. Any changes will be reflected in the e-mails which have not been sent yet.'), 'warning');
  708. }
  709. }
  710. if (module_exists('token')) {
  711. $form['simplenews_token_help'] = array(
  712. '#title' => t('Replacement patterns'),
  713. '#type' => 'fieldset',
  714. '#collapsible' => TRUE,
  715. '#collapsed' => TRUE,
  716. '#description' => t('These tokens can be used in all text fields except subject and will be replaced on-screen and in the email.'),
  717. );
  718. $form['simplenews_token_help']['browser'] = array(
  719. '#theme' => 'token_tree',
  720. '#token_types' => array('simplenews-category', 'simplenews-subscriber', 'node'),
  721. );
  722. }
  723. }
  724. /**
  725. * Implements hook_entity_info_alter().
  726. */
  727. function simplenews_entity_info_alter(&$info) {
  728. // Add the 'Plain', 'HTML' and 'Text alternative' view mode for nodes in email.
  729. $info['node']['view modes'] += array(
  730. 'email_plain' => array(
  731. 'label' => t('Email: Plain'),
  732. 'custom settings' => FALSE,
  733. ),
  734. 'email_html' => array(
  735. 'label' => t('Email: HTML'),
  736. 'custom settings' => FALSE,
  737. ),
  738. 'email_textalt' => array(
  739. 'label' => t('Email: HTML text alternative'),
  740. 'custom settings' => FALSE,
  741. ),
  742. );
  743. }
  744. /**
  745. * Implements hook_cron().
  746. */
  747. function simplenews_cron() {
  748. module_load_include('inc', 'simplenews', 'includes/simplenews.mail');
  749. simplenews_mail_spool(variable_get('simplenews_throttle', 20));
  750. simplenews_clear_spool();
  751. // Update sent status for newsletter admin panel.
  752. simplenews_send_status_update();
  753. }
  754. /**
  755. * Implements hook_form_FORM_ID_alter().
  756. *
  757. * Add simplenews subscription fields to user register form.
  758. * @todo mode this function to another place in the module.
  759. */
  760. function simplenews_form_user_register_form_alter(&$form, &$form_state) {
  761. $options = $default_value = $hidden = array();
  762. // Determine the lists to which a user can choose to subscribe.
  763. // Determine to which other list a user is automatically subscribed.
  764. foreach (simplenews_categories_load_multiple() as $list) {
  765. $subscribe_new_account = $list->new_account;
  766. $opt_inout_method = $list->opt_inout;
  767. if (($subscribe_new_account == 'on' || $subscribe_new_account == 'off') && ($opt_inout_method == 'single' || $opt_inout_method == 'double')) {
  768. $options[$list->tid] = check_plain(_simplenews_newsletter_name($list));
  769. if ($subscribe_new_account == 'on') {
  770. $default_value[] = $list->tid;
  771. }
  772. }
  773. else {
  774. if ($subscribe_new_account == 'silent' || ($subscribe_new_account == 'on' && $opt_inout_method == SIMPLENEWS_OPT_INOUT_HIDDEN)) {
  775. $hidden[] = $list->tid;
  776. }
  777. }
  778. }
  779. if (count($options)) {
  780. // @todo Change this text: use less words;
  781. $form['simplenews'] = array(
  782. '#type' => 'fieldset',
  783. '#description' => t('Select the newsletter(s) to which you wish to subscribe.'),
  784. '#weight' => 5,
  785. );
  786. $form['simplenews']['newsletters'] = array(
  787. '#type' => 'checkboxes',
  788. '#options' => $options,
  789. '#default_value' => $default_value,
  790. );
  791. }
  792. if (count($hidden)) {
  793. $form['simplenews_hidden'] = array(
  794. '#type' => 'hidden',
  795. '#value' => implode(',', $hidden),
  796. );
  797. }
  798. }
  799. /**
  800. * Implements hook_user_insert().
  801. *
  802. * Update uid and preferred language when the new account was already subscribed.
  803. */
  804. function simplenews_user_insert(&$edit, $account, $category) {
  805. // Use the email address to check if new account is already subscribed.
  806. $subscriber = simplenews_subscriber_load_by_mail($edit['mail']);
  807. // If the user is subscribed, we update the subscriber with uid and language.
  808. if ($subscriber) {
  809. $subscriber->uid = $edit['uid'];
  810. $subscriber->language = $edit['language'];
  811. $subscriber->activated = 1;
  812. simplenews_subscriber_save($subscriber);
  813. }
  814. // Process subscription check boxes.
  815. if (isset($edit['newsletters'])) {
  816. $nl_tids = array_keys(array_filter($edit['newsletters']));
  817. if (!empty($nl_tids)) {
  818. $newsletters = simplenews_categories_load_multiple($nl_tids, array('show_all' => TRUE));
  819. foreach ($newsletters as $newsletter) {
  820. simplenews_subscribe_user($account->mail, $newsletter->tid, FALSE, 'website', $edit['language']);
  821. drupal_set_message(t('You have been subscribed to %newsletter.', array('%newsletter' => _simplenews_newsletter_name($newsletter))));
  822. }
  823. }
  824. }
  825. // Process hidden (automatic) subscriptions.
  826. if (isset($edit['simplenews_hidden'])) {
  827. foreach (explode(',', $edit['simplenews_hidden']) as $tid) {
  828. simplenews_subscribe_user($account->mail, $tid, FALSE, 'automatically', $edit['language']);
  829. }
  830. unset($edit['simplenews_hidden']);
  831. }
  832. // set inactive if not created by an administrator.
  833. // @todo This needs a cleaner API.
  834. if (!user_access('administer users')) {
  835. // this user will be activated on first login (see simplenews_user_login)
  836. $query = db_update('simplenews_subscriber')
  837. ->fields(array('activated' => SIMPLENEWS_SUBSCRIPTION_INACTIVE))
  838. ->condition('uid', $account->uid)
  839. ->execute();
  840. }
  841. }
  842. /**
  843. * Implements hook_user_login().
  844. *
  845. * Subscribe user to a newsletter as per registration form.
  846. */
  847. function simplenews_user_login(&$edit, $account) {
  848. // Subscriptions of users that did sign up by themselves have to be
  849. // activated first at their first login (-> account::access = 0)
  850. if ($account->access == 0) {
  851. $query = db_update('simplenews_subscriber')
  852. ->fields(array('activated' => SIMPLENEWS_SUBSCRIPTION_ACTIVE))
  853. ->condition('uid', $account->uid)
  854. ->execute();
  855. }
  856. }
  857. /**
  858. * Implements hook_user_presave().
  859. *
  860. * User data (mail, status, language) is synchronized with subscriber.
  861. * This function handles existing user account, simplenews_user_insert takes
  862. * care of new accounts.
  863. * @see simplenews_user_insert()
  864. */
  865. function simplenews_user_presave(&$edit, $account, $category) {
  866. switch ($category) {
  867. case 'account':
  868. // We only process existing accounts.
  869. if (!empty($account->uid)) {
  870. $subscriber = simplenews_subscriber_load_by_uid($account->uid);
  871. if ($subscriber) {
  872. // Update mail, status and language if they are changed.
  873. if (isset($edit['mail'])) {
  874. $subscriber->mail = $edit['mail'];
  875. }
  876. if (isset($edit['status']) && variable_get('simplenews_sync_account', TRUE)) {
  877. $subscriber->activated = $edit['status'];
  878. }
  879. if (isset($edit['language'])) {
  880. $subscriber->language = $edit['language'];
  881. }
  882. simplenews_subscriber_save($subscriber);
  883. }
  884. }
  885. break;
  886. }
  887. }
  888. /**
  889. * Implements hook_user_cancel().
  890. */
  891. function simplenews_user_cancel($edit, $account, $method) {
  892. // Deactivate subscriber when account is disabled via cancel user.
  893. if ($account) {
  894. $query = db_update('simplenews_subscriber')
  895. ->fields(array('activated' => SIMPLENEWS_SUBSCRIPTION_INACTIVE))
  896. ->condition('uid', $account->uid)
  897. ->execute();
  898. }
  899. }
  900. /**
  901. * Implements hook_user_delete().
  902. */
  903. function simplenews_user_delete($account) {
  904. // Delete subscription when account is removed.
  905. $subscriber = simplenews_subscriber_load_by_mail($account->mail);
  906. if ($subscriber) {
  907. simplenews_subscriber_delete($subscriber);
  908. }
  909. }
  910. /**
  911. * Implements hook_user_categories().
  912. */
  913. function simplenews_user_categories() {
  914. $output[] = array(
  915. 'name' => 'simplenews',
  916. 'title' => t('Newsletters'),
  917. 'weight' => 10,
  918. 'access callback' => 'simplenews_subscription_edit_access',
  919. );
  920. return $output;
  921. }
  922. /**
  923. * Access callback for user newsletter editing.
  924. */
  925. function simplenews_subscription_edit_access($account) {
  926. global $user;
  927. // Disallow anonymous.
  928. if (!$account || $account->uid == 0) {
  929. return FALSE;
  930. }
  931. // Allow edit own subscription.
  932. if ($user->uid == $account->uid) {
  933. if (user_access('subscribe to newsletters', $account)) {
  934. return TRUE;
  935. }
  936. }
  937. // Allow administrator to edit account's subscription.
  938. if (user_access('administer users')) {
  939. return TRUE;
  940. }
  941. return FALSE;
  942. }
  943. /**
  944. * Implements hook_form_FORM_ID_alter().
  945. *
  946. * Add simplenews subscription management form to account category 'Newsletters'
  947. */
  948. function simplenews_form_user_profile_form_alter(&$form, &$form_state) {
  949. if ($form['#user_category'] == 'simplenews') {
  950. $subscription = simplenews_subscriber_load_by_mail($form['#user']->mail);
  951. module_load_include('inc', 'simplenews', 'includes/simplenews.subscription');
  952. simplenews_subscriptions_account_form($form, $form_state, $subscription);
  953. $form['subscriptions']['#title'] = t('Newsletter subscriptions');
  954. unset($form['update'], $form['subscriptions']['mail']);
  955. }
  956. }
  957. /**
  958. * Implements hook_user_view().
  959. */
  960. function simplenews_user_view($account, $build_mode) {
  961. global $user;
  962. if ($user->uid == $account->uid || user_access('administer users')) {
  963. $account->content['simplenews'] = array(
  964. '#type' => 'user_profile_category',
  965. '#title' => t('Newsletters'),
  966. );
  967. // Collect newsletter to which the current user is subscribed.
  968. // 'hidden' newsletters are not listed.
  969. $newsletters = simplenews_category_get_visible();
  970. $subscription = simplenews_subscriber_load_by_mail($account->mail);
  971. foreach ($newsletters as $newsletter) {
  972. if (isset($subscription->newsletter_subscription[$newsletter->tid]) && $subscription->newsletter_subscription[$newsletter->tid]->status == TRUE) {
  973. $links[] = l(_simplenews_newsletter_name($newsletter), 'taxonomy/term/' . $newsletter->tid);
  974. }
  975. }
  976. if (isset($links)) {
  977. // @todo replace with theme('links', $links) to form a list of newsletters?
  978. $links = implode(', ', $links);
  979. }
  980. else {
  981. $links = t('None');
  982. }
  983. // When a user has no permission to subscribe and is not subscribed
  984. // we do not display the 'no subscriptions' message.
  985. if (user_access('subscribe to newsletters') || $links != t('None')) {
  986. $account->content['simplenews']['subscriptions'] = array(
  987. '#type' => 'user_profile_item',
  988. '#title' => t('Subscribed to'),
  989. '#markup' => $links,
  990. );
  991. }
  992. if (user_access('subscribe to newsletters')) {
  993. $account->content['simplenews']['my_newsletters'] = array(
  994. '#type' => 'user_profile_item',
  995. '#title' => '',
  996. '#markup' => t('Manage <a href="!url">subscriptions</a>', array('!url' => url('user/' . $account->uid . '/edit/simplenews'))),
  997. );
  998. }
  999. }
  1000. }
  1001. /**
  1002. * Implements hook_block_info().
  1003. */
  1004. function simplenews_block_info() {
  1005. $blocks = array();
  1006. // Special block for multi
  1007. $blocks[0] = array(
  1008. 'info' => t('Newsletter: Multi Subscription'),
  1009. );
  1010. // Only list a block if the newsletter is not 'hidden' and marked to provide a block.
  1011. foreach (simplenews_categories_load_multiple(array(), array('block' => '1', 'show_all' => FALSE)) as $category) {
  1012. //@todo 1. without form -> by role; 2. with form -> user caching with refresh on subscribe/unsubscribe (option as setting) or no caching
  1013. $blocks[$category->tid] = array(
  1014. 'info' => t('Newsletter: @title', array('@title' => _simplenews_newsletter_name($category))),
  1015. // @todo Use block's own settings?
  1016. 'cache' => variable_get('simplenews_block_f_' . $category->tid, 1) ? DRUPAL_NO_CACHE : DRUPAL_CACHE_PER_ROLE,
  1017. );
  1018. }
  1019. return $blocks;
  1020. }
  1021. /**
  1022. * Implements hook_block_configure().
  1023. */
  1024. function simplenews_block_configure($delta = '') {
  1025. // Special block for multi
  1026. if ($delta == 0) {
  1027. $form['simplenews_block_multiple']['simplenews_block_m_multiple'] = array(
  1028. '#type' => 'textfield',
  1029. '#title' => t('Block message'),
  1030. '#size' => 60,
  1031. '#maxlength' => 255,
  1032. // @todo: clean localization / i18n needed
  1033. '#default_value' => variable_get('simplenews_block_m_multiple', t('Select the newsletter(s) to which you want to subscribe or unsubscribe.')),
  1034. );
  1035. }
  1036. else {
  1037. $form['simplenews_block_' . $delta]['simplenews_block_m_' . $delta] = array(
  1038. '#type' => 'textfield',
  1039. '#title' => t('Block message'),
  1040. '#size' => 60,
  1041. '#maxlength' => 255,
  1042. '#default_value' => variable_get('simplenews_block_m_' . $delta, t('Stay informed on our latest news!')),
  1043. );
  1044. $form['simplenews_block_' . $delta]['simplenews_block_f_' . $delta] = array(
  1045. '#type' => 'radios',
  1046. '#title' => t('Subscription interface'),
  1047. '#options' => array('1' => t('Subscription form'), '0' => t('Link to form')),
  1048. '#description' => t("Note: this requires permission 'subscribe to newsletters'."),
  1049. '#default_value' => variable_get('simplenews_block_f_' . $delta, 1),
  1050. );
  1051. $form['simplenews_block_' . $delta]['simplenews_block_l_' . $delta] = array(
  1052. '#type' => 'checkbox',
  1053. '#title' => t('Display link to previous issues'),
  1054. '#return_value' => 1,
  1055. '#default_value' => variable_get('simplenews_block_l_' . $delta, 1),
  1056. );
  1057. $form['simplenews_block_' . $delta]['simplenews_block_i_status_' . $delta] = array(
  1058. '#type' => 'checkbox',
  1059. '#title' => t('Display previous issues'),
  1060. '#return_value' => 1,
  1061. '#default_value' => variable_get('simplenews_block_i_status_' . $delta, 0),
  1062. );
  1063. $form['simplenews_block_' . $delta]['simplenews_block_i_' . $delta] = array(
  1064. '#type' => 'select',
  1065. '#title' => t('Number of issues to display'),
  1066. '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
  1067. '#default_value' => variable_get('simplenews_block_i_' . $delta, 5),
  1068. );
  1069. $form['simplenews_block_' . $delta]['simplenews_block_r_' . $delta] = array(
  1070. '#type' => 'checkbox',
  1071. '#title' => t('Display RSS-feed icon'),
  1072. '#return_value' => 1,
  1073. '#default_value' => variable_get('simplenews_block_r_' . $delta, 1),
  1074. );
  1075. }
  1076. return $form;
  1077. }
  1078. /**
  1079. * Implements hook_block_save().
  1080. */
  1081. function simplenews_block_save($delta = '', $edit = array()) {
  1082. if ($delta == 0) {
  1083. variable_set('simplenews_block_m_multiple', $edit['simplenews_block_m_multiple']);
  1084. }
  1085. else {
  1086. variable_set('simplenews_block_m_' . $delta, $edit['simplenews_block_m_' . $delta]);
  1087. variable_set('simplenews_block_f_' . $delta, $edit['simplenews_block_f_' . $delta]);
  1088. variable_set('simplenews_block_l_' . $delta, $edit['simplenews_block_l_' . $delta]);
  1089. variable_set('simplenews_block_i_status_' . $delta, $edit['simplenews_block_i_status_' . $delta]);
  1090. variable_set('simplenews_block_i_' . $delta, $edit['simplenews_block_i_' . $delta]);
  1091. variable_set('simplenews_block_r_' . $delta, $edit['simplenews_block_r_' . $delta]);
  1092. }
  1093. }
  1094. /**
  1095. * Implements hook_block_view().
  1096. */
  1097. function simplenews_block_view($delta = '') {
  1098. // Special block for multi
  1099. if ($delta == 0) {
  1100. if (user_access('subscribe to newsletters')) {
  1101. module_load_include('inc', 'simplenews', 'includes/simplenews.subscription');
  1102. // render block only if permitted
  1103. $block = array(
  1104. 'subject' => t('Newsletters'),
  1105. 'content' => theme('simplenews_multi_block'),
  1106. );
  1107. return $block;
  1108. }
  1109. return NULL;
  1110. }
  1111. else {
  1112. $newsletters = simplenews_categories_load_multiple(array(), array('block' => '1', 'show_all' => FALSE));
  1113. // Only display a block if $delta is a valid newsletter term id.
  1114. if (in_array($delta, array_keys($newsletters))) {
  1115. // $delta is validated, the block can be displayed.
  1116. $block = array(
  1117. 'subject' => check_plain(_simplenews_newsletter_name($newsletters[$delta])),
  1118. 'content' => theme(array('simplenews_block__' . $delta, 'simplenews_block'), array('tid' => $delta)),
  1119. );
  1120. return $block;
  1121. }
  1122. }
  1123. }
  1124. /**
  1125. * Implements hook_forms().
  1126. *
  1127. * All form blocks are build using simplenews_block_form().
  1128. * hook_forms() is required to provide unique form id for each block form.
  1129. */
  1130. function simplenews_forms($form_id, $args) {
  1131. $forms = array();
  1132. $match = preg_match('/simplenews_block_form_(\d+)/', $form_id, $matches);
  1133. if ($match) {
  1134. module_load_include('inc', 'simplenews', 'includes/simplenews.subscription');
  1135. $forms['simplenews_block_form_' . $matches[1]] = array(
  1136. 'callback' => 'simplenews_block_form',
  1137. 'callback arguments' => array($matches[1]),
  1138. );
  1139. }
  1140. return $forms;
  1141. }
  1142. /**
  1143. * Load a user or creates a dummy anonymous user.
  1144. *
  1145. * @return
  1146. * A user object if a user with that mail address exists, otherwise an object
  1147. * with the properties mail and uid (set to 0).
  1148. *
  1149. * @ingroup subscriber
  1150. */
  1151. function simplenews_load_user_by_mail($mail) {
  1152. $account = user_load_by_mail($mail);
  1153. if ($account === FALSE) {
  1154. // Construct anonymous user since we don't have a user that matches that e-mail.
  1155. $account = new stdClass();
  1156. $account->uid = 0;
  1157. $account->mail = $mail;
  1158. }
  1159. return $account;
  1160. }
  1161. /**
  1162. * Subscribe a user to a newsletter or send a confirmation mail.
  1163. *
  1164. * The $confirm parameter determines the action:
  1165. * FALSE = The user is subscribed
  1166. * TRUE = User receives an email to verify the address and complete the subscription
  1167. * A new subscription account is created when the user is subscribed to the first newsletter
  1168. *
  1169. * @param string $mail
  1170. * The email address to subscribe to the newsletter.
  1171. * @param integer $tid
  1172. * The term ID of the newsletter.
  1173. * @param boolean $confirm
  1174. * TRUE = send confirmation mail; FALSE = subscribe immediate to the newsletter
  1175. * @param string $preferred_language
  1176. * The language code (i.e. 'en', 'nl') of the user preferred language.
  1177. * Use '' for the site default language.
  1178. * Use NULL for the language of the current page.
  1179. * @param string $source
  1180. * Indication for source of subscription. Simplenews uses these sources:
  1181. * website: via any website form (with or without confirmation email)
  1182. * mass subscribe: mass admin UI
  1183. * mass unsubscribe: mass admin UI
  1184. * action: Drupal actions
  1185. *
  1186. * @ingroup subscription
  1187. */
  1188. function simplenews_subscribe_user($mail, $tid, $confirm = TRUE, $source = 'unknown', $preferred_language = NULL) {
  1189. global $language;
  1190. // Get current subscriptions if any.
  1191. $subscriber = simplenews_subscriber_load_by_mail($mail);
  1192. // If user is not subscribed to ANY newsletter, create a subscription account
  1193. if (!$subscriber) {
  1194. // To subscribe a user:
  1195. // - Fetch the users uid.
  1196. // - Determine the user preferred language.
  1197. // - Add the user to the database.
  1198. // - Get the full subscription object based on the mail address.
  1199. // Note that step 3 gets subscription data based on mail address because the uid can be 0 (for anonymous users)
  1200. $account = simplenews_load_user_by_mail($mail);
  1201. // If the site is multilingual:
  1202. // - Anonymous users are subscribed with their preferred language
  1203. // equal to the language of the current page.
  1204. // - Registered users will be subscribed with their default language as
  1205. // set in their account settings.
  1206. // By default the preferred language is not set.
  1207. if (variable_get('language_count', 1) > 1) {
  1208. if ($account->uid) {
  1209. $preferred_language = $account->language;
  1210. }
  1211. else {
  1212. $preferred_language = isset($preferred_language) ? $preferred_language : $language->language;
  1213. }
  1214. }
  1215. else {
  1216. $preferred_language = '';
  1217. }
  1218. $subscriber = new stdClass();
  1219. $subscriber->mail = $mail;
  1220. $subscriber->uid = $account->uid;
  1221. $subscriber->language = $preferred_language;
  1222. $subscriber->activated = 1;
  1223. simplenews_subscriber_save($subscriber);
  1224. }
  1225. if ($confirm) {
  1226. // Create an unconfirmed subscription object if it doesn't exist yet.
  1227. if (!isset($subscriber->tids[$tid])) {
  1228. $subscription = new stdClass();
  1229. $subscription->snid = $subscriber->snid;
  1230. $subscription->tid = $tid;
  1231. $subscription->status = SIMPLENEWS_SUBSCRIPTION_STATUS_UNCONFIRMED;
  1232. $subscription->timestamp = REQUEST_TIME;
  1233. $subscription->source = $source;
  1234. simplenews_subscription_save($subscription);
  1235. $subscriber->newsletter_subscription[$tid] = $subscription;
  1236. }
  1237. simplenews_confirmation_send('subscribe', $subscriber, simplenews_category_load($tid));
  1238. }
  1239. elseif (!isset($subscriber->tids[$tid])) {
  1240. // Subscribe the user if not already subscribed.
  1241. // @todo rewrite if subscription object is loaded in $subscriber->tids[$tid]
  1242. $subscription = new stdClass();
  1243. $subscription->snid = $subscriber->snid;
  1244. $subscription->tid = $tid;
  1245. $subscription->status = SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED;
  1246. $subscription->timestamp = REQUEST_TIME;
  1247. $subscription->source = $source;
  1248. simplenews_subscription_save($subscription);
  1249. $subscriber->newsletter_subscription[$tid] = $subscription;
  1250. $subscriber->tids[$tid] = $tid;
  1251. module_invoke_all('simplenews_subscribe_user', $subscriber, $subscription);
  1252. }
  1253. return TRUE;
  1254. }
  1255. /**
  1256. * Unsubscribe a user from a mailing list or send a confirmation mail.
  1257. *
  1258. * The $confirm parameter determines the action:
  1259. * FALSE = The user is unsubscribed
  1260. * TRUE = User receives an email to verify the address and complete the subscription cancellation
  1261. *
  1262. * @param $mail
  1263. * The email address to unsubscribe from the mailing list.
  1264. * @param $tid
  1265. * The term ID of the list.
  1266. * @param $confirm
  1267. * If TRUE, send a confirmation mail; if FALSE, unsubscribe immediately.
  1268. * @param $source
  1269. * Indicates the unsubscribe source. Simplenews uses these sources:
  1270. * - website: Via any website form (with or without confirmation email).
  1271. * - mass subscribe: Mass admin UI.
  1272. * - mass unsubscribe: Mass admin UI.
  1273. * - action: Drupal actions.
  1274. *
  1275. * @ingroup subscription
  1276. */
  1277. function simplenews_unsubscribe_user($mail, $tid, $confirm = TRUE, $source = 'unknown') {
  1278. $subscriber = simplenews_subscriber_load_by_mail($mail);
  1279. // The unlikely case that a user is unsubscribed from a non existing mailing list is logged
  1280. if (!$category = simplenews_category_load($tid)) {
  1281. watchdog('simplenews', 'Attempt to unsubscribe from non existing mailing list ID %id', array('%id' => $tid), WATCHDOG_ERROR);
  1282. return FALSE;
  1283. }
  1284. if ($confirm) {
  1285. // Make sure the mail address is set.
  1286. if (empty($subscriber)) {
  1287. $subscriber = new stdClass();
  1288. $subscriber->mail = $mail;
  1289. }
  1290. simplenews_confirmation_send('unsubscribe', $subscriber, simplenews_category_load($tid));
  1291. }
  1292. elseif (isset($subscriber->tids[$tid])) {
  1293. // Unsubscribe the user from the mailing list.
  1294. $subscriber->newsletter_subscription[$tid]->status = SIMPLENEWS_SUBSCRIPTION_STATUS_UNSUBSCRIBED;
  1295. $subscriber->newsletter_subscription[$tid]->timestamp = REQUEST_TIME;
  1296. $subscriber->newsletter_subscription[$tid]->source = $source;
  1297. simplenews_subscription_save($subscriber->newsletter_subscription[$tid]);
  1298. unset($subscriber->tids[$tid]);
  1299. // Clear eventually existing mail spool rows for this subscriber.
  1300. module_load_include('inc', 'simplenews', 'includes/simplenews.mail');
  1301. simplenews_delete_spool(array('snid' => $subscriber->snid, 'tid' => $tid));
  1302. module_invoke_all('simplenews_unsubscribe_user', $subscriber, $subscriber->newsletter_subscription[$tid]);
  1303. }
  1304. return TRUE;
  1305. }
  1306. /**
  1307. * Check if the email address is subscribed to the given mailing list.
  1308. *
  1309. * @param string $mail
  1310. * The email address to be checkd.
  1311. * @param integer $tid
  1312. * The mailing list id.
  1313. *
  1314. * @return boolean
  1315. * TRUE if the email address is subscribed; otherwise false.
  1316. *
  1317. * @ingroup subscription
  1318. *
  1319. * @todo Caching should be done in simplenews_load_user_by_mail().
  1320. */
  1321. function simplenews_user_is_subscribed($mail, $tid) {
  1322. $subscribed = &drupal_static(__FUNCTION__, array());
  1323. if (!isset($subscribed[$mail][$tid])) {
  1324. $subscriber = simplenews_subscriber_load_by_mail($mail);
  1325. // Check that a subscriber was found, he is active and subscribed to the
  1326. // requested tid.
  1327. $subscribed[$mail][$tid] = $subscriber && $subscriber->activated && isset($subscriber->tids[$tid]);
  1328. }
  1329. return $subscribed[$mail][$tid];
  1330. }
  1331. /**
  1332. * Returns a list of active subscriptions for a given newsletter category.
  1333. *
  1334. * WARNING: Use with caution - this might return a huge list
  1335. *
  1336. * @param $tid
  1337. * The newsletter category term id.
  1338. *
  1339. * @return
  1340. * An array keyed by the mail address, containing another array with the keys
  1341. * mail, uid, language, snid and status.
  1342. *
  1343. * @ingroup subscription
  1344. */
  1345. function simplenews_get_subscriptions_by_list($tid) {
  1346. $query = db_select('simplenews_subscriber', 'sn');
  1347. $query->innerJoin('simplenews_subscription', 'ss', 'ss.snid = sn.snid');
  1348. $query->fields('sn', array('mail', 'uid', 'language', 'snid'))
  1349. ->fields('ss', array('status'))
  1350. ->condition('sn.activated', 1)
  1351. ->condition('ss.tid', $tid)
  1352. ->condition('ss.status', SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED);
  1353. return $query->execute()->fetchAllAssoc('mail');
  1354. }
  1355. /**
  1356. * Update subscriber objects in the database.
  1357. *
  1358. * @param $conditions
  1359. * Array of selection conditions. e.g. array('tid' => 5, 'snid' => 12).
  1360. * @param $data
  1361. * Associative array of database fields to be updated.
  1362. *
  1363. * @todo Replace with simplenews_subscription_save() ?
  1364. *
  1365. * @ingroup subscription
  1366. */
  1367. function simplenews_subscription_update($conditions = array(), $data) {
  1368. $query = db_update('simplenews_subscription');
  1369. foreach ($conditions as $key => $condition) {
  1370. $query->condition($key, $condition);
  1371. }
  1372. $query->fields($data);
  1373. $query->execute();
  1374. }
  1375. /**
  1376. * Save a subscription.
  1377. *
  1378. * @ingroup subscription
  1379. */
  1380. function simplenews_subscription_save($subscription) {
  1381. db_merge('simplenews_subscription')
  1382. ->key(array('tid' => $subscription->tid))
  1383. ->key(array('snid' => $subscription->snid))
  1384. ->fields(array(
  1385. 'snid' => $subscription->snid,
  1386. 'tid' => $subscription->tid,
  1387. 'status' => $subscription->status,
  1388. 'timestamp' => $subscription->timestamp,
  1389. 'source' => $subscription->source,
  1390. ))
  1391. ->execute();
  1392. }
  1393. /**
  1394. * Delete subscriptions.
  1395. *
  1396. * @param $conditions
  1397. * An associative array of conditions matching the records to be delete.
  1398. * Example: array('tid' => 5, 'snid' => 12)
  1399. * Delete the subscription of subscriber 12 to newsletter tid 5.
  1400. *
  1401. * @ingroup subscription
  1402. */
  1403. function simplenews_subscription_delete($conditions = array()) {
  1404. $query = db_delete('simplenews_subscription');
  1405. foreach ($conditions as $key => $condition) {
  1406. $query->condition($key, $condition);
  1407. }
  1408. $query->execute();
  1409. }
  1410. /**
  1411. * Load a simplenews newsletter subscriber object.
  1412. *
  1413. * @param $snid
  1414. * Simplenews subscriber ID.
  1415. * @return
  1416. * Newsletter subscriber object, FALSE if subscriber does not exist.
  1417. *
  1418. * @ingroup subscriber
  1419. */
  1420. function simplenews_subscriber_load($snid, $reset = FALSE) {
  1421. $subscribers = simplenews_subscriber_load_multiple(array($snid), $reset);
  1422. return $subscribers ? reset($subscribers) : FALSE;
  1423. }
  1424. /**
  1425. * Load a simplenews newsletter subscriber object.
  1426. *
  1427. * @param $mail
  1428. * Subscriber e-mail address.
  1429. * @return
  1430. * Newsletter subscriber object, FALSE if subscriber does not exist.
  1431. *
  1432. * @ingroup subscriber
  1433. */
  1434. function simplenews_subscriber_load_by_mail($mail) {
  1435. $subscribers = simplenews_subscriber_load_multiple(array(), array('mail' => $mail));
  1436. return $subscribers ? reset($subscribers) : FALSE;
  1437. }
  1438. /**
  1439. * Load a simplenews newsletter subscriber object.
  1440. *
  1441. * @param $uid
  1442. * Subscriber user id.
  1443. * @return
  1444. * Newsletter subscriber object, FALSE if subscriber does not exist.
  1445. *
  1446. * @ingroup subscriber
  1447. */
  1448. function simplenews_subscriber_load_by_uid($uid) {
  1449. $subscribers = simplenews_subscriber_load_multiple(array(), array('uid' => $uid));
  1450. return $subscribers ? reset($subscribers) : FALSE;
  1451. }
  1452. /**
  1453. * Load a simplenews subscriber using the hash.
  1454. *
  1455. * @param $hash
  1456. * A hash generated by simplenews_generate_hash().
  1457. *
  1458. * @return
  1459. * Simplenews subscriber object, FALSE if the hash is invalid or the
  1460. * corresponding subscriber does not exist.
  1461. */
  1462. function simplenews_subscriber_load_by_hash($hash) {
  1463. // Attempt to detect invalid format.
  1464. if (!preg_match('/.{10}[0-9]+t[0-9]+/', $hash)) {
  1465. return FALSE;
  1466. }
  1467. $md5 = drupal_substr($hash, 0, 10);
  1468. list($snid, $tid) = explode('t', drupal_substr($hash, 10));
  1469. $subscriber = simplenews_subscriber_load($snid);
  1470. if (!$subscriber) {
  1471. return FALSE;
  1472. }
  1473. // Check the hash if the comparison fails, return a not found error.
  1474. if ($md5 != drupal_substr(md5($subscriber->mail . simplenews_private_key()), 0, 10)) {
  1475. return FALSE;
  1476. }
  1477. return $subscriber;
  1478. }
  1479. /**
  1480. * Loads simplenews subscriber objects.
  1481. *
  1482. * @param $snids
  1483. * (Optional) Array of subscriber ID's.
  1484. * @param $conditions
  1485. * (Optional) Array of query conditions.
  1486. * @param $reset
  1487. * Reset the static cache.
  1488. *
  1489. * @return
  1490. * Array of subscriber objects that match the given ID's/conditions.
  1491. *
  1492. * @ingroup subscriber
  1493. */
  1494. function simplenews_subscriber_load_multiple($snids = array(), $conditions = array(), $reset = FALSE) {
  1495. $subscribers = &drupal_static(__FUNCTION__, array());
  1496. // Only cache if we load all records from the database.
  1497. // @todo Caching could be improved.
  1498. if (!$subscribers || $snids || $conditions || $reset) {
  1499. $subscribers = array();
  1500. $query = db_select('simplenews_subscriber', 'ss')
  1501. ->fields('ss');
  1502. if ($snids) {
  1503. $query->condition('snid', $snids);
  1504. }
  1505. if ($conditions) {
  1506. foreach ($conditions as $key => $condition) {
  1507. $query->condition($key, $condition);
  1508. }
  1509. }
  1510. $subscribers = $query->execute()->fetchAllAssoc('snid');
  1511. if (!empty($subscribers)) {
  1512. // Initialize default values and unserialize.
  1513. foreach ($subscribers as $subscriber) {
  1514. $subscriber->changes = unserialize($subscriber->changes);
  1515. $subscriber->tids = array();
  1516. }
  1517. $query = db_select('simplenews_subscription', 'ss')
  1518. ->fields('ss')
  1519. ->condition('snid', array_keys($subscribers));
  1520. foreach ($query->execute() as $subscription) {
  1521. if ($subscription->status == SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED) {
  1522. $subscribers[$subscription->snid]->tids[$subscription->tid] = $subscription->tid;
  1523. }
  1524. $subscribers[$subscription->snid]->newsletter_subscription[$subscription->tid] = $subscription;
  1525. }
  1526. }
  1527. }
  1528. return $subscribers;
  1529. }
  1530. /**
  1531. * Store subscriber object in the database.
  1532. *
  1533. * @ingroup subscriber
  1534. */
  1535. function simplenews_subscriber_save(&$subscriber) {
  1536. if (!empty($subscriber->snid)) {
  1537. db_update('simplenews_subscriber')
  1538. ->condition('snid', $subscriber->snid)
  1539. ->fields(array(
  1540. 'snid' => $subscriber->snid,
  1541. 'activated' => (int) $subscriber->activated,
  1542. 'mail' => $subscriber->mail,
  1543. 'uid' => $subscriber->uid,
  1544. 'language' => $subscriber->language,
  1545. 'changes' => serialize(isset($subscriber->changes) ? $subscriber->changes : array()),
  1546. ))
  1547. ->execute();
  1548. module_invoke_all('simplenews_subscriber_update', $subscriber);
  1549. }
  1550. elseif (empty($subscriber->snid)) {
  1551. $query = db_insert('simplenews_subscriber')
  1552. ->fields(array(
  1553. 'activated' => $subscriber->activated,
  1554. 'mail' => $subscriber->mail,
  1555. 'uid' => $subscriber->uid,
  1556. 'language' => $subscriber->language,
  1557. 'changes' => serialize(isset($subscriber->changes) ? $subscriber->changes : array()),
  1558. 'created' => REQUEST_TIME,
  1559. ));
  1560. $last_insert_id = $query->execute();
  1561. if ($last_insert_id !== FALSE) {
  1562. $subscriber->snid = $last_insert_id;
  1563. module_invoke_all('simplenews_subscriber_insert', $subscriber);
  1564. }
  1565. }
  1566. }
  1567. /**
  1568. * Delete subscriber and corresponding subscriptions from the database.
  1569. *
  1570. * @param $snid
  1571. * Simplenews subscriber object.
  1572. *
  1573. * @ingroup subscriber
  1574. */
  1575. function simplenews_subscriber_delete(stdClass $subscriber) {
  1576. simplenews_subscription_delete(array('snid' => $subscriber->snid));
  1577. db_delete('simplenews_subscriber')
  1578. ->condition('snid', $subscriber->snid)
  1579. ->execute();
  1580. module_invoke_all('simplenews_subscriber_delete', $subscriber);
  1581. }
  1582. /**
  1583. * Create a list of recent newsletters issues.
  1584. *
  1585. * @param integer $tid
  1586. * The newsletter category id.
  1587. * @param integer $count
  1588. * The number of newsletters.
  1589. *
  1590. * @ingroup issue
  1591. *
  1592. * @todo Replace this list by a View.
  1593. */
  1594. function simplenews_recent_newsletters($tid, $count = 5) {
  1595. $titles = '';
  1596. $query = db_select('node', 'n');
  1597. $query->innerJoin('simplenews_newsletter', 'sn', 'n.nid = sn.nid');
  1598. $query->fields('n', array('nid', 'title'))
  1599. ->condition('sn.tid', $tid)
  1600. ->condition('n.status', NODE_PUBLISHED)
  1601. ->condition('sn.status', SIMPLENEWS_STATUS_SEND_NOT, '<>')
  1602. ->orderBy('n.created', 'DESC')
  1603. ->range(0, $count);
  1604. $titles = array();
  1605. foreach ($query->execute() as $item) {
  1606. $titles[$item->nid]['data'] = l($item->title, 'node/' . $item->nid);
  1607. }
  1608. return $titles;
  1609. }
  1610. /**
  1611. * Implements hook_mail().
  1612. *
  1613. * Send simplenews mails using drupal mail API.
  1614. *
  1615. * @param $key
  1616. * Must be one of: node, test, subscribe, unsubscribe.
  1617. * @param $message
  1618. * The message array, containing at least the following keys:
  1619. * - from
  1620. * - headers: An array containing at least a 'From' key.
  1621. * - language: The preferred message language.
  1622. * @param array $params
  1623. * The parameter array, containing the following keys:
  1624. * - simplenews_source: An implementation of SimplenewsSourceInterface which
  1625. * provides the necessary information to build the newsletter mail.
  1626. * @see drupal_mail()
  1627. */
  1628. function simplenews_mail($key, &$message, $params) {
  1629. module_load_include('inc', 'simplenews', 'includes/simplenews.mail');
  1630. switch ($key) {
  1631. case 'node':
  1632. case 'test':
  1633. simplenews_build_newsletter_mail($message, $params['simplenews_source']);
  1634. break;
  1635. case 'subscribe':
  1636. simplenews_build_subscribe_mail($message, $params);
  1637. break;
  1638. break;
  1639. case 'subscribe_combined':
  1640. simplenews_build_combined_mail($message, $params);
  1641. break;
  1642. case 'unsubscribe':
  1643. simplenews_build_unsubscribe_mail($message, $params);
  1644. break;
  1645. }
  1646. // Debug message to check for outgoing emails messages.
  1647. // Debug message of node and test emails is set in simplenews_mail_mail().
  1648. if (variable_get('simplenews_debug', FALSE) && $key != 'node' && $key != 'test') {
  1649. watchdog('simplenews', 'Outgoing email. Message type: %type<br />Subject: %subject<br />Recipient: %to', array('%type' => $key, '%to' => $message['to'], '%subject' => $message['subject']), WATCHDOG_DEBUG);
  1650. }
  1651. }
  1652. /**
  1653. * Implements hook_views_api().
  1654. */
  1655. function simplenews_views_api() {
  1656. return array(
  1657. 'api' => 3,
  1658. 'path' => drupal_get_path('module', 'simplenews') . '/includes/views'
  1659. );
  1660. }
  1661. /**
  1662. * Get a simplenews newsletter category object.
  1663. *
  1664. * @param $tid
  1665. * Simplenews category ID.
  1666. * @return
  1667. * Newsletter category object.
  1668. * FALSE if category does not exist
  1669. *
  1670. * @ingroup newsletter
  1671. */
  1672. function simplenews_category_load($tid, $reset = FALSE) {
  1673. if (!is_numeric($tid)) {
  1674. return FALSE;
  1675. }
  1676. $categories = simplenews_categories_load_multiple(array($tid), $reset);
  1677. return $categories ? $categories[$tid] : FALSE;
  1678. }
  1679. /**
  1680. * Get list of simplenews categories with translated names.
  1681. *
  1682. * @return
  1683. * array of category names. Translated if required.
  1684. *
  1685. * @ingroup newsletter.
  1686. */
  1687. function simplenews_category_list() {
  1688. $categories = simplenews_categories_load_multiple();
  1689. $cats = array();
  1690. foreach ($categories as $key => $category) {
  1691. $cats[$key] = check_plain(_simplenews_newsletter_name($category));
  1692. }
  1693. return $cats;
  1694. }
  1695. /**
  1696. * @todo
  1697. *
  1698. * @ingroup newsletter
  1699. */
  1700. function simplenews_categories_load_multiple($tids = array(), $conditions = array(), $reset = FALSE) {
  1701. $categories = &drupal_static(__FUNCTION__, array());
  1702. // Only cache if we load all records from the database. This could be improved.
  1703. if (!$categories || $tids || $conditions || $reset) {
  1704. $categories = array();
  1705. $query = db_select('simplenews_category', 'sc');
  1706. // This function might be called when the corresponding taxonomy term was
  1707. // already deleted.
  1708. $query->leftJoin('taxonomy_term_data', 't', 't.tid = sc.tid');
  1709. $query->fields('sc')
  1710. ->fields('t', array('name', 'description', 'weight', 'vid'))
  1711. ->orderBy('t.weight', 'ASC');
  1712. if ($tids) {
  1713. $query->condition('sc.tid', $tids);
  1714. }
  1715. if ($conditions) {
  1716. foreach ($conditions as $key => $condition) {
  1717. if ($key == 'show_all') {
  1718. if (!$condition) {
  1719. $query->condition('opt_inout', SIMPLENEWS_OPT_INOUT_HIDDEN, '<>');
  1720. }
  1721. }
  1722. else {
  1723. $query->condition($key, $condition);
  1724. }
  1725. }
  1726. }
  1727. $categories = $query->execute()->fetchAllAssoc('tid');
  1728. }
  1729. return $categories;
  1730. }
  1731. /**
  1732. * Store newsletter category in the database.
  1733. *
  1734. * @param $category
  1735. * Newsletter category object
  1736. *
  1737. * @ingroup newsletter
  1738. */
  1739. function simplenews_category_save($category) {
  1740. db_merge('simplenews_category')
  1741. ->key(array('tid' => $category->tid))
  1742. ->fields(array(
  1743. 'tid' => $category->tid,
  1744. 'format' => $category->format,
  1745. 'priority' => $category->priority,
  1746. 'receipt' => $category->receipt,
  1747. 'from_name' => $category->from_name,
  1748. 'from_address' => $category->from_address,
  1749. 'email_subject' => $category->email_subject,
  1750. 'hyperlinks' => $category->hyperlinks,
  1751. 'new_account' => $category->new_account,
  1752. 'opt_inout' => $category->opt_inout,
  1753. 'block' => $category->block,
  1754. ))
  1755. ->execute();
  1756. module_invoke_all('simplenews_category_update', $category);
  1757. }
  1758. /**
  1759. * Delete newsletter category from the database.
  1760. *
  1761. * @param $category
  1762. * Simplenews category object or category ID.
  1763. *
  1764. * @ingroup newsletter
  1765. */
  1766. function simplenews_category_delete($category) {
  1767. if (!is_object($category)) {
  1768. $category = simplenews_category_load($category);
  1769. }
  1770. if ($category) {
  1771. db_delete('simplenews_category')
  1772. ->condition('tid', $category->tid)
  1773. ->execute();
  1774. // Delete subscriptions
  1775. simplenews_subscription_delete(array('tid' => $category->tid));
  1776. drupal_set_message(t('All subscriptions to newsletter %newsletter have been deleted.', array('%newsletter' => _simplenews_newsletter_name($category))));
  1777. // Delete subscription block
  1778. db_delete('block')
  1779. ->condition('module', 'simplenews')
  1780. ->condition('delta', $category->tid)
  1781. ->execute();
  1782. module_invoke_all('simplenews_category_delete', $category);
  1783. }
  1784. }
  1785. /**
  1786. * Loads all visible newsletter categories.
  1787. *
  1788. * Does not include categories with the opt-out/opt-in setting set to hidden.
  1789. *
  1790. * @ingroup newsletter
  1791. */
  1792. function simplenews_category_get_visible() {
  1793. $categories = &drupal_static(__FUNCTION__, NULL);
  1794. if (!isset($categories)) {
  1795. $categories = simplenews_categories_load_multiple(array(), array('show_all' => FALSE));
  1796. }
  1797. return $categories;
  1798. }
  1799. /**
  1800. * @todo
  1801. *
  1802. * @ingroup issue
  1803. */
  1804. function simplenews_newsletter_load($nid, $reset = FALSE) {
  1805. $conditions = array();
  1806. $newsletters = simplenews_newsletter_load_multiple(array($nid), $conditions, $reset);
  1807. return $newsletters ? reset($newsletters) : FALSE;
  1808. }
  1809. /**
  1810. * @todo
  1811. *
  1812. * @ingroup issue
  1813. */
  1814. function simplenews_newsletter_load_multiple($nids = array(), $conditions = array(), $reset = FALSE) {
  1815. $newsletters = &drupal_static(__FUNCTION__, array());
  1816. // We only cache if all records are loaded from the database,
  1817. // unless reset is forced.
  1818. if (!$newsletters || $nids || $conditions || $reset) {
  1819. $newsletters = array();
  1820. $query = db_select('simplenews_newsletter', 'sn');
  1821. $query->innerJoin('taxonomy_term_data', 't', 't.tid = sn.tid');
  1822. $query->fields('sn')
  1823. ->fields('t', array('name', 'description', 'weight'))
  1824. ->orderBy('t.weight', 'ASC');
  1825. if ($nids) {
  1826. $query->condition('nid', $nids);
  1827. }
  1828. if ($conditions) {
  1829. foreach ($conditions as $key => $condition) {
  1830. $query->condition($key, $condition);
  1831. }
  1832. }
  1833. $newsletters = $query->execute()->fetchAllAssoc('nid');
  1834. }
  1835. return $newsletters;
  1836. }
  1837. /**
  1838. * Store newsletter object in the database.
  1839. *
  1840. * @param $newsletter
  1841. * Simplenews newsletter object.
  1842. *
  1843. * @ingroup issue
  1844. */
  1845. function simplenews_newsletter_save($newsletter) {
  1846. db_merge('simplenews_newsletter')
  1847. ->key(array('nid' => $newsletter->nid))
  1848. ->fields(array(
  1849. 'tid' => $newsletter->tid,
  1850. 'status' => $newsletter->status,
  1851. ))
  1852. ->execute();
  1853. }
  1854. /**
  1855. * Delete simplenews newsletter from the database.
  1856. *
  1857. * @param $newsletter
  1858. * Simplenews newsletter object or nid.
  1859. *
  1860. * @ingroup issue
  1861. */
  1862. function simplenews_newsletter_delete($newsletter) {
  1863. if (!is_object($newsletter)) {
  1864. $newsletter = simplenews_newsletter_load($newsletter);
  1865. }
  1866. if ($newsletter) {
  1867. db_delete('simplenews_newsletter')
  1868. ->condition('nid', $newsletter->nid)
  1869. ->execute();
  1870. }
  1871. }
  1872. /**
  1873. * Create a 32 character identifier.
  1874. */
  1875. function simplenews_private_key() {
  1876. $key = variable_get('simplenews_private_key', FALSE);
  1877. if (!$key) {
  1878. // This will create a 32 character identifier (a 128 bit hex number) that is extremely difficult to predict
  1879. $key = md5(uniqid(rand()));
  1880. variable_set('simplenews_private_key', $key);
  1881. }
  1882. return $key;
  1883. }
  1884. /**
  1885. * Implements hook_help().
  1886. *
  1887. * @todo Rewrite help text to match the new terminology, the new data architecture, the new admin pages, the news node-form interface.
  1888. */
  1889. function simplenews_help($path, $arg) {
  1890. switch ($path) {
  1891. case 'admin/help#simplenews':
  1892. $help = "<p>" . t('Simplenews publishes and sends newsletters to lists of subscribers. Both anonymous and authenticated users can opt-in to different mailing lists.') . "</p>\n";
  1893. $help .= "<p>" . t('Simplenews uses nodes for <strong>newsletter issues</strong>. Newsletter issues are grouped by a <strong>newsletter taxonomy term</strong>. Node type and vocabulary are selectable. A newsletter is send to all email addresses which are subscribed to the newsletter. Newsletter issues can be sent only once. Large mailings should be sent by cron to balance the mailserver load.') . "</p>\n";
  1894. $help .= "<p>" . t('Simplenews adds elements to the newsletter node add/edit form to manage newsletter format and sending of the newsletter issue. A newsletter issue can be sent for test before sending officially.') . "</p>\n";
  1895. $help .= "<p>" . t('Both anonymous and authenticated users can <strong>opt-in and opt-out</strong> to a newsletter. A confirmation message is sent to anonymous users when they (un)subscribe. Users can (un)subscribe using a form and a block. A <strong>subscription block</strong> is available for each newsletter offering a subscription form, a link to recent newsletters and RSS feed. Email addresses can also be imported and exported via the subscription administration pages.') . "</p>\n";
  1896. $help .= "<h2>" . t('Configuration') . "</h2>\n";
  1897. $help .= '<ul>';
  1898. if (user_access('administer permissions')) {
  1899. $help .= '<li>' . l(t('Configure permissions'), 'admin/people/permissions', array('fragment' => 'module-simplenews')) . "</li>\n";
  1900. }
  1901. if (user_access('administer simplenews settings')) {
  1902. $help .= '<li>' . l(t('Configure Simplenews'), 'admin/config/services/simplenews/settings') . "</li>\n";
  1903. }
  1904. if (user_access('administer blocks')) {
  1905. $help .= '<li>' . t('Enable a newsletter <a href="@admin_blocks">subscription block</a>.', array('@admin_blocks' => url('admin/structure/block'))) . "</li>\n";
  1906. }
  1907. if (user_access('administer simplenews settings')) {
  1908. $help .= '<li>' . t('Manage your <a href="@newsletters">newsletters</a>, <a href="@sent">sent newsletters</a> and <a href="@subscriptions">subscriptions</a>.', array('@newsletters' => url('admin/config/services/simplenews'), '@sent' => url('admin/content/simplenews'), '@subscriptions' => url('admin/people/simplenews'))) . "</li>\n";
  1909. }
  1910. $help .= '</ul>';
  1911. $help .= "<p>" . t('For more information, see the online handbook entry for <a href="@handbook">Simplenews</a>.', array('@handbook', 'http://drupal.org/node/197057')) . "</p>\n";
  1912. return $help;
  1913. case 'node/add/simplenews':
  1914. $help = '<ul>';
  1915. $help .= '<li>' . t('Add this newsletter issue to a newsletter by selecting a newsletter from the select list. To send this newsletter issue, first save the node, then use the "Newsletter" tab.') . "</li>\n";
  1916. if (user_access('administer simplenews settings')) {
  1917. $help .= '<li>' . t('Set default send options at <a href="@configuration">Administration > Configuration > Web services > Newsletters</a>.', array('@configuration' => url('admin/config/services/simplenews'))) . "</li>\n";
  1918. }
  1919. if (user_access('administer newsletters')) {
  1920. $help .= '<li>' . t('Set newsletter specific options at <a href="@configuration">Administration > Content > Newsletters</a>.', array('@configuration' => url('admin/content/simplenews'))) . "</li>\n";
  1921. }
  1922. $help .= '</ul>';
  1923. return $help;
  1924. case 'admin/config/services/simplenews/newsletter':
  1925. $help = '<ul>';
  1926. $help .= '<li>' . t('These settings are default to all newsletters. Newsletter specific settings can be found at the <a href="@page">newsletter\'s settings page</a>.', array('@page' => url('admin/config/services/simplenews'))) . "</li>\n";
  1927. $help .= '<li>' . t('Install <a href="!mime_mail_url">Mime Mail</a> or <a href="!html_mail_url">HTML Mail</a> to send HTML emails or emails with attachments (both plain text and HTML).', array('!mime_mail_url' => 'http://drupal.org/project/mimemail', '!html_mail_url' => 'http://drupal.org/project/htmlmail')) . "</li>\n";
  1928. $help .= '</ul>';
  1929. return $help;
  1930. case 'admin/config/services/simplenews':
  1931. $help = '<p>' . t('Newsletter allow you to send periodic e-mails to subscribers. See <a href="!manage_subscribers">Newsletter subscriptions</a> for a listing of the subscribers', array('!manage_subscribers' => url('admin/people/simplenews')));
  1932. return $help;
  1933. case 'admin/config/services/simplenews/add':
  1934. $help = '<p>' . t('You can create different newsletters (or subjects) to categorize your news (e.g. Cats news, Dogs news, ...).') . "</p>\n";
  1935. return $help;
  1936. case 'admin/structure/types/manage/simplenews/display':
  1937. $help = '<p>' . t("'Plain' display settings apply to the content of emails send in plain text format. 'HTML' display settings apply to both HTML and plain text alternative content of emails send in HTML format.") . "</p>\n";
  1938. return $help;
  1939. }
  1940. }
  1941. /**
  1942. * Helper function to translate a newsletter name if required.
  1943. *
  1944. * @param object $newsletter
  1945. * Newsletter category object, typically from simplenews_category_load().
  1946. * Contains at least the following properties:
  1947. * - tid: The newsletter category id.
  1948. * - name: The newsletter name.
  1949. * @param string $langcode
  1950. * (optional) The language code. Defaults to $GLOBALS['language'].
  1951. *
  1952. * @return string
  1953. * The translated newsletter name.
  1954. */
  1955. function _simplenews_newsletter_name($newsletter, $langcode = NULL) {
  1956. if (module_exists('i18n_taxonomy')) {
  1957. return i18n_taxonomy_term_name($newsletter, $langcode);
  1958. }
  1959. return $newsletter->name;
  1960. }
  1961. /**
  1962. * Helper function to translate a newsletter description if required.
  1963. *
  1964. * @param object $newsletter
  1965. * Newsletter category object, typically from simplenews_category_load().
  1966. * Contains at least the following properties:
  1967. * - tid: The newsletter category id.
  1968. * - name: The newsletter name.
  1969. * @param string $langcode
  1970. * (optional) The language code. Defaults to $GLOBALS['language'].
  1971. *
  1972. * @return string
  1973. * The translated newsletter name.
  1974. */
  1975. function _simplenews_newsletter_description($newsletter, $langcode = NULL) {
  1976. if (module_exists('i18n_taxonomy')) {
  1977. return i18n_string(array('taxonomy', 'term', $newsletter->tid, 'description'), $newsletter->description, array('langcode' => $langcode));
  1978. }
  1979. return $newsletter->description;
  1980. }
  1981. /**
  1982. * Generate the hash key used for subscribe/unsubscribe link.
  1983. */
  1984. function simplenews_generate_hash($mail, $snid, $tid) {
  1985. return drupal_substr(md5($mail . simplenews_private_key()), 0, 10) . $snid . 't' . $tid;
  1986. }
  1987. /**
  1988. * Returns simplenews format options.
  1989. */
  1990. function simplenews_format_options() {
  1991. return array(
  1992. 'plain' => t('Plain'),
  1993. 'html' => t('HTML'),
  1994. );
  1995. }
  1996. /**
  1997. * Implements hook_variable_info().
  1998. */
  1999. function simplenews_variable_info($options) {
  2000. $variable['simplenews_confirm_subscribe_subject'] = array(
  2001. 'title' => t('Simplenews confirmation subject'),
  2002. 'localize' => TRUE,
  2003. );
  2004. $variable['simplenews_confirm_subscribe_unsubscribed'] = array(
  2005. 'title' => t('Simplenews subscription confirmation body'),
  2006. 'localize' => TRUE,
  2007. );
  2008. $variable['simplenews_confirm_subscribe_subscribed'] = array(
  2009. 'title' => t('Simplenews subscribed confirmation body (already subscribed)'),
  2010. 'localize' => TRUE,
  2011. );
  2012. $variable['simplenews_confirm_unsubscribe_subscribed'] = array(
  2013. 'title' => t('Simplenews unsubscription confirmation body'),
  2014. 'localize' => TRUE,
  2015. );
  2016. $variable['simplenews_confirm_unsubscribe_unsubscribed'] = array(
  2017. 'title' => t('Simplenews unsubscription confirmation body (not subscribed)'),
  2018. 'localize' => TRUE,
  2019. );
  2020. $variable['simplenews_confirm_combined_subject'] = array(
  2021. 'title' => t('Simplenews combined confirmation subject'),
  2022. 'localize' => TRUE,
  2023. );
  2024. $variable['simplenews_confirm_combined_body'] = array(
  2025. 'title' => t('Simplenews combined confirmation body'),
  2026. 'localize' => TRUE,
  2027. );
  2028. $variable['simplenews_confirm_combined_body_unchanged'] = array(
  2029. 'title' => t('Simplenews combined confirmation body when there are no changes.'),
  2030. 'localize' => TRUE,
  2031. );
  2032. $variable['simplenews_confirm_combined_line_subscribe_unsubscribed'] = array(
  2033. 'title' => t('Simplenews combined confirmation subscribe line'),
  2034. 'localize' => TRUE,
  2035. );
  2036. $variable['simplenews_confirm_combined_line_subscribe_subscribed'] = array(
  2037. 'title' => t('Simplenews combined confirmation already subscribed line'),
  2038. 'localize' => TRUE,
  2039. );
  2040. $variable['simplenews_confirm_combined_line_unsubscribe_subscribed'] = array(
  2041. 'title' => t('Simplenews combined confirmation unsubscribe line'),
  2042. 'localize' => TRUE,
  2043. );
  2044. $variable['simplenews_confirm_combined_line_unsubscribe_unsubscribed'] = array(
  2045. 'title' => t('Simplenews combined confirmation not subscribed line'),
  2046. 'localize' => TRUE,
  2047. );
  2048. return $variable;
  2049. }
  2050. /**
  2051. * Generate default and custom subscription confirmation email text.
  2052. *
  2053. * @param string $key
  2054. * Text identification key.
  2055. *
  2056. * @return
  2057. * Invitation text.
  2058. *
  2059. */
  2060. function simplenews_subscription_confirmation_text($key) {
  2061. $text = variable_get('simplenews_confirm_' . $key, FALSE);
  2062. // If administrator did not change the text, the variable is empty.
  2063. // We get the default here.
  2064. if (!$text) {
  2065. switch ($key) {
  2066. case 'subscribe_unsubscribed':
  2067. $text = t("We have received a request to subscribe [simplenews-subscriber:mail] to the [simplenews-category:name] newsletter on [site:name] website at [site:url]. To confirm please use the link below.\n\n[simplenews-subscriber:subscribe-url]");
  2068. break;
  2069. case 'subscribe_subscribed':
  2070. $text = t("We have received a request to subscribe [simplenews-subscriber:mail] to the [simplenews-category:name] newsletter on [site:name] website at [site:url]. However, this email is already subscribed to this newsletter. If you intended to unsubscribe please visit our site: [site:url]");
  2071. break;
  2072. case 'unsubscribe_subscribed':
  2073. $text = t("We have received a request to remove the [simplenews-subscriber:mail] from the [simplenews-category:name] mailing list on [site:name] website at [site:url]. To confirm please use the link below.\n\n[simplenews-subscriber:unsubscribe-url]");
  2074. break;
  2075. case 'unsubscribe_unsubscribed':
  2076. $text = t("We have received a request to remove the [simplenews-subscriber:mail] from the [simplenews-category:name] mailing list on [site:name] website at [site:url]. However, this email is not subscribed to this mailing list. If you intended to subscribe please visit our site at [site:url]");
  2077. break;
  2078. case 'subscribe_subject':
  2079. $text = t("Confirmation for [simplenews-category:name] from [site:name]");
  2080. break;
  2081. case 'combined_subject':
  2082. $text = t('Confirmation for [site:name]');
  2083. break;
  2084. case 'combined_body':
  2085. $text = t("We have received a request for the following subscription changes for [simplenews-subscriber:mail] at [site:url]:\n\n[changes-list]\n\nTo confirm please use the link below.\n\n[simplenews-subscriber:combined-url]");
  2086. break;
  2087. case 'combined_body_unchanged':
  2088. $text = t("We have received a request for the following subscription changes for [simplenews-subscriber:mail] at [site:url]:\n\n[changes-list]\n\nNo confirmation necessary because all requested changes equal the current state.");
  2089. break;
  2090. case 'combined_line_subscribe_unsubscribed';
  2091. $text = t('Subscribe to [simplenews-category:name]');
  2092. break;
  2093. case 'combined_line_subscribe_subscribed';
  2094. $text = t('Already subscribed to [simplenews-category:name]');
  2095. break;
  2096. case 'combined_line_unsubscribe_subscribed';
  2097. $text = t('Unsubscribe from [simplenews-category:name]');
  2098. break;
  2099. case 'combined_line_unsubscribe_unsubscribed';
  2100. $text = t('Already unsubscribed from [simplenews-category:name]');
  2101. break;
  2102. }
  2103. }
  2104. return $text;
  2105. }
  2106. /**
  2107. * Get defaults for the simplenews node form.
  2108. */
  2109. function _simplenews_get_node_form_defaults() {
  2110. $defaults = array(
  2111. 'tid' => '0',
  2112. 'status' => SIMPLENEWS_STATUS_SEND_NOT,
  2113. );
  2114. return $defaults;
  2115. }
  2116. /**
  2117. * Implements hook_theme().
  2118. *
  2119. */
  2120. function simplenews_theme() {
  2121. $path = drupal_get_path('module', 'simplenews');
  2122. return array(
  2123. 'simplenews_multi_block' => array(
  2124. 'template' => 'simplenews-multi-block',
  2125. 'arguments' => array(),
  2126. 'path' => $path . '/theme',
  2127. ),
  2128. 'simplenews_admin_categories' => array(
  2129. 'render element' => 'form',
  2130. ),
  2131. 'simplenews_block' => array(
  2132. 'render element' => 'tid',
  2133. 'template' => 'simplenews-block',
  2134. 'pattern' => 'simplenews_block__',
  2135. 'path' => $path . '/theme',
  2136. ),
  2137. 'simplenews_status' => array(
  2138. 'file' => 'simplenews.admin.inc',
  2139. 'path' => $path . '/includes',
  2140. 'variables' => array(
  2141. 'source' => NULL,
  2142. 'status' => NULL,
  2143. ),
  2144. ),
  2145. 'simplenews_newsletter_body' => array(
  2146. 'variables' => array(
  2147. 'build' => NULL,
  2148. 'category' => NULL,
  2149. 'language' => NULL,
  2150. ),
  2151. 'path' => $path . '/theme',
  2152. 'mail theme' => TRUE,
  2153. 'template' => 'simplenews-newsletter-body',
  2154. 'pattern' => 'simplenews-newsletter-body__',
  2155. ),
  2156. 'simplenews_newsletter_footer' => array(
  2157. 'variables' => array(
  2158. 'build' => NULL,
  2159. 'category' => NULL,
  2160. 'context' => NULL,
  2161. 'key' => NULL,
  2162. 'language' => NULL,
  2163. ),
  2164. 'path' => $path . '/theme',
  2165. 'mail theme' => TRUE,
  2166. 'template' => 'simplenews-newsletter-footer',
  2167. 'pattern' => 'simplenews-newsletter-footer__',
  2168. ),
  2169. 'simplenews_filter_form' => array(
  2170. 'render element' => 'form',
  2171. 'file' => 'simplenews.admin.inc',
  2172. 'path' => $path . '/includes',
  2173. ),
  2174. 'simplenews_field' => array(
  2175. 'render element' => 'element',
  2176. ),
  2177. );
  2178. }
  2179. /**
  2180. * Process variables to format the simplenews block.
  2181. *
  2182. * Collect data and apply access restrictions.
  2183. *
  2184. * @see simplenews-block.tpl.php
  2185. *
  2186. * @ingroup theming
  2187. */
  2188. function template_preprocess_simplenews_block(&$variables) {
  2189. global $user;
  2190. $tid = $variables['tid'];
  2191. $category = simplenews_category_load($tid);
  2192. // Set default values in case of missing permission.
  2193. $variables['form'] = '';
  2194. $variables['subscription_link'] = '';
  2195. $variables['newsletter_link'] = '';
  2196. $variables['issue_list'] = '';
  2197. $variables['rssfeed'] = '';
  2198. // Block content variables
  2199. $variables['message'] = check_plain(variable_get('simplenews_block_m_' . $tid, t('Stay informed on our latest news!')));
  2200. if (user_access('subscribe to newsletters')) {
  2201. module_load_include('inc', 'simplenews', 'simplenews.subscription');
  2202. $variables['form'] = drupal_get_form('simplenews_block_form_' . $tid);
  2203. $variables['subscription_link'] = l(t('Manage my subscriptions'), 'newsletter/subscriptions');
  2204. }
  2205. // @todo replace path
  2206. $variables['newsletter_link'] = l(t('Previous issues'), 'taxonomy/term/' . $tid);
  2207. $recent = simplenews_recent_newsletters($tid, variable_get('simplenews_block_i_' . $tid, 5));
  2208. $variables['issue_list'] = '';
  2209. if ($recent) {
  2210. $variables['issue_list'] = theme('item_list', array('items' => $recent, 'title' => t('Previous issues'), 'type' => 'ul'));
  2211. }
  2212. $variables['rssfeed'] = theme('feed_icon', array('url' => 'taxonomy/term/' . $tid . '/feed', 'title' => t('@newsletter feed', array('@newsletter' => _simplenews_newsletter_name($category)))));
  2213. // Block content control variables
  2214. $variables['use_form'] = variable_get('simplenews_block_f_' . $tid, 1);
  2215. $variables['use_issue_link'] = variable_get('simplenews_block_l_' . $tid, 1);
  2216. $variables['use_issue_list'] = variable_get('simplenews_block_i_status_' . $tid, 0);
  2217. $variables['use_rss'] = variable_get('simplenews_block_r_' . $tid, 1);
  2218. // Additional variables
  2219. $variables['subscribed'] = empty($user->uid) ? FALSE : (simplenews_user_is_subscribed($user->mail, $tid) == TRUE);
  2220. $variables['user'] = !empty($user->uid);
  2221. }
  2222. /**
  2223. * Process variables for the multi subscription block.
  2224. *
  2225. * $variables are empty:
  2226. *
  2227. * @see simplenews-multi-block.tpl.php
  2228. *
  2229. * @ingroup theming
  2230. */
  2231. function template_preprocess_simplenews_multi_block(&$variables) {
  2232. // Block content variables
  2233. $variables['message'] = check_plain(variable_get('simplenews_block_m_multiple', t('Select the newsletter(s) to which you want to subscribe or unsubscribe.')));
  2234. $variables['form'] = drupal_get_form('simplenews_subscriptions_multi_block_form');
  2235. }
  2236. /**
  2237. * @todo
  2238. *
  2239. * @see template_preprocess_field()
  2240. * @see theme_simplenews_field()
  2241. */
  2242. function template_preprocess_simplenews_field(&$variables, $hook) {
  2243. $element = $variables['element'];
  2244. $variables['label_hidden'] = ($element['#label_display'] == 'hidden');
  2245. $variables['label'] = $variables['label_hidden'] ? NULL : check_plain($element['#title']);
  2246. $variables['items'] = array();
  2247. foreach ($element['#items'] as $delta => $item) {
  2248. if (!empty($element[$delta])) {
  2249. $variables['items'][$delta] = $element[$delta];
  2250. }
  2251. }
  2252. $variables['view_mode'] = $variables['element']['#view_mode'];
  2253. // Add specific suggestions that can override the default implementation.
  2254. $variables['theme_hook_suggestions'] = array(
  2255. 'simplenews_field__' . $element['#field_name'],
  2256. 'simplenews_field__' . $element['#view_mode'],
  2257. 'simplenews_field__' . $element['#field_name'] . '__' . $element['#view_mode'],
  2258. );
  2259. }
  2260. /**
  2261. * @todo
  2262. *
  2263. * @see theme_field()
  2264. *
  2265. * @ingroup theming
  2266. */
  2267. // @todo make simplenews_field.tpl.php
  2268. function theme_simplenews_field($variables) {
  2269. $output = '';
  2270. switch ($variables['view_mode']) {
  2271. case 'email_plain':
  2272. case 'email_textalt':
  2273. // Render the label, if it's not hidden.
  2274. if (!$variables['label_hidden']) {
  2275. $output .= $variables['label'] . ":\n";
  2276. }
  2277. // Render the items.
  2278. foreach ($variables['items'] as $item) {
  2279. $output .= drupal_render($item) . "\n";
  2280. }
  2281. // Add an extra line break at the end of the field.
  2282. $output .= "\n";
  2283. break;
  2284. case 'email_html':
  2285. default:
  2286. // Render the label, if it's not hidden.
  2287. if (!$variables['label_hidden']) {
  2288. $output .= '<div class="field-label">' . $variables['label'] . ':&nbsp;</div>';
  2289. }
  2290. // Render the items.
  2291. $output .= '<div class="field-items">';
  2292. foreach ($variables['items'] as $delta => $item) {
  2293. $classes = 'field-item ' . ($delta % 2 ? 'odd' : 'even');
  2294. $output .= '<div class="' . $classes . '">' . drupal_render($item) . '</div>';
  2295. }
  2296. $output .= '</div>';
  2297. // Render the top-level DIV.
  2298. $output = '<div class="clearfix">' . $output . '</div>';
  2299. break;
  2300. }
  2301. return $output;
  2302. }
  2303. /**
  2304. * Process variables to format the simplenews newsletter body.
  2305. *
  2306. * @see simplenews-newsletter-body.tpl.php
  2307. *
  2308. * @ingroup theming
  2309. */
  2310. function template_preprocess_simplenews_newsletter_body(&$variables) {
  2311. // We don't want to include links and comments in the email.
  2312. unset($variables['build']['links']);
  2313. unset($variables['build']['comments']);
  2314. $theme = function_exists('mailsystem_get_mail_theme') ? mailsystem_get_mail_theme() : path_to_theme();
  2315. $variables['simplenews_theme'] = drupal_get_path('theme', $theme);
  2316. $variables['title'] = check_plain($variables['build']['#node']->title);
  2317. $variables['language'] = $variables['build']["#language"];
  2318. $variables['view_mode'] = $variables['build']['#view_mode'];
  2319. // Add specific suggestions that can override the default implementation.
  2320. $variables['theme_hook_suggestions'] = array(
  2321. 'simplenews_newsletter_body__' . $variables['category']->tid,
  2322. 'simplenews_newsletter_body__' . $variables['build']['#view_mode'],
  2323. 'simplenews_newsletter_body__' . $variables['category']->tid . '__' . $variables['build']['#view_mode'],
  2324. );
  2325. }
  2326. /**
  2327. * Process variables to format the simplenews newsletter footer.
  2328. *
  2329. * @see simplenews-newsletter-footer.tpl.php
  2330. *
  2331. * @ingroup theming
  2332. */
  2333. function template_preprocess_simplenews_newsletter_footer(&$variables) {
  2334. // We don't want to include links and comments in the email.
  2335. unset($variables['build']['links']);
  2336. unset($variables['build']['comments']);
  2337. $theme = function_exists('mailsystem_get_mail_theme') ? mailsystem_get_mail_theme() : path_to_theme();
  2338. $variables['simplenews_theme'] = drupal_get_path('theme', $theme);
  2339. $variables['unsubscribe_text'] = t('Unsubscribe from this newsletter', array(), array('langcode' => $variables['language']));
  2340. $variables['test_message'] = t('This is a test version of the newsletter.', array(), array('langcode' => $variables['language']));
  2341. $variables['view_mode'] = $variables['build']['#view_mode'];
  2342. // Add specific suggestions that can override the default implementation.
  2343. $variables['theme_hook_suggestions'] = array(
  2344. 'simplenews_newsletter_footer__' . $variables['category']->tid,
  2345. 'simplenews_newsletter_footer__' . $variables['build']['#view_mode'],
  2346. 'simplenews_newsletter_footer__' . $variables['category']->tid . '__' . $variables['build']['#view_mode'],
  2347. );
  2348. // Do not display the unsubscribe link by default for hidden categories.
  2349. $variables['opt_out_hidden'] = $variables['category']->opt_inout == 'hidden';
  2350. }
  2351. /**
  2352. * Access callback; Acces to Newsletter tab page.
  2353. */
  2354. function simplenews_node_tab_access($node) {
  2355. return simplenews_check_node_types($node->type) && user_access('send newsletter');
  2356. }
  2357. /**
  2358. * Update the sent status of a node.
  2359. *
  2360. * If the node part of a translation set, all corresponding translations are
  2361. * updated as well.
  2362. *
  2363. * @param $node
  2364. * The node object to be updated.
  2365. * @param $status
  2366. * The new status, defaults to SIMPLENEWS_STATUS_SEND_PENDING.
  2367. *
  2368. * @ingroup newsletter
  2369. */
  2370. function simplenews_newsletter_update_sent_status($node, $status = SIMPLENEWS_STATUS_SEND_PENDING) {
  2371. // When this node is selected for translation, all translation of this node
  2372. // will be sent too.
  2373. if (module_exists('translation') && translation_supported_type($node->type) && $node->tnid > 0) {
  2374. if ($translations = translation_node_get_translations($node->tnid)) {
  2375. foreach ($translations as $translation) {
  2376. $newsletter = simplenews_newsletter_load($translation->nid);
  2377. if (!$newsletter) {
  2378. $node = node_load($translation->nid);
  2379. $newsletter = (object) simplenews_newsletter_defaults($node);
  2380. }
  2381. $newsletter->status = SIMPLENEWS_STATUS_SEND_PENDING;
  2382. simplenews_newsletter_save($newsletter);
  2383. }
  2384. }
  2385. }
  2386. if (!empty($node->simplenews)) {
  2387. $newsletter = $node->simplenews;
  2388. }
  2389. else {
  2390. $newsletter = simplenews_newsletter_load($node->nid);
  2391. }
  2392. if (!$newsletter) {
  2393. $newsletter = (object) simplenews_newsletter_defaults($node);
  2394. }
  2395. $newsletter->status = $status;
  2396. simplenews_newsletter_save($newsletter);
  2397. }
  2398. /**
  2399. * Implements hook_field_extra_fields().
  2400. */
  2401. function simplenews_field_extra_fields() {
  2402. $return['user']['user'] = array(
  2403. 'display' => array(
  2404. 'simplenews' => array(
  2405. 'label' => 'Newsletters',
  2406. 'description' => t('Newsletter subscriptions of the user'),
  2407. 'weight' => 5,
  2408. ),
  2409. ),
  2410. );
  2411. return $return;
  2412. }
  2413. /**
  2414. * Implements hook_node_access().
  2415. *
  2416. * Don't allow deletion when a newsletter is pending
  2417. */
  2418. function simplenews_node_access($node, $op, $account) {
  2419. if ($op == 'delete') {
  2420. // Check if a newsletter is pending
  2421. $query = db_select('simplenews_newsletter', 'n');
  2422. $query->fields('n', array('status'));
  2423. $query->condition('n.nid', $node->nid);
  2424. $result = $query->execute();
  2425. if ($result->rowCount()) {
  2426. $status = $result->fetch();
  2427. if ($status->status == SIMPLENEWS_STATUS_SEND_PENDING) {
  2428. drupal_set_message(t('You can\'t delete this newsletter because it has not been sent to all its subscribers.'), 'warning');
  2429. return NODE_ACCESS_DENY;
  2430. }
  2431. }
  2432. }
  2433. return NODE_ACCESS_IGNORE;
  2434. }
  2435. /**
  2436. * Implements hook_cronapi().
  2437. *
  2438. * As initiated by elysia_cron.
  2439. * http://drupal.org/project/elysia_cron
  2440. * */
  2441. function simplenews_cronapi($op) {
  2442. switch ($op) {
  2443. case 'list':
  2444. return array(
  2445. 'simplenews_cron' => t('Process simplenews mail spool'),
  2446. );
  2447. case 'rule':
  2448. return '5 * * * *';
  2449. case 'execute':
  2450. break;
  2451. }
  2452. }
  2453. /**
  2454. * Returns TRUE if the newsletter category requires double opt in.
  2455. *
  2456. * @ingroup newsletter
  2457. */
  2458. function simplenews_require_double_opt_in($tid, $account) {
  2459. global $user;
  2460. // If email belongs to the current registered user, don't send confirmation.
  2461. // Other addresses receive a confirmation if double opt-in is selected.
  2462. if (!empty($account->uid) && !empty($user->uid) && $account->uid == $user->uid) {
  2463. return FALSE;
  2464. }
  2465. else {
  2466. $category = simplenews_category_load($tid);
  2467. return $category->opt_inout == 'double';
  2468. }
  2469. }
  2470. /**
  2471. * Returns the available simplenews sources.
  2472. *
  2473. * @ingroup source
  2474. */
  2475. function simplenews_get_source_caches() {
  2476. $sources = module_invoke_all('simplenews_source_cache_info');
  2477. drupal_alter('simplenews_source_cache_info', $sources);
  2478. return $sources;
  2479. }
  2480. /**
  2481. * Implements hook_simplenews_source_cache_info().
  2482. *
  2483. * @ingroup source
  2484. */
  2485. function simplenews_simplenews_source_cache_info() {
  2486. return array(
  2487. 'SimplenewsSourceCacheNone' => array(
  2488. 'label' => t('No caching'),
  2489. 'description' => t('This allows to theme each newsletter separately.'),
  2490. ),
  2491. 'SimplenewsSourceCacheBuild' => array(
  2492. 'label' => t('Cached content source'),
  2493. 'description' => t('This caches the rendered content to be sent for multiple recipients. It is not possible to use subscriber specific theming but tokens can be used for personalization.'),
  2494. ),
  2495. );
  2496. }
  2497. /**
  2498. * Impersonates another user.
  2499. *
  2500. * Each time this function is called, the active user is saved and $new_user
  2501. * becomes the active user. Multiple calls to this function can be nested,
  2502. * and session saving will be disabled until all impersonation attempts have
  2503. * been reverted using user_revert_user().
  2504. *
  2505. * @todo: This function is a backport of http://drupal.org/node/287292. Switch
  2506. * to that once available.
  2507. *
  2508. * @param $new_user
  2509. * User to impersonate, either a UID or a user object.
  2510. *
  2511. * @return
  2512. * Current user object.
  2513. *
  2514. * @see simplenews_revert_user()
  2515. */
  2516. function simplenews_impersonate_user($new_user = NULL) {
  2517. global $user;
  2518. $user_original = &drupal_static(__FUNCTION__);
  2519. if (!isset($new_user)) {
  2520. if (isset($user_original) && !empty($user_original)) {
  2521. // Restore the previous user from the stack.
  2522. $user = array_pop($user_original);
  2523. // Re-enable session saving if we are no longer impersonating a user.
  2524. if (empty($user_original)) {
  2525. drupal_save_session(TRUE);
  2526. }
  2527. }
  2528. }
  2529. else {
  2530. // Push the original user onto the stack and prevent session saving.
  2531. $user_original[] = $user;
  2532. drupal_save_session(FALSE);
  2533. if (is_numeric($new_user)) {
  2534. $user = user_load($new_user);
  2535. }
  2536. else {
  2537. $user = is_object($new_user) ? $new_user : (object) $new_user;
  2538. }
  2539. }
  2540. return $user;
  2541. }
  2542. /**
  2543. * Reverts to the previous user after impersonating.
  2544. *
  2545. * @return
  2546. * Current user.
  2547. *
  2548. * @see simplenews_impersonate_user()
  2549. */
  2550. function simplenews_revert_user() {
  2551. return simplenews_impersonate_user();
  2552. }
  2553. /**
  2554. * Starts combining confirmation mails.
  2555. *
  2556. * If combining mails is enabled, it is mandatory to call
  2557. * simplenews_send_combined_confirmation() after all subscription changes have
  2558. * been applied or no mails will be sent.
  2559. *
  2560. * @param $collect
  2561. * TRUE to start combining mail conformations, FALSE to disable it.
  2562. *
  2563. * @return
  2564. * TRUE if combining has been started, FALSE otherwise.
  2565. */
  2566. function simplenews_confirmation_combine($collect = NULL) {
  2567. $static_collect = &drupal_static(__FUNCTION__, FALSE);
  2568. if (isset($collect)) {
  2569. $static_collect = $collect;
  2570. }
  2571. return $static_collect;
  2572. }
  2573. /**
  2574. * Add a mail confirmation or fetch them.
  2575. *
  2576. * @param $action
  2577. * (Optional) The confirmation type, either subscribe or unsubscribe.
  2578. * @param $subscriber
  2579. * (Optional) The subscriber object.
  2580. * @param $category
  2581. * (Optional) The newsletter category object.
  2582. *
  2583. * @return
  2584. * If no arguments are passed in, an array of current confirmations, keyed by
  2585. * subscriber id, the value is an array with the keys action and tid.
  2586. */
  2587. function simplenews_confirmation_add_combined($action = NULL, $subscriber = NULL, $category = NULL) {
  2588. $group = &drupal_static(__FUNCTION__, array());
  2589. if (!empty($action)) {
  2590. $group[$subscriber->mail][$category->tid] = $action;
  2591. }
  2592. else {
  2593. return $group;
  2594. }
  2595. }
  2596. /**
  2597. * Send collected confirmations.
  2598. *
  2599. * Depending on the settings, always sends a combined confirmation,
  2600. * only when there are multiple changes for a subscriber or never.
  2601. *
  2602. * Calling this functions also resets the combine flag so that later
  2603. * confirmations are sent separately. simplenews_combine_confirmations() needs
  2604. * to be called again to re-enable combining.
  2605. *
  2606. * @return
  2607. * TRUE if any confirmation mails have been sent.
  2608. *
  2609. * @todo This function currently does not return information about which
  2610. * subscriber received a confirmation.
  2611. */
  2612. function simplenews_confirmation_send_combined() {
  2613. global $language;
  2614. // Disable combining for further confirmations.
  2615. simplenews_confirmation_combine(FALSE);
  2616. $group = simplenews_confirmation_add_combined();
  2617. foreach ($group as $mail => $changes) {
  2618. module_load_include('inc', 'simplenews', 'includes/simplenews.mail');
  2619. $params['from'] = _simplenews_set_from();
  2620. $subscriber = simplenews_subscriber_load_by_mail($mail);
  2621. if (!$subscriber) {
  2622. $subscriber = new stdClass();
  2623. $subscriber->mail = $mail;
  2624. $subscriber->language = $language->language;
  2625. $subscriber->tids = array();
  2626. }
  2627. $params['context']['simplenews_subscriber'] = $subscriber;
  2628. // Send multiple if there is more than one change for this subscriber
  2629. // single otherwise.
  2630. $use_combined = variable_get('simplenews_use_combined', 'multiple');
  2631. if ((count($changes) > 1 && $use_combined != 'never') || $use_combined == 'always') {
  2632. $params['context']['changes'] = $changes;
  2633. $key = 'subscribe_combined';
  2634. drupal_mail('simplenews', $key, $subscriber->mail, $subscriber->language, $params, $params['from']['address']);
  2635. }
  2636. else {
  2637. foreach ($changes as $tid => $key) {
  2638. $params['context']['category'] = simplenews_category_load($tid);
  2639. drupal_mail('simplenews', $key, $subscriber->mail, $subscriber->language, $params, $params['from']['address']);
  2640. }
  2641. }
  2642. // Save the changes in the subscriber if there is a real subscriber object.
  2643. if (!empty($subscriber->snid)) {
  2644. $subscriber->changes = $changes;
  2645. simplenews_subscriber_save($subscriber);
  2646. }
  2647. }
  2648. return !empty($group);
  2649. }
  2650. /**
  2651. * Send a confirmation mail.
  2652. *
  2653. * Either sends a mail immediatly or collects them for a combined mail.
  2654. *
  2655. * @param $action
  2656. * The confirmation type, either subscribe or unsubscribe.
  2657. * @param $subscriber
  2658. * The subscriber object.
  2659. * @param $category
  2660. * The newsletter category object.
  2661. */
  2662. function simplenews_confirmation_send($action, $subscriber, $category) {
  2663. // Check if confirmation should be sent immediatly or grouped.
  2664. if (simplenews_confirmation_combine()) {
  2665. simplenews_confirmation_add_combined($action, $subscriber, $category);
  2666. }
  2667. else {
  2668. // Send confirmation email to user to complete (un)subscription.
  2669. // Confirmation mail is in the user preferred language which is by default the
  2670. // language_default().
  2671. module_load_include('inc', 'simplenews', 'includes/simplenews.mail');
  2672. $params['from'] = _simplenews_set_from();
  2673. $params['context']['category'] = $category;
  2674. $params['context']['simplenews_subscriber'] = $subscriber;
  2675. drupal_mail('simplenews', $action, $subscriber->mail, $subscriber->language, $params, $params['from']['address']);
  2676. }
  2677. }
  2678. /**
  2679. * Converts an array of subscription changes into descriptions.
  2680. *
  2681. * @param $subscriber
  2682. * Simplenews subscriber object.
  2683. * @param $changes
  2684. * (Optional) Array of changes, each is an array with the keys action and tid.
  2685. * Defaults to $subscriber->changes, which contains the currently saved
  2686. * changes for the subscriber. No-op changes are removed from this array.
  2687. * @param $langcode
  2688. * (Optional) Specify the language of the description strings, defaults to the
  2689. * current language.
  2690. *
  2691. * @return
  2692. * Array of description strings describing the changes.
  2693. */
  2694. function simplenews_confirmation_get_changes_list($subscriber, &$changes = NULL, $langcode = NULL) {
  2695. if (empty($langcode)) {
  2696. global $language;
  2697. $langcode = $language->language;
  2698. }
  2699. if (empty($changes)) {
  2700. $changes = $subscriber->changes;
  2701. }
  2702. $changes_list = array();
  2703. foreach ($changes as $tid => $action) {
  2704. $subscribed = simplenews_user_is_subscribed($subscriber->mail, $tid);
  2705. // Get text for each possible combination.
  2706. if ($action == 'subscribe' && !$subscribed) {
  2707. $line = simplenews_subscription_confirmation_text('combined_line_subscribe_unsubscribed', $langcode);
  2708. }
  2709. elseif ($action == 'subscribe' && $subscribed) {
  2710. $line = simplenews_subscription_confirmation_text('combined_line_subscribe_subscribed', $langcode);
  2711. }
  2712. elseif ($action == 'unsubscribe' && !$subscribed) {
  2713. $line = simplenews_subscription_confirmation_text('combined_line_unsubscribe_unsubscribed', $langcode);
  2714. }
  2715. elseif ($action == 'unsubscribe' && $subscribed) {
  2716. $line = simplenews_subscription_confirmation_text('combined_line_unsubscribe_subscribed', $langcode);
  2717. }
  2718. $category_context = array(
  2719. 'simplenews_subscriber' => $subscriber,
  2720. 'category' => simplenews_category_load($tid),
  2721. );
  2722. $changes_list[$tid] = token_replace($line, $category_context, array('sanitize' => FALSE));
  2723. }
  2724. return $changes_list;
  2725. }