simplenews.module 103 KB

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