|
1
|
|
|
<?php |
|
2
|
|
|
/* For licensing terms, see /license.txt */ |
|
3
|
|
|
|
|
4
|
|
|
/** |
|
5
|
|
|
* Code library for HotPotatoes integration. |
|
6
|
|
|
* @package chamilo.exercise |
|
7
|
|
|
* @author Istvan Mandak (original author) |
|
8
|
|
|
*/ |
|
9
|
|
|
|
|
10
|
|
|
/* TODO: This is a global variable with too simple name, conflicts are possible. |
|
11
|
|
|
Better eliminate it. Correct the test unit too. */ |
|
12
|
|
|
$dbTable = Database::get_course_table(TABLE_DOCUMENT); |
|
13
|
|
|
|
|
14
|
|
|
/** |
|
15
|
|
|
* Creates a hotpotato directory. |
|
16
|
|
|
* |
|
17
|
|
|
* If a directory of that name already exists, don't create any. If a file of that name exists, remove it and create a directory. |
|
18
|
|
|
* @param string Wanted path |
|
19
|
|
|
* @return boolean Always true so far |
|
20
|
|
|
*/ |
|
21
|
|
|
function hotpotatoes_init($base_work_dir) |
|
22
|
|
|
{ |
|
23
|
|
|
//global $_course, $_user; |
|
24
|
|
|
$document_path = $base_work_dir.'/'; |
|
25
|
|
|
if (!is_dir($document_path)) { |
|
26
|
|
|
if (is_file($document_path)) { |
|
27
|
|
|
@unlink($document_path); |
|
28
|
|
|
} |
|
29
|
|
|
@mkdir($document_path, api_get_permissions_for_new_directories()); |
|
30
|
|
|
return true; |
|
31
|
|
|
} else { |
|
32
|
|
|
return false; |
|
33
|
|
|
} |
|
34
|
|
|
//why create a .htaccess here? |
|
35
|
|
|
//if (!is_file($document_path.".htacces")) |
|
36
|
|
|
//{ |
|
37
|
|
|
// if (!($fp = fopen($document_path.".htaccess", "w"))) { |
|
38
|
|
|
// } |
|
39
|
|
|
// $str = "order deny,allow\nallow from all"; |
|
40
|
|
|
// if (!fwrite($fp,$str)) { } |
|
41
|
|
|
//} |
|
42
|
|
|
} |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* Gets the title of the quiz file given as parameter. |
|
46
|
|
|
* @param string File name |
|
47
|
|
|
* @param string File path |
|
48
|
|
|
* @return string The exercise title |
|
49
|
|
|
*/ |
|
50
|
|
|
function GetQuizName($fname, $fpath) |
|
51
|
|
|
{ |
|
52
|
|
|
$title = GetComment($fname); |
|
53
|
|
|
if (trim($title) == '') { |
|
54
|
|
|
if (file_exists($fpath.$fname)) { |
|
55
|
|
|
if (!($fp = @fopen($fpath.$fname, 'r'))) { |
|
56
|
|
|
//die('Could not open Quiz input.'); |
|
57
|
|
|
return basename($fname); |
|
58
|
|
|
} |
|
59
|
|
|
|
|
60
|
|
|
$contents = @fread($fp, filesize($fpath.$fname)); |
|
61
|
|
|
@fclose($fp); |
|
62
|
|
|
|
|
63
|
|
|
$title = api_get_title_html($contents); |
|
64
|
|
|
} |
|
65
|
|
|
} |
|
66
|
|
|
if ($title == '') { |
|
67
|
|
|
$title = basename($fname); |
|
68
|
|
|
} |
|
69
|
|
|
return (string)$title; |
|
70
|
|
|
} |
|
71
|
|
|
|
|
72
|
|
|
/** |
|
73
|
|
|
* Gets the comment about a file from the corresponding database record. |
|
74
|
|
|
* @param string File path |
|
75
|
|
|
* @return string Comment from the database record |
|
76
|
|
|
* Added conditional to the table if is empty. |
|
77
|
|
|
*/ |
|
78
|
|
|
function GetComment($path, $course_code = '') |
|
79
|
|
|
{ |
|
80
|
|
|
$dbTable = Database::get_course_table(TABLE_DOCUMENT); |
|
81
|
|
|
$course_info = api_get_course_info($course_code); |
|
82
|
|
|
$path = Database::escape_string($path); |
|
83
|
|
|
if (!empty($course_info) && !empty($path)) { |
|
84
|
|
|
$query = "SELECT comment FROM $dbTable WHERE c_id = {$course_info['real_id']}"; |
|
85
|
|
|
$result = Database::query($query); |
|
86
|
|
|
while ($row = Database::fetch_array($result)) { |
|
87
|
|
|
return $row[0]; |
|
88
|
|
|
} |
|
89
|
|
|
} |
|
90
|
|
|
return null; |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
/** |
|
94
|
|
|
* Sets the comment in the database for a particular path. |
|
95
|
|
|
* @param string File path |
|
96
|
|
|
* @param string Comment to set |
|
97
|
|
|
* @return string Result of the database operation (Database::query will output some message directly on error anyway) |
|
98
|
|
|
*/ |
|
99
|
|
|
function SetComment($path, $comment) |
|
100
|
|
|
{ |
|
101
|
|
|
$dbTable = Database::get_course_table(TABLE_DOCUMENT); |
|
102
|
|
|
$path = Database::escape_string($path); |
|
103
|
|
|
$comment = Database::escape_string($comment); |
|
104
|
|
|
$course_id = api_get_course_int_id(); |
|
105
|
|
|
$query = "UPDATE $dbTable SET comment='$comment' |
|
106
|
|
|
WHERE $course_id AND path='$path'"; |
|
107
|
|
|
$result = Database::query($query); |
|
108
|
|
|
|
|
109
|
|
|
return $result; |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
/** |
|
113
|
|
|
* Reads the file contents into a string. |
|
114
|
|
|
* @param string Urlencoded path |
|
115
|
|
|
* @return string The file contents or false on security error |
|
116
|
|
|
*/ |
|
117
|
|
|
function ReadFileCont($full_file_path) |
|
118
|
|
|
{ |
|
119
|
|
|
if (empty($full_file_path)) { |
|
120
|
|
|
return false; |
|
121
|
|
|
} |
|
122
|
|
|
if (Security::check_abs_path(dirname($full_file_path).'/', api_get_path(SYS_COURSE_PATH))) { |
|
123
|
|
|
if (is_file($full_file_path)) { |
|
124
|
|
|
if (!($fp = fopen(urldecode($full_file_path), 'r'))) { |
|
125
|
|
|
return ''; |
|
126
|
|
|
} |
|
127
|
|
|
$contents = fread($fp, filesize($full_file_path)); |
|
128
|
|
|
fclose($fp); |
|
129
|
|
|
return $contents; |
|
130
|
|
|
} |
|
131
|
|
|
} |
|
132
|
|
|
return false; |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* Writes the file contents into the given file path. |
|
137
|
|
|
* @param string Urlencoded path |
|
138
|
|
|
* @param string The file contents |
|
139
|
|
|
* @return boolean True on success, false on security error |
|
140
|
|
|
*/ |
|
141
|
|
|
function WriteFileCont($full_file_path, $content) |
|
142
|
|
|
{ |
|
143
|
|
|
// Check if this is not an attack, trying to get into other directories or something like that. |
|
144
|
|
|
$_course = api_get_course_info(); |
|
145
|
|
|
if (Security::check_abs_path(dirname($full_file_path).'/', api_get_path(SYS_COURSE_PATH).$_course['path'].'/')) { |
|
146
|
|
|
// Check if this is not an attack, trying to upload a php file or something like that. |
|
147
|
|
|
if (basename($full_file_path) != Security::filter_filename(basename($full_file_path))) { |
|
148
|
|
|
return false; |
|
149
|
|
|
} |
|
150
|
|
|
if (!($fp = fopen(urldecode($full_file_path), 'w'))) { |
|
|
|
|
|
|
151
|
|
|
//die('Could not open Quiz input.'); |
|
152
|
|
|
} |
|
153
|
|
|
fwrite($fp, $content); |
|
154
|
|
|
fclose($fp); |
|
155
|
|
|
return true; |
|
156
|
|
|
} |
|
157
|
|
|
return false; |
|
158
|
|
|
} |
|
159
|
|
|
|
|
160
|
|
|
/** |
|
161
|
|
|
* Gets the name of an img whose path is given (without directories or extensions). |
|
162
|
|
|
* @param string An image tag (<img src="...." ...>) |
|
163
|
|
|
* @return string The image file name or an empty string |
|
164
|
|
|
*/ |
|
165
|
|
|
function GetImgName($imgtag) |
|
166
|
|
|
{ |
|
167
|
|
|
// Select src tag from img tag. |
|
168
|
|
|
$match = array(); |
|
169
|
|
|
//preg_match('/(src=(["\'])1.*(["\'])1)/i', $imgtag, $match); //src |
|
170
|
|
|
preg_match('/src(\s)*=(\s)*[\'"]([^\'"]*)[\'"]/i', $imgtag, $match); //get the img src as contained between " or ' |
|
171
|
|
|
//list($key, $srctag) = each($match); |
|
172
|
|
|
$src = $match[3]; |
|
173
|
|
|
//$src = substr($srctag, 5, (strlen($srctag) - 7)); |
|
174
|
|
|
if (stristr($src, 'http') === false) { |
|
175
|
|
|
// Valid or invalid image name. |
|
176
|
|
|
if ($src == '') { |
|
177
|
|
|
return ''; |
|
178
|
|
|
} else { |
|
179
|
|
|
$tmp_src = basename($src) ; |
|
180
|
|
|
if ($tmp_src == '') { |
|
181
|
|
|
return $src; |
|
182
|
|
|
} else { |
|
183
|
|
|
return $tmp_src; |
|
184
|
|
|
} |
|
185
|
|
|
} |
|
186
|
|
|
} else { |
|
187
|
|
|
// The img tag contained "http", which means it is probably external. Ignore it. |
|
188
|
|
|
return ''; |
|
189
|
|
|
} |
|
190
|
|
|
} |
|
191
|
|
|
|
|
192
|
|
|
/** |
|
193
|
|
|
* Gets the source path of an image tag. |
|
194
|
|
|
* @param string An image tag |
|
195
|
|
|
* @return string The image source or "" |
|
196
|
|
|
*/ |
|
197
|
|
|
function GetSrcName($imgtag) |
|
198
|
|
|
{ |
|
199
|
|
|
// Select src tag from img tag. |
|
200
|
|
|
$match = array(); |
|
201
|
|
|
preg_match("|(src=\".*\" )|U", $imgtag, $match); //src |
|
202
|
|
|
list($key, $srctag) = each($match); |
|
|
|
|
|
|
203
|
|
|
$src = substr($srctag, 5, (strlen($srctag) - 7)); |
|
204
|
|
|
if (stristr($src, 'http') === false) { |
|
205
|
|
|
// valid or invalid image name |
|
206
|
|
|
return $src; |
|
207
|
|
|
} else { |
|
208
|
|
|
return ''; |
|
209
|
|
|
} |
|
210
|
|
|
} |
|
211
|
|
|
|
|
212
|
|
|
/** |
|
213
|
|
|
* Gets the image parameters from an image path. |
|
214
|
|
|
* @param string File name |
|
215
|
|
|
* @param string File path |
|
216
|
|
|
* @param reference Reference to a list of image parameters (emptied, then used to return results) |
|
217
|
|
|
* @param reference Reference to a counter of images (emptied, then used to return results) |
|
218
|
|
|
*/ |
|
219
|
|
|
function GetImgParams($fname, $fpath, &$imgparams, &$imgcount) |
|
220
|
|
|
{ |
|
221
|
|
|
// Select img tags from context. |
|
222
|
|
|
$imgparams = array(); |
|
223
|
|
|
//phpinfo(); |
|
224
|
|
|
$contents = ReadFileCont("$fpath"."$fname"); |
|
225
|
|
|
$matches = array(); |
|
226
|
|
|
preg_match_all('(<img .*>)', $contents, $matches); |
|
227
|
|
|
$imgcount = 0; |
|
228
|
|
|
while (list($int, $match) = each($matches)) { |
|
|
|
|
|
|
229
|
|
|
// Each match consists of a key and a value. |
|
230
|
|
|
while (list($key, $imgtag) = each($match)) { |
|
|
|
|
|
|
231
|
|
|
$imgname = GetImgName($imgtag); |
|
232
|
|
|
if ($imgname != '' && !in_array($imgname, $imgparams)) { |
|
233
|
|
|
array_push($imgparams, $imgname); // name (+ type) of the images in the html test |
|
234
|
|
|
$imgcount = $imgcount + 1; // number of images in the html test |
|
235
|
|
|
} |
|
236
|
|
|
} |
|
237
|
|
|
} |
|
238
|
|
|
} |
|
239
|
|
|
|
|
240
|
|
|
/** |
|
241
|
|
|
* Generates a list of hidden fields with the image params given as parameter to this function. |
|
242
|
|
|
* @param array List of image parameters |
|
243
|
|
|
* @return string String containing the hidden parameters built from the list given |
|
244
|
|
|
*/ |
|
245
|
|
|
function GenerateHiddenList($imgparams) |
|
246
|
|
|
{ |
|
247
|
|
|
$list = ''; |
|
248
|
|
|
if (is_array($imgparams)) { |
|
249
|
|
|
while (list($int, $string) = each($imgparams)) { |
|
|
|
|
|
|
250
|
|
|
$list .= "<input type=\"hidden\" name=\"imgparams[]\" value=\"$string\" />\n"; |
|
251
|
|
|
} |
|
252
|
|
|
} |
|
253
|
|
|
return $list; |
|
254
|
|
|
} |
|
255
|
|
|
|
|
256
|
|
|
/** |
|
257
|
|
|
* Searches for a node in the given array. |
|
258
|
|
|
* @param reference Reference to the array to search |
|
259
|
|
|
* @param string Node we are looking for in the array |
|
260
|
|
|
* @return mixed Node name or false if not found |
|
261
|
|
|
*/ |
|
262
|
|
|
function myarraysearch(&$array, $node) |
|
263
|
|
|
{ |
|
264
|
|
|
$match = false; |
|
265
|
|
|
$tmp_array = array(); |
|
266
|
|
|
for ($i = 0; $i < count($array); $i++) { |
|
267
|
|
|
if (!strcmp($array[$i], $node)) { |
|
268
|
|
|
$match = $node; |
|
269
|
|
|
} else { |
|
270
|
|
|
array_push($tmp_array, $array[$i]); |
|
271
|
|
|
} |
|
272
|
|
|
} |
|
273
|
|
|
$array = $tmp_array; |
|
274
|
|
|
return $match; |
|
275
|
|
|
} |
|
276
|
|
|
|
|
277
|
|
|
/** |
|
278
|
|
|
* Searches an image name into an array. |
|
279
|
|
|
* @param reference Reference to an array to search |
|
280
|
|
|
* @param string String to look for |
|
281
|
|
|
* @return mixed String given if found, false otherwise |
|
282
|
|
|
* @uses myarraysearch This function is just an additional layer on the myarraysearch() function |
|
283
|
|
|
*/ |
|
284
|
|
|
function CheckImageName(&$imgparams, $string) |
|
285
|
|
|
{ |
|
286
|
|
|
$checked = myarraysearch($imgparams, $string); |
|
287
|
|
|
return $checked; |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
/** |
|
291
|
|
|
* Replaces an image tag by ??? |
|
292
|
|
|
* @param string The content to replace |
|
293
|
|
|
* @return string The modified content |
|
294
|
|
|
*/ |
|
295
|
|
|
function ReplaceImgTag($content) |
|
296
|
|
|
{ |
|
297
|
|
|
$newcontent = $content; |
|
298
|
|
|
$matches = array(); |
|
299
|
|
|
preg_match_all('(<img .*>)', $content, $matches); |
|
300
|
|
|
while (list($int, $match) = each($matches)) { |
|
|
|
|
|
|
301
|
|
|
while (list($key, $imgtag) = each($match)) { |
|
|
|
|
|
|
302
|
|
|
$imgname = GetSrcName($imgtag); |
|
303
|
|
|
if ($imgname == '') { |
|
|
|
|
|
|
304
|
|
|
// Valid or invalid image name. |
|
305
|
|
|
} else { |
|
306
|
|
|
$prehref = $imgname; |
|
307
|
|
|
$posthref = basename($imgname); |
|
308
|
|
|
$newcontent = str_replace($prehref, $posthref, $newcontent); |
|
309
|
|
|
} |
|
310
|
|
|
} |
|
311
|
|
|
} |
|
312
|
|
|
return $newcontent; |
|
313
|
|
|
} |
|
314
|
|
|
|
|
315
|
|
|
/** |
|
316
|
|
|
* Fills the folder name up to a certain length with "0". |
|
317
|
|
|
* @param string Original folder name |
|
318
|
|
|
* @param integer Length to reach |
|
319
|
|
|
* @return string Modified folder name |
|
320
|
|
|
*/ |
|
321
|
|
|
function FillFolderName($name, $nsize) |
|
322
|
|
|
{ |
|
323
|
|
|
$str = ''; |
|
324
|
|
|
for ($i = 0; $i < $nsize - strlen($name); $i++) { |
|
325
|
|
|
$str .= '0'; |
|
326
|
|
|
} |
|
327
|
|
|
$str .= $name; |
|
328
|
|
|
return $str; |
|
329
|
|
|
} |
|
330
|
|
|
|
|
331
|
|
|
/** |
|
332
|
|
|
* Generates the HotPotato folder tree. |
|
333
|
|
|
* @param string Folder path |
|
334
|
|
|
* @return string Folder name (modified) |
|
335
|
|
|
*/ |
|
336
|
|
|
function GenerateHpFolder($folder) |
|
337
|
|
|
{ |
|
338
|
|
|
$filelist = array(); |
|
339
|
|
View Code Duplication |
if ($dir = @opendir($folder)) { |
|
340
|
|
|
while (($file = readdir($dir)) !== false) { |
|
341
|
|
|
if ($file != '.') { |
|
342
|
|
|
if ($file != '..') { |
|
343
|
|
|
$full_name = $folder.'/'.$file; |
|
344
|
|
|
if (is_dir($full_name)) { |
|
345
|
|
|
$filelist[] = $file; |
|
346
|
|
|
} |
|
347
|
|
|
} |
|
348
|
|
|
} |
|
349
|
|
|
} |
|
350
|
|
|
} |
|
351
|
|
|
$w = 0; |
|
352
|
|
|
do { |
|
353
|
|
|
$name = FillFolderName(mt_rand(1, 99999), 6); |
|
354
|
|
|
$checked = myarraysearch($filelist, $name); |
|
355
|
|
|
// As long as we find the name in the array, continue looping. As soon as we have a new element, quit. |
|
356
|
|
|
if ($checked) { |
|
357
|
|
|
$w = 1; |
|
358
|
|
|
} else { |
|
359
|
|
|
$w = 0; |
|
360
|
|
|
} |
|
361
|
|
|
} while ($w == 1); |
|
362
|
|
|
return $name; |
|
363
|
|
|
} |
|
364
|
|
|
|
|
365
|
|
|
/** |
|
366
|
|
|
* Gets the folder name (strips down path). |
|
367
|
|
|
* @param string Path |
|
368
|
|
|
* @return string Folder name stripped down |
|
369
|
|
|
*/ |
|
370
|
|
|
function GetFolderName($fname) |
|
371
|
|
|
{ |
|
372
|
|
|
$name = explode('/', $fname); |
|
373
|
|
|
$name = $name[sizeof($name) - 2]; |
|
374
|
|
|
return $name; |
|
375
|
|
|
} |
|
376
|
|
|
|
|
377
|
|
|
/** |
|
378
|
|
|
* Gets the folder path (with out the name of the folder itself) ? |
|
379
|
|
|
* @param string Path |
|
380
|
|
|
* @return string Path stripped down |
|
381
|
|
|
*/ |
|
382
|
|
|
function GetFolderPath($fname) |
|
383
|
|
|
{ |
|
384
|
|
|
$str = ''; |
|
385
|
|
|
$name = explode('/', $fname); |
|
386
|
|
|
for ($i = 0; $i < sizeof($name) - 1; $i++) { |
|
387
|
|
|
$str = $str.$name[$i].'/'; |
|
388
|
|
|
} |
|
389
|
|
|
return $str; |
|
390
|
|
|
} |
|
391
|
|
|
|
|
392
|
|
|
/** |
|
393
|
|
|
* Checks if there are subfolders. |
|
394
|
|
|
* @param string Path |
|
395
|
|
|
* @return integer 1 if a subfolder was found, 0 otherwise |
|
396
|
|
|
*/ |
|
397
|
|
|
function CheckSubFolder($path) |
|
398
|
|
|
{ |
|
399
|
|
|
$folder = GetFolderPath($path); |
|
400
|
|
|
$dflag = 0; |
|
401
|
|
View Code Duplication |
if ($dir = @opendir($folder)) { |
|
402
|
|
|
while (($file = readdir($dir)) !== false) { |
|
403
|
|
|
if ($file != '.') { |
|
404
|
|
|
if ($file != '..') { |
|
405
|
|
|
$full_name = $folder.'/'.$file; |
|
406
|
|
|
if (is_dir($full_name)) { |
|
407
|
|
|
$dflag = 1; // first directory |
|
408
|
|
|
} |
|
409
|
|
|
} |
|
410
|
|
|
} |
|
411
|
|
|
} |
|
412
|
|
|
} |
|
413
|
|
|
return $dflag; |
|
414
|
|
|
} |
|
415
|
|
|
|
|
416
|
|
|
/** |
|
417
|
|
|
* Hotpotato Garbage Collector |
|
418
|
|
|
* @param string Path |
|
419
|
|
|
* @param integer Flag |
|
420
|
|
|
* @param integer User id |
|
421
|
|
|
* @return void No return value, but echoes results |
|
422
|
|
|
*/ |
|
423
|
|
|
function HotPotGCt($folder, $flag, $user_id) |
|
424
|
|
|
{ |
|
425
|
|
|
// Garbage Collector |
|
426
|
|
|
$filelist = array(); |
|
427
|
|
|
if ($dir = @opendir($folder)) { |
|
428
|
|
|
while (($file = readdir($dir)) !== false) { |
|
429
|
|
|
if ($file != '.') { |
|
430
|
|
|
if ($file != '..') { |
|
431
|
|
|
$full_name = $folder.'/'.$file; |
|
432
|
|
|
if (is_dir($full_name)) { |
|
433
|
|
|
HotPotGCt($folder.'/'.$file, $flag, $user_id); |
|
434
|
|
|
} else { |
|
435
|
|
|
$filelist[] = $file; |
|
436
|
|
|
} |
|
437
|
|
|
} |
|
438
|
|
|
} |
|
439
|
|
|
} |
|
440
|
|
|
closedir($dir); |
|
441
|
|
|
} |
|
442
|
|
|
while (list($key, $val) = each($filelist)) { |
|
|
|
|
|
|
443
|
|
|
if (stristr($val, $user_id.'.t.html')) { |
|
444
|
|
|
if ($flag == 1) { |
|
445
|
|
|
my_delete($folder.'/'.$val); |
|
446
|
|
|
} else { |
|
447
|
|
|
echo $folder.'/'.$val.'<br />'; |
|
448
|
|
|
} |
|
449
|
|
|
} |
|
450
|
|
|
} |
|
451
|
|
|
} |
|
452
|
|
|
|
|
453
|
|
|
/** |
|
454
|
|
|
* Deletes an attempt from TABLE_STATISTIC_TRACK_E_HOTPOTATOES |
|
455
|
|
|
* @param int $id |
|
456
|
|
|
*/ |
|
457
|
|
|
function deleteAttempt($id) |
|
458
|
|
|
{ |
|
459
|
|
|
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES); |
|
460
|
|
|
$id = intval($id); |
|
461
|
|
|
$sql = "DELETE FROM $table WHERE id = $id"; |
|
462
|
|
|
Database::query($sql); |
|
463
|
|
|
} |
|
464
|
|
|
|
This check looks for the bodies of
ifstatements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
ifbodies can be removed. If you have an empty if but statements in theelsebranch, consider inverting the condition.could be turned into
This is much more concise to read.