REST Service for POPCORN - ILIAS
alex
2025-09-18 d87115324de76811165e7a5071cf6340a00ca00e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
const mysql = require("mysql2/promise")
const dayjs = require("dayjs")
 
const log = require("../logger")
const searchLib = require("./search")
const {host, port, user, database, password} = require("./../settings").db
 
/////////////////////////////////////////////////////////////////////////
 
let poolP = initPool()
 
async function initPool() {
    return mysql.createPool({
        host,
        port,
        database,
        user,
        password,
    })
}
 
/////////////////////////////////////////////////////////////////////////
 
module.exports = {
    getUsers,
    getUserCount,
    getUserByLogin,
    getUserByUserId,
    getUserDefinedFields,
    getUserDefinedField,
    getUserTeilnahmen,
 
    getObjIdFromRefId,
    getRefIdFromObjId,
 
    getKurse,
    getKurs,
    getKursItems,
    getKursItems2,
    // getKursByObjId,
    // getKursByRefId,
    getKursTeilnehmer,
    getSingleKursTeilnehmer,
    getKursTeilnehmerCount,
 
    getKursOffline,
    setKursOffline,
 
    getUdf,
 
    getKursTeilnehmerRolle,
    getKursTeilnehmerByRole,
    getKursRoles,
 
    setStatus,
}
 
/////////////////////////////////////////////////////////////////////////
 
async function getUserSearchQuery(offset, limit, search) {
    const sel = `usr_id, login, firstname, lastname, gender, email, institution, street, city, zipcode, country, department, active`
    if (!search || search === '') {
        return `SELECT ${sel}
                FROM ${database}.usr_data AS ud
                WHERE login REGEXP '^[0-9]+$'
              LIMIT ${limit}
                OFFSET ${offset}
        `
    } else {
        const ids = await searchLib.search(search)
        if (!ids.length) {
            throw "nothing found"
            return await getUserSearchQuery(offset, limit)
        } // nothing found
        return `SELECT ${sel}
                FROM ${database}.usr_data AS ud
                WHERE login REGEXP '^[0-9]+$'
              AND usr_id IN (${ids.join(",")})
                    LIMIT ${limit}
                OFFSET ${offset}
        `
    }
}
 
async function getUsers(offset = 0, limit = 10, search = null) {
    log.info("++++++++++ get users", offset, limit, search)
    limit = Number(limit) || 10
    offset = Number(offset) || 0
    // TODO check args for SQL Injection
 
    const pool = await poolP
 
    try {
        let userSearchQuery = await getUserSearchQuery(offset, limit, search)
        // log.info(userSearchQuery)
        const [results, fields] = await pool.query(userSearchQuery)
        // console.log(results, fields)
        const count = await getUserCount(offset, limit, search)
        return {
            total: count,
            offset, limit,
            data: results,
        }
    } catch (ex) {
        return {
            total: 0,
            offset: 0,
            limit: 0,
            data: [],
        }
    }
}
 
async function getUserCount(offset, limit, search) {
    const pool = await poolP
    // const q = getUserSearchQuery(offset, limit, search)
    const q = await getUserSearchQuery(0, 1000000, search) // hier darf kein Limit sein, offset=0
    const q2 = `SELECT COUNT(*) AS count
                FROM (${q}) AS X`
    const [results, fields] = await pool.query(q2)
    return results[0].count
 
    // const [results, fields] = await pool.query(
    //    `SELECT COUNT(*)
    //     FROM ${database}.usr_data AS ud
    //     WHERE login REGEXP '^[0-9]+$'`
    // )
    // return results[0]["COUNT(*)"]
}
 
async function getUserByLogin(login) {
    const sel = `usr_id, login, firstname, lastname, gender, email, institution, street, city, zipcode, country, department, active`
    // TODO check args for SQL Injection
 
    const pool = await poolP
    const [results, fields] = await pool.query(
        `SELECT ${sel}
         FROM ${database}.usr_data AS ud
         WHERE login = '${login}'`
    )
    return joinUDF(results[0])
}
 
async function getUserByUserId(userId) {
    const sel = `usr_id, login, firstname, lastname, gender, email, institution, street, city, zipcode, country, department, active`
    // TODO check args for SQL Injection
 
    const pool = await poolP
    const [results, fields] = await pool.query(
        `SELECT ${sel}
         FROM ${database}.usr_data AS ud
         WHERE usr_id = '${userId}'`
    )
    return joinUDF(results[0])
}
 
async function getUserDefinedFields() {
    const pool = await poolP
    const [results] = await pool.query(
        `SELECT ut.usr_id, ud.field_name, ut.value
         FROM ${database}.udf_definition ud
                  JOIN ${database}.udf_text ut ON ut.field_id = ud.field_id`
    )
    return results
}
 
async function getUserDefinedField(usr_id) {
    const pool = await poolP
    const [results] = await pool.query(
        `SELECT ut.usr_id, ud.field_name, ut.value
         FROM ${database}.udf_definition ud
                  JOIN ${database}.udf_text ut ON ut.field_id = ud.field_id
         WHERE ut.usr_id = '${usr_id}'
        `
    )
    return results
}
 
/////// obj_id / ref_id ////////////////////////////////////////////////////////////////
 
/**
 * @param refId
 * @returns {Promise<{ref_id,obj_id}|undefined>}
 */
async function getObjIdFromRefId(refId) {
    const pool = await poolP
    const [results] = await pool.query(
        `SELECT ref_id, obj_id
         FROM ${database}.object_reference as obr
         WHERE obr.ref_id = ${refId}
        `
    )
    return results.length ? results[0] : undefined
}
 
async function getRefIdFromObjId(objId) {
    const pool = await poolP
    const [results] = await pool.query(
        `SELECT ref_id, obj_id
         FROM ${database}.object_reference as obr
         WHERE obr.obj_id = ${objId}
        `
    )
    return results.length ? results[0] : undefined
}
 
/////////////////////////////////////////////////////////////////////////
 
async function promiseDelay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}
 
async function joinUDF(user) {
    if (!user) return user
    const fields = await getUserDefinedField(user.usr_id)
    for (const field of fields) {
        user[field.field_name] = field.value
    }
    return user
}
 
/////// Kurs ////////////////////////////////////////////////////////////////
 
async function getKurse() {
    const pool = await poolP
    const q = `SELECT or2.ref_id, or2.obj_id, od.title, od.description, od.type, od.offline
               FROM ${database}.object_reference or2
                        INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
               WHERE od.type = 'crs'
                 AND or2.deleted IS NULL
    `
    const [results] = await pool.query(q)
    return results
}
 
// async function getKursByRefId (refId) {
//    const {ref_id, obj_id} = await getObjIdFromRefId(refId)
//    return getKursByObjId(obj_id)
// }
 
async function getKurs(ref_id) {
    const pool = await poolP
    const q = `SELECT or2.ref_id, or2.obj_id, od.title, od.description, od.type, od.create_date, od.offline
               FROM ${database}.object_reference or2
                        INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
               WHERE or2.ref_id = '${ref_id}'
                 AND or2.deleted IS NULL
    `
    let [results] = await pool.query(q)
    results = results.length ? results[0] : undefined
    return results
}
 
async function getKursItems(ref_id) {
    const pool = await poolP
    // const q = `SELECT or2.ref_id, or2.obj_id, od.title, od.description, od.type
    //            FROM ${database}.object_reference or2
    //            INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
    //            WHERE or2.obj_id = '${obj_id}'
    // `
    const q = `SELECT ci.parent_id, or2.ref_id, or2.obj_id, od.title, od.type
               FROM ${database}.crs_items ci
                        INNER JOIN ${database}.object_reference or2 ON or2.ref_id = ci.obj_id
                        INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
               WHERE or2.ref_id = ${ref_id}
                  OR parent_id = ${ref_id}
                   AND or2.deleted is NULL
    `
    const [results] = await pool.query(q)
    return results
}
 
async function getKursItems2(ref_id) {
    const pool = await poolP
    const q = `
        WITH RECURSIVE tree (parent_id, obj_id, ref_id, title, type) AS
                           (SELECT ci.parent_id,
                                   or2.obj_id,
                                   ci.obj_id as ref_id,
                                   od.title,
                                   od.type
                            FROM ${database}.crs_items ci
                                     INNER JOIN ${database}.object_reference or2 ON or2.ref_id = ci.obj_id
                                     INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
                            WHERE ci.obj_id = ${ref_id} -- Kurs ref_id
                              AND or2.deleted is NULL
 
                            UNION ALL
 
                            SELECT child.parent_id,
                                   or2.obj_id,
                                   child.obj_id as ref_id,
                                   od.title,
                                   od.type
                            FROM ${database}.crs_items child
                                     INNER JOIN ${database}.object_reference or2 ON or2.ref_id = child.obj_id
                                     INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
                                     JOIN tree ON child.parent_id = tree.ref_id
                            WHERE or2.deleted is NULL)
        SELECT *
        FROM tree
        ORDER BY tree.ref_id
    `
    const [results] = await pool.query(q)
    return results
}
 
async function getKursTeilnehmer(ref_id) {
    const pool = await poolP
    const q = `SELECT ci.parent_id,
                      or2.ref_id,
                      or2.obj_id,
                      od.title,
                      od.type,
                      om.usr_id,
                      ud.login,
                      ud.firstname,
                      ud.lastname,
                      ud.active,
                      om.passed,
                      ulm.status,
                      ulm.status_changed
               FROM ${database}.crs_items ci
                        INNER JOIN ${database}.object_reference or2 ON or2.ref_id = ci.obj_id
                        INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
                        INNER JOIN ${database}.obj_members om ON om.obj_id = or2.obj_id AND om.member = 1
                        INNER JOIN ${database}.usr_data ud ON ud.usr_id = om.usr_id
                        LEFT JOIN ${database}.ut_lp_marks ulm ON ulm.obj_id = or2.obj_id AND ud.usr_id = ulm.usr_id
               WHERE (or2.ref_id = ${ref_id} OR parent_id = ${ref_id})
                 AND or2.deleted IS NULL
               ORDER BY usr_id
    `
    // log.info(q)
    const [results] = await pool.query(q)
    return results
}
 
async function getSingleKursTeilnehmer(ref_id, usr_id) {
    const pool = await poolP
    const q = `SELECT ci.parent_id,
                      or2.ref_id,
                      or2.obj_id,
                      od.title,
                      od.type,
                      om.usr_id,
                      ud.login,
                      ud.firstname,
                      ud.lastname,
                      ud.active,
                      om.passed,
                      ulm.status,
                      ulm.status_changed
               FROM ${database}.crs_items ci
                        INNER JOIN ${database}.object_reference or2 ON or2.ref_id = ci.obj_id
                        INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
                        INNER JOIN ${database}.obj_members om ON om.obj_id = or2.obj_id AND om.member = 1
                        INNER JOIN ${database}.usr_data ud ON ud.usr_id = om.usr_id
                        LEFT JOIN ${database}.ut_lp_marks ulm ON ulm.obj_id = or2.obj_id AND ud.usr_id = ulm.usr_id
               WHERE (or2.ref_id = ${ref_id}
                   OR parent_id = ${ref_id})
                 AND om.usr_id = ${usr_id}
               ORDER BY usr_id
    `
    // log.info(q)
    const [results] = await pool.query(q)
    return results[0]
}
 
async function getKursTeilnehmerCount(ref_id) {
    const pool = await poolP
    const q = `SELECT COUNT(*) as count
               FROM ${database}.crs_items ci
                   INNER JOIN ${database}.object_reference or2
               ON or2.ref_id = ci.obj_id
                   INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
                   INNER JOIN ${database}.obj_members om ON om.obj_id = or2.obj_id AND om.member = 1
                   INNER JOIN ${database}.usr_data ud ON ud.usr_id = om.usr_id
                   LEFT JOIN ${database}.ut_lp_marks ulm ON ulm.obj_id = or2.obj_id AND ud.usr_id = ulm.usr_id
               WHERE (or2.ref_id = ${ref_id}
                  OR parent_id = ${ref_id})
               ORDER BY usr_id
    `
    let [results] = await pool.query(q)
    results = results.length ? results[0] : undefined
    return {ref_id, count: results.count}
}
 
async function getUserTeilnahmen(usr_id) {
    const pool = await poolP
    const q = `SELECT om.obj_id, or2.ref_id, om.usr_id, od.title, ulm.status, om.passed, ulm.status_changed
               FROM ${database}.obj_members om
                        INNER JOIN ${database}.object_reference or2 ON or2.obj_id = om.obj_id
                        INNER JOIN ${database}.usr_data ud ON ud.usr_id = om.usr_id
                        INNER JOIN ${database}.object_data od ON od.obj_id = om.obj_id
                        LEFT JOIN ${database}.ut_lp_marks ulm
                                  ON ulm.usr_id = om.usr_id AND ulm.obj_id = om.obj_id
               WHERE om.usr_id = ${usr_id}
                 AND om.member = 1
    `
    // log.info(q).catch(console.error)
    const [results] = await pool.query(q)
    return results
}
 
async function getKursOffline(obj_id) {
    const pool = await poolP
    const q = `SELECT offline 
                FROM ${database}.object_data
               WHERE obj_id = ${obj_id}
    `
    const [results] = await pool.query(q)
    return results[0]
}
 
async function setKursOffline(isOffline, obj_id) {
    const pool = await poolP
    const q = `UPDATE ${database}.object_data
               SET offline = ${isOffline}
               WHERE obj_id = ${obj_id}
    `
    const [results] = await pool.query(q)
    // return results
    return {offline: isOffline}
}
 
/////// UDF ////////////////////////////////////////////////////////////////
 
async function getUdf() {
    const pool = await poolP
    const q = `SELECT field_id, field_name, field_type
               from ${database}.udf_definition;`
    const [results] = await pool.query(q)
    return results
}
 
/////// ROLLEN ////////////////////////////////////////////////////////////////
 
async function getKursTeilnehmerRolle(obj_id) {
    const pool = await poolP
    const q = `SELECT obj_id, title, description
               from object_data od
               WHERE type = "role" #AND title LIKE 'il_crs_member_157'
                AND od.description LIKE 'Member%${obj_id}'
    `
    const [results] = await pool.query(q)
    // darf nur einen Treffer ergeben
    if (results.length) {
        const {obj_id, title, description} = results[0]
        return obj_id
    } else {
        return null
    }
}
 
async function getKursTeilnehmerByRole(obj_id) {
    const pool = await poolP
    const q = `
        SELECT obj_id as role_id, ru.usr_id, ud.firstname, ud.lastname
        FROM ${database}.object_data od
                 INNER JOIN ${database}.rbac_ua ru ON ru.rol_id = od.obj_id
                 INNER JOIN ${database}.usr_data ud ON ud.usr_id = ru.usr_id
        WHERE type = "role" #AND title LIKE 'il_crs_member_157'
                AND od.description LIKE 'Member%${obj_id}'
    `
    const [results] = await pool.query(q)
    return results
}
 
async function getKursRoles(ref_id) {
    const pool = await poolP
    const q = `
        SELECT pa.rol_id, or2.ref_id, or2.obj_id, od2.type, od2.title, od2.description
        FROM ${database}.rbac_pa pa
                 INNER JOIN ${database}.object_reference or2 ON or2.ref_id = pa.ref_id
                 INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
                 INNER JOIN ${database}.object_data od2 ON od2.obj_id = pa.rol_id
        WHERE pa.ref_id = ${ref_id}
    `
    const [results] = await pool.query(q)
    return results
}
 
 
/////// STATUS ////////////////////////////////////////////////////////////////
 
/**
 * Zwei Möglichkeiten:
 * 1. es gibt schon einen Eintrag in ut_lp_marks, dann muss man diesen updaten
 * 2. es gibt noch keinen Eintrag in ut_lp_marks, dann muss erst einer erstellt werden (NEIN! Für neuen TN sind bereits beide Einträge vorhanden, kann also wegfallen)
 *
 * Weiterhin haben wir es mit zwei Tabellen zu tun:
 *    - obj_members (passed)
 *    - ut_lp_marks (status)
 *
 * UPDATE: Wir nehmen an, dass beide Einträge schon vorhanden sind!
 *
 * @param courseId
 * @param userId
 * @param {Number} passed
 * @param {Number} status
 * @returns {Promise<{status: string}>}
 */
async function setStatus(courseId, userId, passed = null, status = null) {
    const pool = await poolP
    log.info(`------------setStatus----- courseId=${courseId}  userId=${userId}  passed=${passed}  status=${status}`)
    passed = isNaN(Number(passed)) ? passed : Number(passed)
    status = isNaN(Number(status)) ? status : Number(status)
 
    // ACHTUNG Transactions funktioneren so nicht, erst mal ohne machen...
    const q = `
START TRANSACTION;
 
UPDATE ${database}.ut_lp_marks ulm  
SET status = ${status}
WHERE ulm.usr_id = 31793 AND ulm.obj_id = 32212;
 
UPDATE ${database}.obj_members om 
SET passed = ${passed}
WHERE om.usr_id = ${userId} AND om.obj_id = ${courseId};
 
COMMIT;
`
 
    let date = dayjs().format('YYYY-MM-DD HH:mm:ss')
    const q1 = `
        UPDATE ${database}.ut_lp_marks ulm
        SET status         = ${status},
            status_changed = "${date}"
        WHERE ulm.usr_id = ${userId}
          AND ulm.obj_id = ${courseId};
    `
    const q2 = `
        UPDATE ${database}.obj_members om
        SET passed = ${passed}
        WHERE om.usr_id = ${userId}
          AND om.obj_id = ${courseId};
    `
 
    const [results1] = await pool.query(q1)
    const [results2] = await pool.query(q2)
    const {affectedRows: affectedRows1} = results1
    const {affectedRows: affectedRows2} = results2
 
    if (affectedRows1 && affectedRows2) {
        return {status: "ok"}
    } else {
        throw {
            status: "error",
            reason: {affectedRows1, affectedRows2}
        }
    }
}