Commit 6f46c3f5 authored by theo lem's avatar theo lem
Browse files

Merge branch 'forbid-empty-forms' into 'master'

Anti spam measures

See merge request framasoft/framaforms!60
parents e1988345 648c1b4b
......@@ -7,9 +7,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
### Added
### Added
* BOTCHA module to fix the possible bypass of CAPTCHA Riddler.
* IP module to log user IPs.
* Anti-spam measures that unpublish forms containing suspicious words in title, or don't have any webform component.
* Automatic deletion of forms after a period of time.
* Views : /possible-spam, displays unpublished forms from above.
## [1.0.1] 2020/07/15
......
......@@ -56,6 +56,8 @@ function framaforms_install () {
variable_set('framaforms_expiration_period_default_value', 26); // default creation-expiration time interval
variable_set('framaforms_deletion_period_value', 9); // default expiration-deletion time interval
variable_set('framaforms_notification_period_value', 2); // default notification-expiration time interval
variable_set('framaforms_suspicious_form_deletion', '1 week'); // default deletion period for possible spam
variable_set('site_contact_url', 'https://contact.framasoft.org/#framaforms');
/* various other custom manipulation */
......
......@@ -565,6 +565,30 @@ function framaforms_cron(){
make_forms_expire(); // Look in node table for nodes about to expire, and notify the users.
update_modified_nodes(); // Look in the node table for nodes whose expiration date got changed by the user
delete_expired_nodes(); // Delete expired nodes after a period of inactivity
framaforms_delete_possible_spam();
}
/**
* Deletes from database forms that were considered as potential spam (unpublished)
* and that were not changed for a period of time
*
* @return void
*/
function framaforms_delete_possible_spam () {
$max_possible_spam_lifetime = variable_get('framaforms_suspicious_form_deletion');
$query = "SELECT nid
FROM node
WHERE status = 0
AND type = 'form1'
AND to_timestamp(changed) < NOW() - interval'{$max_possible_spam_lifetime}'";
try{
$results = db_query($query)->fetchCol();
node_delete_multiple($results);
}
catch(PDOException $e){
watchdog("framaforms", "Error deletion potential spam : ".$e);
}
}
/**
......@@ -577,4 +601,63 @@ function framaforms_node_delete ($node) {
->condition('source', 'node/'.$node->nid)
->execute();
}
}
/**
* Implements hook_node_insert.
*
* @param $node : the node that was published
* @return void
*/
function framaforms_node_presave($node) {
// Check newly created form nodes for webform components. If no webfom components were inserted, unpublish the form.
// Only checking nodes where nid != NULL because we only want to consider fully created nodes.
if($node->type == 'form1' && $node->nid != NULL && $node->webform['components'] == array()) {
$node->status = 0; // Unpublish the form node - it won't be accessibly to the public.
drupal_set_message(t("Your form was created empty. It is considered as spam, and will soon be deleted automatically. Please insert at least one form field to prove this content is legitimate."), 'warning');
}
// Republish nodes that were previously considered as spam where components were added
if($node->status == 0 && $node->webform['components'] != array()){
$node->status = 1; // Publish the content
drupal_set_message(t("Your form is now considered as legitimate, and is accessible publicly."), 'status');
}
}
/**
* Implements hook_node_insert.
* Check title for suspicious words in new form titles.
* @param [type] $node
* @return void
*/
function framaforms_node_insert ($node) {
// Regex containing suspicious words
$suspicious_words = "%(free|watch|online|movie|pelicula|completa|streaming|1080|fortnite|tiktok|mulan|poker)%";
if($node->type == 'form1' && $node->nid != NULL && preg_match($suspicious_words, strtolower($node->title)) != 0){
$node->status = 0; // Unpublish the form node - it won't be accessibly to the public.
drupal_set_message(t("Your form contains suspicious words and is therefore considered as spam. The site's administrators will review it shortly."), 'warning');
}
}
/**
* Implements hook_views_api
*
* @return void
*/
function framaforms_views_api () {
return array(
'api' => 3,
);
}
/**
* Implements hook_views_default_views
*
* @return void
*/
function framaforms_views_default_views () {
module_load_include('inc', 'framaforms', 'includes/framaforms.views');
$views = array();
$views['possible-spam'] = return_possible_spam_view_definition();
return $views;
}
\ No newline at end of file
<?php
/**
* Returns the view definition for "Possible spam" (accessible at /possible-spam) :
* lists forms that were unpublished because considered as spam.
*
* @return void
*/
function return_possible_spam_view_definition () {
$view = new view();
$view->name = 'possible_spam';
$view->description = '';
$view->tag = 'default';
$view->base_table = 'node';
$view->human_name = 'Possible spam';
$view->core = 7;
$view->api_version = '3.0';
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
/* Display: Master */
$handler = $view->new_display('default', 'Master', 'default');
$handler->display->display_options['title'] = 'Possible spam';
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['use_more_text'] = 'plus';
$handler->display->display_options['access']['type'] = 'role';
$handler->display->display_options['access']['role'] = array(
3 => '3',
);
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['exposed_form']['options']['submit_button'] = 'Appliquer';
$handler->display->display_options['exposed_form']['options']['reset_button_label'] = 'Réinitialiser';
$handler->display->display_options['exposed_form']['options']['exposed_sorts_label'] = 'Trier par';
$handler->display->display_options['pager']['type'] = 'full';
$handler->display->display_options['pager']['options']['items_per_page'] = '50';
$handler->display->display_options['pager']['options']['expose']['items_per_page_label'] = 'Éléments par page';
$handler->display->display_options['pager']['options']['expose']['items_per_page_options_all_label'] = '- Tout -';
$handler->display->display_options['pager']['options']['expose']['offset_label'] = 'Décalage';
$handler->display->display_options['pager']['options']['tags']['first'] = '« premier';
$handler->display->display_options['pager']['options']['tags']['previous'] = '‹ précédent';
$handler->display->display_options['pager']['options']['tags']['next'] = 'suivant ›';
$handler->display->display_options['pager']['options']['tags']['last'] = 'dernier »';
$handler->display->display_options['style_plugin'] = 'table';
/* Champ: Opérations en masse : Contenu */
$handler->display->display_options['fields']['views_bulk_operations']['id'] = 'views_bulk_operations';
$handler->display->display_options['fields']['views_bulk_operations']['table'] = 'views_entity_node';
$handler->display->display_options['fields']['views_bulk_operations']['field'] = 'views_bulk_operations';
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['display_type'] = '0';
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['enable_select_all_pages'] = 1;
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['row_clickable'] = 1;
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['force_single'] = 0;
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['entity_load_capacity'] = '10';
$handler->display->display_options['fields']['views_bulk_operations']['vbo_operations'] = array(
'action::node_unpublish_action' => array(
'selected' => 1,
'postpone_processing' => 0,
'skip_confirmation' => 0,
'skip_permission_check' => 0,
'override_label' => 0,
'label' => '',
),
'action::views_bulk_operations_delete_item' => array(
'selected' => 1,
'postpone_processing' => 0,
'skip_confirmation' => 0,
'skip_permission_check' => 0,
'override_label' => 0,
'label' => '',
'settings' => array(
'log' => 0,
),
),
);
/* Champ: Contenu : uid de l'auteur */
$handler->display->display_options['fields']['uid']['id'] = 'uid';
$handler->display->display_options['fields']['uid']['table'] = 'node';
$handler->display->display_options['fields']['uid']['field'] = 'uid';
$handler->display->display_options['fields']['uid']['label'] = 'Auteur';
/* Champ: Contenu : Titre */
$handler->display->display_options['fields']['title']['id'] = 'title';
$handler->display->display_options['fields']['title']['table'] = 'node';
$handler->display->display_options['fields']['title']['field'] = 'title';
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
/* Champ: Contenu : Date de publication */
$handler->display->display_options['fields']['created']['id'] = 'created';
$handler->display->display_options['fields']['created']['table'] = 'node';
$handler->display->display_options['fields']['created']['field'] = 'created';
$handler->display->display_options['fields']['created']['date_format'] = 'long';
$handler->display->display_options['fields']['created']['second_date_format'] = 'long';
$handler->display->display_options['fields']['created']['format_date_sql'] = 0;
/* Champ: Contenu : Chemin */
$handler->display->display_options['fields']['path']['id'] = 'path';
$handler->display->display_options['fields']['path']['table'] = 'node';
$handler->display->display_options['fields']['path']['field'] = 'path';
$handler->display->display_options['fields']['path']['label'] = 'Modifier';
$handler->display->display_options['fields']['path']['alter']['alter_text'] = TRUE;
$handler->display->display_options['fields']['path']['alter']['text'] = '<a href="[path]/edit">Modifier</a>';
$handler->display->display_options['fields']['path']['alter']['path'] = '[path]/edit';
/* Critère de tri: Contenu : Date de publication */
$handler->display->display_options['sorts']['created']['id'] = 'created';
$handler->display->display_options['sorts']['created']['table'] = 'node';
$handler->display->display_options['sorts']['created']['field'] = 'created';
$handler->display->display_options['sorts']['created']['order'] = 'DESC';
/* Critère de filtrage: Contenu : Published status */
$handler->display->display_options['filters']['status']['id'] = 'status';
$handler->display->display_options['filters']['status']['table'] = 'node';
$handler->display->display_options['filters']['status']['field'] = 'status';
$handler->display->display_options['filters']['status']['value'] = '0';
$handler->display->display_options['filters']['status']['group'] = 1;
$handler->display->display_options['filters']['status']['expose']['operator'] = FALSE;
/* Critère de filtrage: Contenu : Type */
$handler->display->display_options['filters']['type']['id'] = 'type';
$handler->display->display_options['filters']['type']['table'] = 'node';
$handler->display->display_options['filters']['type']['field'] = 'type';
$handler->display->display_options['filters']['type']['value'] = array(
'form1' => 'form1',
);
/* Display: Page */
$handler = $view->new_display('page', 'Page', 'page');
$handler->display->display_options['path'] = 'possible-spam';
$translatables['possible_spam'] = array(
t('Master'),
t('Possible spam'),
t('plus'),
t('Appliquer'),
t('Réinitialiser'),
t('Trier par'),
t('Asc'),
t('Desc'),
t('Éléments par page'),
t('- Tout -'),
t('Décalage'),
t('« premier'),
t('‹ précédent'),
t('suivant ›'),
t('dernier »'),
t('Contenu'),
t('- Choisir une opération -'),
t('Auteur'),
t('Titre'),
t('Date de publication'),
t('Modifier'),
t('<a href="[path]/edit">Modifier</a>'),
t('Page'),
);
return $view;
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment