Passed
Pull Request — development (#3540)
by Emanuele
07:11
created

Agreement   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 262
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 72
dl 0
loc 262
ccs 0
cts 122
cp 0
rs 10
c 0
b 0
f 0
wmc 22

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getParsedText() 0 5 1
A accept() 0 21 1
A save() 0 14 2
A isWritable() 0 5 2
A _backupId() 0 13 2
A storeBackup() 0 9 2
A __construct() 0 10 3
A _createBackup() 0 20 4
A checkAccepted() 0 14 1
A buildName() 0 3 1
A getPlainText() 0 17 3
1
<?php
2
3
/**
4
 * This class takes care of the registration agreement
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * @version 2.0 dev
11
 *
12
 */
13
14
namespace ElkArte;
15
16
use BBC\ParserWrapper;
17
use Elkarte\Util;
18
19
/**
20
 * Class Agreement
21
 *
22
 * A simple class to take care of the registration agreement
23
 */
24
class Agreement
25
{
26
	/**
27
	 * Default language of the agreement.
28
	 *
29
	 * @var string
30
	 */
31
	protected $_language = '';
32
33
	/**
34
	 * The directory where backups are stored
35
	 *
36
	 * @var string
37
	 */
38
	protected $_backup_dir = '';
39
40
	/**
41
	 * The name of the file where the agreement is stored
42
	 *
43
	 * @var string
44
	 */
45
	protected $_file_name = 'Agreement';
46
47
	/**
48
	 * The name of the directory where the backup will be saved
49
	 *
50
	 * @var string
51
	 */
52
	protected $_backupdir_name = 'agreements';
53
54
	/**
55
	 * The name of the log table
56
	 *
57
	 * @var string
58
	 */
59
	protected $_log_table_name = '{db_prefix}log_agreement_accept';
60
61
	/**
62
	 * The database object
63
	 *
64
	 * @var Object
65
	 */
66
	protected $_db = null;
67
68
	/**
69
	 * Everything starts here.
70
	 *
71
	 * @param string $language the wanted language of the agreement.
72
	 * @param string $backup_dir where to store the backup of the agreements.
73
	 */
74
	public function __construct($language, $backup_dir = null)
75
	{
76
		$this->_language = ucfirst(strtr($language, array('.' => '')));
77
78
		if ($backup_dir === null || !file_exists($backup_dir))
79
		{
80
			$backup_dir = BOARDDIR . '/packages/backups/' . $this->_backupdir_name;
81
		}
82
		$this->_backup_dir = $backup_dir;
83
		$this->_db = database();
84
	}
85
86
	/**
87
	 * Stores a text into the agreement file.
88
	 * It stores strictly on the *language* agreement, no fallback.
89
	 * If the language passed to the class is empty, then it uses agreement.txt.
90
	 *
91
	 * @param string $text the language of the agreement we want.
92
	 * @param bool $update_backup if store a copy of the text of the agreements.
93
	 * @return bool|string
94
	 */
95
	public function save($text, $update_backup = false)
96
	{
97
		$backup_id = '';
98
		if ($update_backup)
99
		{
100
			$backup_id = $this->storeBackup();
101
		}
102
103
		// Off it goes to the agreement file.
104
		$fp = fopen($this->buildName($this->_language), 'w');
105
		fwrite($fp, str_replace("\r", '', $text));
106
		fclose($fp);
107
108
		return $backup_id;
109
	}
110
111
	/**
112
	 * Creates a backup of the current version of the agreement/s.
113
	 *
114
	 * @return string|bool the backup_id if successful, false if creating the backup fails
115
	 */
116
	public function storeBackup()
117
	{
118
		$backup_id = $this->_backupId();
119
		if (!$this->_createBackup($backup_id))
120
		{
121
			$backup_id = false;
122
		}
123
124
		return $backup_id;
125
	}
126
127
	/**
128
	 * Retrieves the plain text version of the agreement directly from
129
	 * the file that contains it.
130
	 *
131
	 * It uses the language, but if the localized version doesn't exist
132
	 * then it may return the english version.
133
	 *
134
	 * @param bool $fallback if fallback to the English version (default true).
135
	 *
136
	 * @return bool|string
137
	 */
138
	public function getPlainText($fallback = true, $language = null)
139
	{
140
		$language = $language ?? $this->_language;
141
		$file = $this->buildName($language);
142
143
		// Have we got a localized one?
144
		if (file_exists($file))
145
		{
146
			return trim(file_get_contents($file));
147
		}
148
149
		if ($fallback)
150
		{
151
			return $this->getPlainText(false, 'English');
152
		}
153
154
		return '';
155
	}
156
157
	/**
158
	 * Retrieves the BBC-parsed version of the agreement.
159
	 *
160
	 * It uses the language, but if the localized version doesn't exist
161
	 * then it may return the english version.
162
	 *
163
	 * @param bool $fallback if fallback to the English version (default true).
164
	 *
165
	 * @return string
166
	 */
167
	public function getParsedText($fallback = true)
168
	{
169
		$bbc_parser = ParserWrapper::instance();
170
171
		return $bbc_parser->parseAgreement($this->getPlainText($fallback));
172
	}
173
174
	/**
175
	 * Retrieves the BBC-parsed version of the agreement.
176
	 *
177
	 * If the language passed to the class is empty, then it uses agreement.txt.
178
	 */
179
	public function isWritable()
180
	{
181
		$filename = $this->buildName($this->_language);
182
183
		return file_exists($filename) && is_writable($filename);
184
	}
185
186
	/**
187
	 * Test if the user accepted the current agreement or not.
188
	 *
189
	 * @param int $id_member The id of the member
190
	 * @param string $version The date of the agreement
191
	 */
192
	public function checkAccepted($id_member, $version)
193
	{
194
		$accepted = $this->_db->fetchQuery('
195
			SELECT 1
196
			FROM ' . $this->_log_table_name . '
197
			WHERE version = {string:version}
198
				AND id_member = {int:id_member}',
199
			array(
200
				'id_member' => $id_member,
201
				'version' => $version,
202
			)
203
		);
204
205
		return !empty($accepted);
206
	}
207
208
	/**
209
	 * Log that the member accepted the agreement
210
	 *
211
	 * @param int $id_member
212
	 * @param string $ip
213
	 * @param string $version
214
	 * @throws \Exception
215
	 */
216
	public function accept($id_member, $ip, $version)
217
	{
218
		$db = database();
219
220
		$db->insert('ignore',
221
			$this->_log_table_name,
222
			array(
223
				'version' => 'string-20',
224
				'id_member' => 'int',
225
				'accepted_date' => 'date',
226
				'accepted_ip' => 'string-255',
227
			),
228
			array(
229
				array(
230
					'version' => $version,
231
					'id_member' => $id_member,
232
					'accepted_date' => Util::strftime('%Y-%m-%d', forum_time(false)),
233
					'accepted_ip' => $ip,
234
				)
235
			),
236
			array('version', 'id_member')
237
		);
238
	}
239
240
	protected function _backupId()
241
	{
242
		$backup_id = Util::strftime('%Y-%m-%d', forum_time(false));
243
		$counter = '';
244
		$merger = '';
245
246
		while (file_exists($this->_backup_dir . '/' . $backup_id . $merger . $counter . '/'))
247
		{
248
			$counter++;
249
			$merger = '_';
250
		}
251
252
		return $backup_id . $merger . $counter;
253
	}
254
255
	/**
256
	 * Creates a full backup of all the agreements.
257
	 *
258
	 * @param string $backup_id the name of the directory of the backup
259
	 * @return bool true if successful, false if fails to create the directory
260
	 */
261
	protected function _createBackup($backup_id)
262
	{
263
		$destination = $this->_backup_dir . '/' . $backup_id . '/';
264
		if (!file_exists($this->_backup_dir))
265
		{
266
			@mkdir($this->_backup_dir);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

266
			/** @scrutinizer ignore-unhandled */ @mkdir($this->_backup_dir);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
267
		}
268
269
		if (!@mkdir($destination))
270
		{
271
			return false;
272
		}
273
274
		$glob = new \GlobIterator(SOURCEDIR . '/ElkArte/Languages/' . $this->_file_name . '/*.txt', \FilesystemIterator::SKIP_DOTS);
275
		foreach ($glob as $file)
276
		{
277
			copy($file->getPathname(), $destination . $file->getBasename());
278
		}
279
280
		return true;
281
	}
282
283
	protected function buildName($language)
284
	{
285
		return SOURCEDIR . '/ElkArte/Languages/' . $this->_file_name . '/' . $language . '.txt';
286
	}
287
}
288