Completed
Push — master ( 1d7363...6e2aa4 )
by Daniel
05:45
created

code/AkismetField.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Form field to handle akismet error display and handling
5
 *
6
 * @author Damian Mooyman
7
 * @package akismet
8
 */
9
class AkismetField extends FormField {
0 ignored issues
show
As per PSR2, the opening brace for this class should be on a new line.
Loading history...
10
	
11
	/**
12
	 * @var array
13
	 */
14
	private $fieldMapping = array();
15
16
	/**
17
	 *
18
	 * @var boolean
19
	 */
20
	protected $isSpam = null;
21
	
22
	/**
23
	 * Get the nested confirmation checkbox field
24
	 * 
25
	 * @return CheckboxField
26
	 */
27
	protected function confirmationField() {
28
		// Check if confirmation is required
29
		$requireConfirmation = Config::inst()->get('AkismetSpamProtector', 'require_confirmation');
30
		if(empty($requireConfirmation)) return null;
31
		
32
		// If confirmation is required then return a checkbox
33
		return CheckboxField::create(
34
			$this->getName(),
35
			_t('AkismetField.NOTIFICATION', 'I understand that, and give consent to, having this content submitted to '
36
				. 'a third party for automated spam detection')
37
		)
38
			->setError($this->Message(), $this->MessageType())
39
			->setForm($this->getForm());
40
	}
41
	
42
	public function Field($properties = array()) {
43
		$checkbox = $this->confirmationField();
44
		if($checkbox) return $checkbox->Field($properties);
45
	}
46
	
47
	function FieldHolder($properties = array()) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
48
		$checkbox = $this->confirmationField();
49
		if($checkbox) return $checkbox->FieldHolder($properties);
50
	}
51
	
52
	/**
53
	 * @return array
54
	 */
55
	public function getSpamMappedData() {
56
		if(empty($this->fieldMapping)) return null;
57
		
58
		$result = array();
59
		$data = $this->form->getData();
60
61
		foreach($this->fieldMapping as $fieldName => $mappedName) {
62
			$result[$mappedName] = (isset($data[$fieldName])) ? $data[$fieldName] : null;
63
		}
64
65
		return $result;
66
	}
67
	
68
	/**
69
	 * This function first gets values from mapped fields and then check these values against
70
	 * Mollom web service and then notify callback object with the spam checking result.
71
	 * @param Validator $validator
72
	 * @return 	boolean		- true when Mollom confirms that the submission is ham (not spam)
73
	 *						- false when Mollom confirms that the submission is spam 
74
	 * 						- false when Mollom say 'unsure'. 
75
	 *						  In this case, 'mollom_captcha_requested' session is set to true 
76
	 *       				  so that Field() knows it's time to display captcha 			
77
	 */
78
	public function validate($validator) {
79
		
80
		// Check that, if necessary, the user has given permission to check for spam
81
		$requireConfirmation = Config::inst()->get('AkismetSpamProtector', 'require_confirmation');
82
		if($requireConfirmation && !$this->Value()) {
83
			$validator->validationError(
84
				$this->name,
85
				_t(
86
					'AkismetField.NOTIFICATIONREQUIRED', 
87
					'You must give consent to submit this content to spam detection'
88
				),
89
				"error"
90
			);
91
			return false;
92
		}
93
		
94
		// Check result
95
		$isSpam = $this->getIsSpam();
96
		if(!$isSpam) return true;
97
98
		// Save error message
99
		$errorMessage = _t(
100
			'AkismetField.SPAM',
101
			"Your submission has been rejected because it was treated as spam."
102
		);
103
104
		// If spam should be allowed, let it pass and save it for later
105
		if(Config::inst()->get('AkismetSpamProtector', 'save_spam')) {
106
			// In order to save spam but still display the spam message, we must mock a form message
107
			// without failing the validation
108
			$errors = array(array(
109
				'fieldName' => $this->name,
110
				'message' => $errorMessage,
111
				'messageType' => 'error',
112
			));
113
			$formName = $this->getForm()->FormName();
114
			Session::set("FormInfo.{$formName}.errors", $errors);
115
			return true;
116
		} else {
117
			// Mark as spam
118
			$validator->validationError($this->name, $errorMessage, "error");
119
			return false;
120
		}
121
	}
122
123
	/**
124
	 * Determine if this field is spam or not
125
	 *
126
	 * @return boolean
127
	 */
128
	public function getIsSpam() {
129
		// Prevent multiple API calls
130
		if($this->isSpam !== null) return $this->isSpam;
131
132
		// Check bypass permission
133
		$permission = Config::inst()->get('AkismetSpamProtector', 'bypass_permission');
134
		if($permission && Permission::check($permission)) return false;
135
136
		// if the user has logged and there's no force check on member
137
		$bypassMember = Config::inst()->get('AkismetSpamProtector', 'bypass_members');
138
		if($bypassMember && Member::currentUser()) return false;
139
140
		// Map input fields to spam fields
141
		$mappedData = $this->getSpamMappedData();
142
		$content = isset($mappedData['body']) ? $mappedData['body'] : null;
143
		$author = isset($mappedData['authorName']) ? $mappedData['authorName'] : null;
144
		$email = isset($mappedData['authorMail']) ? $mappedData['authorMail'] : null;
145
		$url = isset($mappedData['authorUrl']) ? $mappedData['authorUrl'] : null;
146
147
		// Check result
148
		$api = AkismetSpamProtector::api();
149
		$this->isSpam = $api && $api->isSpam($content, $author, $email, $url);
150
		return $this->isSpam;
151
	}
152
	
153
	/**
154
	 * Get the fields to map spam protection too
155
	 *
156
	 * @return array Associative array of Field Names, where the indexes of the array are
157
	 * the field names of the form and the values are the standard spamprotection
158
	 * fields used by the protector
159
	 */
160
	public function getFieldMapping() {
161
		return $this->fieldMapping;
162
	}
163
164
	/**
165
	 * Set the fields to map spam protection too
166
	 *
167
	 * @param array $fieldMapping array of Field Names, where the indexes of the array are
168
	 * the field names of the form and the values are the standard spamprotection
169
	 * fields used by the protector
170
	 * @return self
171
	 */
172
	public function setFieldMapping($fieldMapping) {
173
		$this->fieldMapping = $fieldMapping;
174
		return $this;
175
	}
176
177
	/**
178
	 * Allow spam flag to be saved to the underlying data record
179
	 *
180
	 * @param \DataObjectInterface $record
181
	 */
182
	public function saveInto(\DataObjectInterface $record) {
183
		if(Config::inst()->get('AkismetSpamProtector', 'save_spam')) {
184
			$dataValue = $this->getIsSpam() ? 1 : 0;
185
			$record->setCastedField($this->name, $dataValue);
186
		}
187
	}
188
}
189