Passed
Push — master ( af9f4d...a3d690 )
by Julito
12:39
created

diff()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 2
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ResourceLink;
5
use Chamilo\CoreBundle\Framework\Container;
6
use ChamiloSession as Session;
7
8
/**
9
 * Functions and main code for the download folder feature.
10
 */
11
set_time_limit(0);
12
13
require_once __DIR__.'/../inc/global.inc.php';
14
15
api_protect_course_script();
16
17
$sysCoursePath = api_get_path(SYS_COURSE_PATH);
0 ignored issues
show
Bug introduced by
The constant SYS_COURSE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
18
$courseInfo = api_get_course_info();
19
$courseId = api_get_course_int_id();
20
$sessionId = api_get_session_id();
21
$groupId = api_get_group_id();
22
$courseCode = api_get_course_id();
23
$repo = Container::getDocumentRepository();
24
25
// Check if folder exists in current course.
26
$documentInfo = DocumentManager::get_document_data_by_id(
0 ignored issues
show
Deprecated Code introduced by
The function DocumentManager::get_document_data_by_id() has been deprecated: use $repo->find() ( Ignorable by Annotation )

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

26
$documentInfo = /** @scrutinizer ignore-deprecated */ DocumentManager::get_document_data_by_id(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
27
    $_GET['id'],
28
    $courseCode,
29
    false,
30
    0
31
);
32
33
if (!empty($sessionId)) {
34
    /* If no data found and session id exists
35
       try to look the file inside the session */
36
    if (empty($documentInfo)) {
37
        $documentInfo = DocumentManager::get_document_data_by_id(
0 ignored issues
show
Deprecated Code introduced by
The function DocumentManager::get_document_data_by_id() has been deprecated: use $repo->find() ( Ignorable by Annotation )

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

37
        $documentInfo = /** @scrutinizer ignore-deprecated */ DocumentManager::get_document_data_by_id(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
38
            $_GET['id'],
39
            $courseCode,
40
            false,
41
            $sessionId
42
        );
43
    }
44
}
45
46
$path = $documentInfo['path'];
47
48
if (empty($path)) {
49
    $path = '/';
50
}
51
52
// A student should not be able to download a root shared directory
53
if (('/shared_folder' == $path ||
54
    $path == '/shared_folder_session_'.api_get_session_id()) &&
55
    (!api_is_allowed_to_edit() || !api_is_platform_admin())
56
) {
57
    api_not_allowed(true);
58
}
59
60
// Creating a ZIP file.
61
$tempZipFile = api_get_path(SYS_ARCHIVE_PATH).api_get_unique_id().".zip";
62
63
$name = ('/' == $path) ? 'documents.zip' : $documentInfo['title'].'.zip';
64
$zip = api_create_zip($name);
65
$doc_table = Database::get_course_table(TABLE_DOCUMENT);
66
$prop_table = Database::get_course_table(TABLE_ITEM_PROPERTY);
0 ignored issues
show
Bug introduced by
The constant TABLE_ITEM_PROPERTY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
67
68
// We need this path to clean it out of the zip file
69
// I'm not using dir name as it gives too much problems (cfr.)
70
$remove_dir = ('/' != $path) ? substr($path, 0, strlen($path) - strlen(basename($path))) : '/';
71
72
// Put the files in the zip
73
// 2 possibilities: Admins get all files and folders in the selected folder (except for the deleted ones)
74
// Normal users get only visible files that are in visible folders
75
function fixDocumentNameCallback($p_event, &$p_header)
76
{
77
    global $remove_dir;
78
    $files = Session::read('doc_files_to_download');
79
    $storedFile = $remove_dir.$p_header['stored_filename'];
80
81
    if (!isset($files[$storedFile])) {
82
        return 0;
83
    }
84
85
    $documentData = $files[$storedFile];
86
    $documentNameFixed = DocumentManager::undoFixDocumentName(
87
        $documentData['path'],
88
        $documentData['c_id'],
89
        $documentData['session_id'],
90
        $documentData['to_group_id']
91
    );
92
93
    // Changes file.phps to file.php
94
    $basename = basename($documentNameFixed);
95
    $basenamePHPFixed = str_replace('.phps', '.php', $basename);
96
    $documentNameFixed = str_replace(
97
        $basename,
98
        $basenamePHPFixed,
99
        $documentNameFixed
100
    );
101
102
    if ('/' != $remove_dir) {
103
        $documentNameFixed = str_replace($remove_dir, '/', $documentNameFixed);
104
        if ('/' == substr($documentNameFixed, 0, 1)) {
105
            $documentNameFixed = substr($documentNameFixed, 1, api_strlen($documentNameFixed));
106
        }
107
    } else {
108
        $documentNameFixed = ltrim($documentNameFixed, '/');
109
    }
110
111
    $p_header['stored_filename'] = $documentNameFixed;
112
113
    return 1;
114
}
115
116
$groupJoin = '';
117
if (!empty($groupId)) {
118
    $table = Database::get_course_table(TABLE_GROUP);
119
    $groupJoin = " INNER JOIN $table g ON (g.iid = props.to_group_id AND g.c_id = docs.c_id)";
120
    $groupCondition = " g.id = ".$groupId;
121
} else {
122
    $groupCondition = " (props.to_group_id = 0 OR props.to_group_id IS NULL ) ";
123
}
124
$tblDocument = Database::get_course_table(TABLE_DOCUMENT);
125
126
// Launch event
127
Event::event_download($name);
0 ignored issues
show
Bug introduced by
The method event_download() does not exist on Event. ( Ignorable by Annotation )

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

127
Event::/** @scrutinizer ignore-call */ 
128
       event_download($name);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
128
129
// Admins are allowed to download invisible files
130
if (api_is_allowed_to_edit()) {
131
    // Set the path that will be used in the query
132
    if ('/' === $path) {
133
        $querypath = ''; // To prevent ...path LIKE '//%'... in query
134
    } else {
135
        $querypath = $path;
136
    }
137
    $querypath = Database::escape_string($querypath);
138
139
    // Search for all files that are not deleted => visibility != 2
140
    $sql = "SELECT
141
                docs.path,
142
                docs.session_id,
143
                docs.id,
144
                docs.c_id
145
            FROM resource_node AS n
146
            INNER JOIN $tblDocument AS docs
147
            ON (docs.resource_node_id = n.id)
148
            INNER JOIN resource_link l
149
            ON (l.resource_node_id = n.id)
150
            WHERE
151
                docs.c_id = $courseId AND
152
                docs.path LIKE '".$querypath."/%' AND
153
                docs.filetype = 'file' AND
154
                docs.path NOT LIKE '%_DELETED_%' AND
155
                l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
156
            ";
157
158
    $sql .= DocumentManager::getSessionFolderFilters($querypath, $sessionId);
159
    $result = Database::query($sql);
160
    $files = [];
161
    while ($row = Database::fetch_array($result, 'ASSOC')) {
162
        $files[$row['path']] = $row;
163
    }
164
165
    foreach ($files as $not_deleted_file) {
166
        // Filtering folders and
167
        if (strpos($not_deleted_file['path'], 'chat_files') > 0 ||
168
            strpos($not_deleted_file['path'], 'shared_folder') > 0
169
        ) {
170
            if (!empty($sessionId)) {
171
                if ($not_deleted_file['session_id'] != $sessionId) {
172
                    continue;
173
                }
174
            }
175
        }
176
177
        $file = $courseInfo['path'].'/document'.$not_deleted_file['path'];
178
        //$document = $repo->find($not_deleted_file['id']);
179
        $data = $repo->getDocumentContent($not_deleted_file['id']);
180
        $zip->addFile($not_deleted_file['path'], $data);
181
182
        /*@$zip->add(
183
            $sysCoursePath.$courseInfo['path'].'/document'.$not_deleted_file['path'],
184
            PCLZIP_OPT_REMOVE_PATH,
185
            $sysCoursePath.$courseInfo['path'].'/document'.$remove_dir,
186
            PCLZIP_CB_PRE_ADD,
187
            'fixDocumentNameCallback'
188
        );*/
189
    }
190
    $zip->finish();
191
} else {
192
    // For other users, we need to create a zip  file with only visible files and folders
193
    if ('/' == $path) {
194
        $querypath = ''; // To prevent ...path LIKE '//%'... in query
195
    } else {
196
        $querypath = $path;
197
    }
198
199
    /* A big problem: Visible files that are in a hidden folder are
200
       included when we do a query for visibility='v'
201
       So... I do it in a couple of steps:
202
       1st: Get all files that are visible in the given path
203
    */
204
    $querypath = Database::escape_string($querypath);
205
    $sql = "SELECT path, docs.session_id, docs.id, props.to_group_id, docs.c_id
206
            FROM $doc_table AS docs
207
            INNER JOIN $prop_table AS props
208
            ON
209
                docs.id = props.ref AND
210
                docs.c_id = props.c_id
211
                $groupJoin
212
            WHERE
213
                docs.c_id = $courseId AND
214
                props.tool = '".TOOL_DOCUMENT."' AND
215
                docs.path LIKE '".$querypath."/%' AND
216
                props.visibility = '1' AND
217
                docs.filetype = 'file' AND
218
                (props.session_id IN ('0', '$sessionId') OR props.session_id IS NULL) AND
219
                $groupCondition
220
            ";
221
222
    $sql .= DocumentManager::getSessionFolderFilters($querypath, $sessionId);
223
    $result = Database::query($sql);
224
225
    $files = [];
226
    $all_visible_files_path = [];
227
    // Add them to an array
228
    while ($all_visible_files = Database::fetch_assoc($result)) {
229
        if (strpos($all_visible_files['path'], 'chat_files') > 0 ||
230
            strpos($all_visible_files['path'], 'shared_folder') > 0
231
        ) {
232
            if (!empty($sessionId)) {
233
                if ($all_visible_files['session_id'] != $sessionId) {
234
                    continue;
235
                }
236
            }
237
        }
238
        $all_visible_files_path[] = $all_visible_files['path'];
239
        $files[$all_visible_files['path']] = $all_visible_files;
240
    }
241
242
    // 2nd: Get all folders that are invisible in the given path
243
    $sql = "SELECT path, docs.session_id, docs.id, props.to_group_id, docs.c_id
244
            FROM $doc_table AS docs
245
            INNER JOIN $prop_table AS props
246
            ON
247
                docs.id = props.ref AND
248
                docs.c_id = props.c_id
249
            WHERE
250
                docs.c_id = $courseId AND
251
                props.tool = '".TOOL_DOCUMENT."' AND
252
                docs.path LIKE '".$querypath."/%' AND
253
                props.visibility <> '1' AND
254
                (props.session_id IN ('0', '$sessionId') OR props.session_id IS NULL) AND
255
                docs.filetype = 'folder'";
256
    $query2 = Database::query($sql);
257
258
    // If we get invisible folders, we have to filter out these results from all visible files we found
259
    if (Database::num_rows($query2) > 0) {
260
        $files = [];
261
        // Add item to an array
262
        while ($invisible_folders = Database::fetch_assoc($query2)) {
263
            //3rd: Get all files that are in the found invisible folder (these are "invisible" too)
264
            $sql = "SELECT path, docs.id, props.to_group_id, docs.c_id
265
                    FROM $doc_table AS docs
266
                    INNER JOIN $prop_table AS props
267
                    ON
268
                        docs.id = props.ref AND
269
                        docs.c_id = props.c_id
270
                    WHERE
271
                        docs.c_id = $courseId AND
272
                        props.tool ='".TOOL_DOCUMENT."' AND
273
                        docs.path LIKE '".$invisible_folders['path']."/%' AND
274
                        docs.filetype = 'file' AND
275
                        (props.session_id IN ('0', '$sessionId') OR props.session_id IS NULL) AND
276
                        props.visibility ='1'";
277
            $query3 = Database::query($sql);
278
            // Add tem to an array
279
            while ($files_in_invisible_folder = Database::fetch_assoc($query3)) {
280
                $files_in_invisible_folder_path[] = $files_in_invisible_folder['path'];
281
                $files[$files_in_invisible_folder['path']] = $files_in_invisible_folder;
282
            }
283
        }
284
285
        // Compare the array with visible files and the array with files in invisible folders
286
        // and keep the difference (= all visible files that are not in an invisible folder)
287
        $files_for_zipfile = diff(
288
            (array) $all_visible_files_path,
289
            (array) $files_in_invisible_folder_path
290
        );
291
    } else {
292
        // No invisible folders found, so all visible files can be added to the zipfile
293
        $files_for_zipfile = $all_visible_files_path;
294
    }
295
296
    Session::write('doc_files_to_download', $files);
297
298
    // Add all files in our final array to the zipfile
299
    for ($i = 0; $i < count($files_for_zipfile); $i++) {
300
        $zip->add(
301
            $sysCoursePath.$courseInfo['path'].'/document'.$files_for_zipfile[$i],
302
            PCLZIP_OPT_REMOVE_PATH,
0 ignored issues
show
Bug introduced by
The constant PCLZIP_OPT_REMOVE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
303
            $sysCoursePath.$courseInfo['path'].'/document'.$remove_dir,
304
            PCLZIP_CB_PRE_ADD,
0 ignored issues
show
Bug introduced by
The constant PCLZIP_CB_PRE_ADD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
305
            'fixDocumentNameCallback'
306
        );
307
    }
308
    Session::erase('doc_files_to_download');
309
}
310
exit;
311
312
/**
313
 * Returns the difference between two arrays, as an array of those key/values
314
 * Use this as array_diff doesn't give the.
315
 *
316
 * @param array $arr1 first array
317
 * @param array $arr2 second array
318
 *
319
 * @return array difference between the two arrays
320
 */
321
function diff($arr1, $arr2)
322
{
323
    $res = [];
324
    $r = 0;
325
    foreach ($arr1 as &$av) {
326
        if (!in_array($av, $arr2)) {
327
            $res[$r] = $av;
328
            $r++;
329
        }
330
    }
331
332
    return $res;
333
}
334