mollom.api.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. <?php
  2. /**
  3. * @file
  4. * API documentation for Mollom module.
  5. */
  6. /**
  7. * @defgroup mollom_api Mollom API
  8. * @{
  9. * Functions to integrate with Mollom form protection.
  10. *
  11. * In general, there are two different kinds of form submissions:
  12. * - Entities created out of form submissions, which can be edited or deleted
  13. * afterwards; whereas "entity" just refers to a uniquely identifiable data
  14. * record.
  15. * - Form submissions that do not store any data, such as contact form mail
  16. * messages and similar forms. While there may be an entity type (e.g.,
  17. * "contact_mail"), there is no unique id for the post, which could be
  18. * referred to later on.
  19. *
  20. * The Mollom API therefore supports two types of integration:
  21. * - Entity form integration: Mollom integrates with the add/edit form for an
  22. * entity, and additionally with the delete confirmation form of the entity
  23. * to send feedback to Mollom. Almost everything happens in an automated way,
  24. * solely based on the information provided via Mollom's info hooks, as
  25. * explained below.
  26. * - Free integration: Mollom integrates with a given form_id without 'entity'.
  27. * Allowing users to send feedback requires to manually add "report to Mollom"
  28. * links. Additionally requires to specify a 'report access [callback]' and
  29. * 'report delete callback' to correctly handle access to report and delete
  30. * a posted piece of content. An example for this kind of integration can be
  31. * found in contact_mollom_form_list(), mollom_mail_alter(), and related
  32. * functions. This kind of integration is discouraged; it is recommended to
  33. * implement and integrate with entity forms.
  34. *
  35. * Considering a very simple Instant Messaging module ("IM") that implements a
  36. * "im_message_form" allowing to send or edit an instant message, which should
  37. * be possible to be protected by Mollom:
  38. * @code
  39. * function im_message_form(&$form_state, $im) {
  40. * // To allow other modules to extend this form more easily and simplify our
  41. * // own form submission handling, we use the dedicated parent key "im" for
  42. * // all message properties (allows for easy casting from array to object).
  43. * // Also helps us to explain handling of hierarchical sub-keys. :)
  44. * $form['#tree'] = TRUE;
  45. *
  46. * // This is the stored message id (or 'post_id'), if any:
  47. * // @see im_message_form_submit()
  48. * $form['im']['id'] = array(
  49. * '#type' => 'value',
  50. * '#value' => isset($im->id) ? $im->id : NULL,
  51. * );
  52. * $form['im']['subject'] = array(
  53. * '#type' => 'textfield',
  54. * '#title' => t('Subject'),
  55. * '#default_value' => isset($im->subject) ? $im->subject : '',
  56. * );
  57. * $form['im']['body'] = array(
  58. * '#type' => 'textfield',
  59. * '#title' => t('Message'),
  60. * '#default_value' => isset($im->body) ? $im->body : '',
  61. * );
  62. * $form['actions']['submit'] = array(
  63. * '#type' => 'submit',
  64. * '#value' => t('Send'),
  65. * );
  66. * return $form;
  67. * }
  68. * @endcode
  69. *
  70. * "entity" refers to an entity type. For example, "node", "user", "comment",
  71. * but also "webform_submission". It is not necessarily the name of a database
  72. * table, but most often it actually is. The string is only used internally to
  73. * identify to which module a form submission belongs. Once in use, it should
  74. * not be changed.
  75. *
  76. * Our form accepts an argument $im, which we assume is the entity being created
  77. * or edited, so we can also assume the following submit handler:
  78. * @code
  79. * function im_message_form_submit($form, &$form_state) {
  80. * // Do whatever we need to do to insert or update the message.
  81. * $im = (object) $form_state['values']['im'];
  82. * im_save($im);
  83. * // Ensure subsequent submit handlers have an entity id to work with, as
  84. * // newly created messages will not have an id in the form values.
  85. * $form_state['values']['im']['id'] = $im->id;
  86. * }
  87. * @endcode
  88. *
  89. * The form values will not contain an entity id for a newly created message,
  90. * which is usually an auto_increment column value returned from the database.
  91. * Whenever a form submission is related to the entity (e.g., leads to a stored
  92. * entity being created, updated, or deleted) the form should *always* contain
  93. * the entity id in the same location of the submitted form values.
  94. * Above example therefore purposively assigns the new id after inserting it.
  95. *
  96. * @code
  97. * function im_message_delete_confirm_form(&$form_state, $im) {
  98. * $form['#im'] = $im;
  99. *
  100. * // Always provide entity id in the same form key as in the entity edit form.
  101. * $form['im']['id'] = array('#type' => 'value', '#value' => $im->id);
  102. *
  103. * // In our case, we also need to enable #tree, so that above value ends up
  104. * // in 'im][id' where we expect it.
  105. * $form['#tree'] = TRUE;
  106. *
  107. * return confirm_form($form,
  108. * t('Are you sure you want to delete %title?', array('%title' => $im->subject)),
  109. * 'im/' . $im->id,
  110. * NULL,
  111. * t('Delete')
  112. * );
  113. * }
  114. * @endcode
  115. *
  116. * The same applies to the delete confirmation form for the entity: it also
  117. * provides the entity id for form submit handlers.
  118. *
  119. * After ensuring these basics, the first step is to register the basic form_id
  120. * along with its title, entity type, as well as the form_id of the
  121. * corresponding delete confirmation form via hook_mollom_form_list():
  122. *
  123. * @code
  124. * function im_mollom_form_list() {
  125. * $forms['im_message_form'] = array(
  126. * 'title' => t('Instant messaging form'),
  127. * 'entity' => 'im',
  128. * // Specify the $form_id of the delete confirmation form that allows
  129. * // privileged users to delete a stored message. Mollom will automatically
  130. * // add form elements to send feedback to Mollom to this form.
  131. * 'delete form' => 'im_message_delete_confirm_form',
  132. * );
  133. * return $forms;
  134. * }
  135. * @endcode
  136. *
  137. * Since modules can provide many forms, only minimal information is returned
  138. * via hook_mollom_form_list(). All details about the form are only required and
  139. * asked for, if the site administrator actually enables Mollom's protection for
  140. * the form. Therefore, everything else is registered via
  141. * hook_mollom_form_info():
  142. *
  143. * @code
  144. * function im_mollom_form_info($form_id) {
  145. * switch ($form_id) {
  146. * case 'im_message_form':
  147. * $form_info = array(
  148. * // Optional: User permission list to skip Mollom's protection for.
  149. * 'bypass access' => array('administer instant messages'),
  150. * // Optional: Function to invoke to put a bad form submission into a
  151. * // moderation queue instead of discarding it.
  152. * 'moderation callback' => 'im_mollom_form_moderation',
  153. * // Optional: To allow textual analysis of the form values, the form
  154. * // elements needs to be registered individually. The keys are the
  155. * // field keys in $form_state['values']. Sub-keys are noted using "]["
  156. * // as delimiter.
  157. * 'elements' => array(
  158. * 'im][subject' => t('Subject'),
  159. * 'im][body' => t('Message body'),
  160. * ),
  161. * // Required when either specifying 'entity' or 'elements': the keys
  162. * // are predefined data properties sent to Mollom (see full list in
  163. * // hook_mollom_form_info()), the values refer to field keys in
  164. * // $form_state['values']. Sub-keys are noted using "][" as delimiter.
  165. * 'mapping' => array(
  166. * // Required when specifying 'entity' above: Where to find the id of
  167. * // the entity being posted, edited, or deleted.
  168. * // Important: The following assignment means that Mollom is able to
  169. * // find the message id of the created, edited, or deleted message
  170. * // in $form_state['values']['im']['id'].
  171. * 'post_id' => 'im][id',
  172. * // Required if the form or entity contains a title-alike field:
  173. * 'post_title' => 'im][subject',
  174. * // Optional: If our instant message form was accessible for
  175. * // anonymous users and would contain form elements to enter the
  176. * // sender's name, e-mail address, and web site, then those fields
  177. * // should be additionally specified. Otherwise, information from
  178. * // the global user session would be automatically taken over.
  179. * 'author_name' => 'im][sender][name',
  180. * 'author_mail' => 'im][sender][mail',
  181. * 'author_url' => 'im][sender][homepage',
  182. * ),
  183. * );
  184. * break;
  185. * }
  186. * return $form_info;
  187. * }
  188. * @endcode
  189. *
  190. * "elements" is a list of form elements, in which users can freely type text.
  191. * The elements should not contain numeric or otherwise predefined option
  192. * values, only text actually coming from user input. Only by registering
  193. * "elements", Mollom is able to perform textual analysis. Without registered
  194. * form elements, Mollom can only provide a CAPTCHA.
  195. *
  196. * "mapping" is a mapping of form elements to predefined XML-RPC data properties
  197. * of the Mollom web service. For example, "post_title", "author_name",
  198. * "author_id", "author_mail", etc. Normally, all form elements specified in
  199. * "elements" would be merged into the "post_body" data property. By specifying
  200. * a "mapping", certain form element values are sent for the specified data
  201. * property instead. In our case, the form submission contains something along
  202. * the lines of a title in the "subject" field, so we map the "post_title" data
  203. * property to the "subject" field.
  204. *
  205. * Additionally, the "post_id" data property always needs to be mapped to a form
  206. * element that holds the entity id.
  207. *
  208. * When registering a 'moderation callback', then the registered function needs
  209. * to be available when the form is validated, and it is responsible for
  210. * changing the submitted form values in a way that results in an unpublished
  211. * post ending up in a moderation queue:
  212. * @code
  213. * function im_mollom_form_moderation(&$form, &$form_state) {
  214. * $form_state['values']['status'] = 0;
  215. * }
  216. * @endcode
  217. *
  218. * @see mollom_node
  219. * @see mollom_comment
  220. * @see mollom_user
  221. * @see mollom_contact
  222. */
  223. /**
  224. * Return information about forms that can be protected by Mollom.
  225. *
  226. * Mollom invokes this hook for all modules to gather information about forms
  227. * that can be protected. Only forms that have been registered via this hook are
  228. * configurable in Mollom's administration interface.
  229. *
  230. * @return
  231. * An associative array containing information about the forms that can be
  232. * protected, keyed by $form_id:
  233. * - title: The human-readable name of the form.
  234. * - entity: (optional) The internal name of the entity type the form is for,
  235. * e.g. 'node' or 'comment'. This is required for all forms that will store
  236. * the submitted content persistently. It is only optional for forms that do
  237. * not permanently store the submitted form values, such as contact forms
  238. * that only send an e-mail, but do not store it in the database.
  239. * Note that forms that specify 'entity' also need to specify 'post_id' in
  240. * the 'mapping' (see below).
  241. * - delete form: (optional) The $form_id of a delete confirmation form
  242. * constructor function for 'entity'. Mollom automatically adds the
  243. * "Report as inappropriate" options to this confirmation form. Requires a
  244. * 'post_id' mapping via hook_mollom_form_info(). Requires the delete
  245. * confirmation form constructor to assign the mapped post_id key in $form
  246. * as a #value. See http://drupal.org/node/645374 for examples. Optionally
  247. * limit access to report options by defining 'report access' permissions.
  248. * - report path: (optional) A Drupal system path pattern to be used for
  249. * reporting an entity to Mollom via a "Report to Mollom" link in e-mail
  250. * notifications. This typically points to the menu router path that allows
  251. * to delete an entity. The placeholder '%id' is dynamically replaced with
  252. * the entity ID. For example, user_mollom_form_list() specifies
  253. * 'user/%id/cancel'.
  254. * - report access: (optional) A list containing user permission strings, from
  255. * which the current user needs to have at least one. Should only be used if
  256. * no "report access callback" was defined.
  257. * - report access callback: (optional) A function name to invoke to check
  258. * access to Mollom's dedicated "report to Mollom" form, which should return
  259. * either TRUE or FALSE (similar to menu access callbacks).
  260. * - report delete callback: (optional) A function name to invoke to delete an
  261. * entity after reporting it to Mollom. The callback will receive the
  262. * entity id as an argument.
  263. * - entity report access callback: (optional) A function name to invoke to
  264. * determine if a user has access to report the entity that the form is for.
  265. * In order for a user to have the option to flag content as inappropriate,
  266. * the user must have the "report to mollom" permission as well as access to
  267. * report the specific entity.
  268. * Note: This function is required if the flag is inappropriate feature is
  269. * desired on the form.
  270. *
  271. * @see hook_mollom_form_info()
  272. */
  273. function hook_mollom_form_list() {
  274. // Mymodule's comment form.
  275. $forms['mymodule_comment_form'] = array(
  276. 'title' => t('Comment form'),
  277. 'entity' => 'mymodule_comment',
  278. // Mollom does not know how to determine access and the callback to invoke
  279. // for reporting and deleting the entity, so your module needs to manually
  280. // output links to Mollom's generic "Report to Mollom" form on
  281. // 'mollom/report/[entity]/[id]' and supply the following two callbacks.
  282. // This kind of integration is deprecated. Use the delete confirmation form
  283. // integration below instead.
  284. 'report access callback' => 'mymodule_comment_report_access',
  285. 'report delete callback' => 'mymodule_comment_report_delete',
  286. 'entity report access callback' => 'mymodule_comment_report_access',
  287. );
  288. // Mymodule's user registration form.
  289. $forms['mymodule_user_register'] = array(
  290. 'title' => t('User registration form'),
  291. 'entity' => 'mymodule_user',
  292. // Mollom will automatically integrate with the delete confirmation form and
  293. // send feedback for the 'entity' specified above and the 'post_id'
  294. // specified via hook_mollom_form_info(). The delete confirmation form has
  295. // to provide the ID of the entity in the mapped post_id key
  296. // (here: $form_state['values']['uid']).
  297. // @see http://drupal.org/node/645374
  298. 'delete form' => 'mymodule_user_delete_confirm_form',
  299. // Optionally specify an include file that contains the delete confirmation
  300. // form constructor to be loaded. The array keys map to function arguments
  301. // of module_load_include().
  302. 'delete form file' => array(
  303. 'name' => 'mymodule.pages',
  304. ),
  305. // Specify where to find the delete confirmation form for e-mails.
  306. 'report path' => 'user/%id/cancel',
  307. // Optionally limit access to report options on the delete confirmation form.
  308. 'report access' => array('administer users', 'bypass node access'),
  309. );
  310. return $forms;
  311. }
  312. /**
  313. * Alter the list of forms that can be protected by Mollom.
  314. *
  315. * @param &$form_list
  316. * An associative array containing information about the forms that can be
  317. * protected, keyed by $form_id. See hook_mollom_form_list() for details.
  318. */
  319. function hook_mollom_form_list_alter(&$form_list) {
  320. if (isset($form_list['mymodule_user_register'])) {
  321. $form_list['mymodule_user_register']['report delete callback'] = '_mymodule_user_register_delete';
  322. }
  323. }
  324. /**
  325. * Return information about a form that can be protected by Mollom.
  326. *
  327. * @param $form_id
  328. * The form id to return information for.
  329. *
  330. * @return
  331. * An associative array describing the form identified by $form_id:
  332. * - mode: (optional) The default protection mode for the form, which can be
  333. * one of:
  334. * - MOLLOM_MODE_ANALYSIS: Text analysis of submitted form values with
  335. * fallback to CAPTCHA.
  336. * - MOLLOM_MODE_CAPTCHA: CAPTCHA-only protection.
  337. * - type: Internal use only.
  338. * - bypass access: (optional) A list of user permissions to check for the
  339. * current user to determine whether to protect the form with Mollom or do
  340. * not validate submitted form values. If the current user has at least one
  341. * of the listed permissions, the form will not be protected.
  342. * - moderation callback: (optional) A function name to invoke when a form
  343. * submission would normally be discarded. This allows modules to put such
  344. * posts into a moderation queue (i.e., to accept but not publish them) by
  345. * altering the $form or $form_state that are passed by reference.
  346. * - context created callback: (optional) A function to invoke to determine
  347. * the creation of the context for this form for textual analysis. The
  348. * function receives the id of the entity being processed and should
  349. * return the UNIX timestamp for the creation date or FALSE if unavailable.
  350. * - mail ids: (optional) An array of mail IDs that will be sent as a result
  351. * of this form being submitted. When these mails are sent, a 'report to
  352. * Mollom' link will be included at the bottom of the mail body. Be sure to
  353. * include only user-submitted mails and not any mails sent by Drupal since
  354. * they should never be reported as spam.
  355. * - elements: (optional) An associative array of elements in the form that
  356. * can be configured for Mollom's text analysis. The site administrator can
  357. * only select the form elements to process (and exclude certain elements)
  358. * when a form registers elements. Each key is a form API element #parents
  359. * string representation of the location of an element in the form. For
  360. * example, a key of "myelement" denotes a form element value on the
  361. * top-level of submitted form values. For nested elements, a key of
  362. * "parent][child" denotes that the value of 'child' is found below 'parent'
  363. * in the submitted form values. Each value contains the form element label.
  364. * If omitted, Mollom can only provide a CAPTCHA protection for the form.
  365. * - mapping: (optional) An associative array to explicitly map form elements
  366. * (that have been specified in 'elements') to the data structure that is
  367. * sent to Mollom for validation. The submitted form values of all mapped
  368. * elements are not used for the post's body, so Mollom can validate certain
  369. * values individually (such as the author's e-mail address). None of the
  370. * mappings are required, but most implementations most likely want to at
  371. * least denote the form element that contains the title of a post.
  372. * The following mappings are possible:
  373. * - post_id: The form element value that denotes the ID of the content
  374. * stored in the database.
  375. * - post_title: The form element value that should be used as title.
  376. * - post_body: Mollom automatically assigns this property based on all
  377. * elements that have been selected for textual analysis in Mollom's
  378. * administrative form configuration.
  379. * - author_name: The form element value that should be used as author name.
  380. * - author_mail: The form element value that should be used as the author's
  381. * e-mail address.
  382. * - author_url: The form element value that should be used as the author's
  383. * homepage.
  384. * - author_id: The form element value that should be used as the author's
  385. * user uid.
  386. * - author_openid: Mollom automatically assigns this property based on
  387. * 'author_id', if no explicit form element value mapping was specified.
  388. * - author_ip: Mollom automatically assigns the user's IP address if no
  389. * explicit form element value mapping was specified.
  390. * - context_id: The form element value that should be used to determine the
  391. * post's parent context. In the case of a comment, this would be the
  392. * node where the comment was posted. This is passed to the 'context
  393. * created callback' to determine the context creation date and both
  394. * must be set in order to take advantage of creation date checking.
  395. */
  396. function hook_mollom_form_info($form_id) {
  397. switch ($form_id) {
  398. // Mymodule's comment form.
  399. case 'mymodule_comment_form':
  400. $form_info = array(
  401. 'mode' => MOLLOM_MODE_ANALYSIS,
  402. 'bypass access' => array('administer comments'),
  403. 'mail ids' => array('mymodule_comment_mail'),
  404. 'elements' => array(
  405. 'subject' => t('Subject'),
  406. 'body' => t('Body'),
  407. ),
  408. 'context created callback' => 'mollom_node_created',
  409. 'mapping' => array(
  410. 'post_id' => 'cid',
  411. 'post_title' => 'subject',
  412. 'author_name' => 'name',
  413. 'author_mail' => 'mail',
  414. 'author_url' => 'homepage',
  415. 'context_id' => 'nid',
  416. ),
  417. );
  418. return $form_info;
  419. // Mymodule's user registration form.
  420. case 'mymodule_user_register':
  421. $form_info = array(
  422. 'mode' => MOLLOM_MODE_CAPTCHA,
  423. 'mapping' => array(
  424. 'post_id' => 'uid',
  425. 'author_name' => 'name',
  426. 'author_mail' => 'mail',
  427. ),
  428. );
  429. return $form_info;
  430. }
  431. }
  432. /**
  433. * Alter registered information about a form that can be protected by Mollom.
  434. *
  435. * @param &$form_info
  436. * An associative array describing the protectable form. See
  437. * hook_mollom_form_info() for details.
  438. * @param $form_id
  439. * The $form_id of the form.
  440. */
  441. function hook_mollom_form_info_alter(&$form_info, $form_id) {
  442. if ($form_id == 'comment_form') {
  443. $form_info['elements']['mymodule_field'] = t('My additional field');
  444. }
  445. }
  446. /**
  447. * Alter textual analysis submission before it is sent to Mollom's checkContent.
  448. *
  449. * @param &$content
  450. * An associative array of data being prepared for Mollom. The parameters at
  451. * https://docs.acquia.com/mollom/api/rest/list#content-create are the array
  452. * keys, like $data['postBody'].
  453. */
  454. function hook_mollom_content_alter(&$content) {
  455. // https://docs.acquia.com/ does not have spam, do not examine its URLs.
  456. if (isset($content['postBody'])) {
  457. $data['postBody'] = preg_replace('#https?://docs\.acquia\.com#', '', $content['postBody']);
  458. }
  459. }
  460. /**
  461. * @} End of "defgroup module_group".
  462. */