9 namespace Fossology\UI\Page;
15 use Symfony\Component\HttpFoundation\JsonResponse;
16 use Symfony\Component\HttpFoundation\Request;
17 use Symfony\Component\HttpFoundation\Response;
21 const NAME =
"admin_custom_text_list";
23 function __construct()
25 parent::__construct(self::NAME, array(
26 self::TITLE =>
"Custom Text List",
27 self::MENU_LIST =>
"Admin::Text Management::List",
28 self::REQUIRES_LOGIN =>
true,
38 protected function handle(Request $request)
45 return $this->flushContent(_(
'Access denied. Admin privileges required.'));
48 $action = $request->get(
'action');
51 if ($action ==
'fetchData') {
55 if ($action ==
'get_bulk_data') {
56 return $this->getBulkDataAjax($request);
59 if ($action ==
'delete' && $request->getMethod() ==
'POST') {
60 return $this->deletePhraseAjax($request);
63 if ($action ==
'toggle' && $request->getMethod() ==
'POST') {
64 return $this->togglePhraseStatusAjax($request);
70 'addModuleName' =>
'admin_custom_text_management'
72 return $this->
render(
'admin_custom_text_list.html.twig', $this->mergeWithDefault($vars));
80 $offset = intval($request->query->get(
'start', 0));
81 $limit = intval($request->query->get(
'length', 10));
82 $draw = intval($request->query->get(
'draw', 1));
83 $searchQuery = $request->query->all(
'search')[
'value'] ??
'';
86 if (!empty($searchQuery)) {
87 $filteredQuery =
'%' . $searchQuery .
'%';
90 $totalCount = $this->getTotalPhrasesCount(
'');
91 $filteredCount = empty($filteredQuery) ? $totalCount : $this->getTotalPhrasesCount($filteredQuery);
92 $phraseArray = $this->getPhrasesServerSide($limit, $offset, $filteredQuery);
94 return new JsonResponse([
96 "recordsTotal" => $totalCount,
97 "recordsFiltered" => $filteredCount,
98 "data" => $phraseArray,
99 ], JsonResponse::HTTP_OK);
105 private function deletePhraseAjax(Request $request)
107 $phraseId = intval($request->get(
'id'));
110 return new JsonResponse(array(
'error' =>
'Invalid phrase ID'), 400);
115 $dbManager = $this->
getObject(
'db.manager');
121 $deleteLicensesSql =
"DELETE FROM custom_phrase_license_map WHERE cp_fk = $1";
122 $dbManager->prepare($deleteLicensesStmt = __METHOD__ .
".delete_licenses", $deleteLicensesSql);
123 $dbManager->freeResult($dbManager->execute($deleteLicensesStmt, array($phraseId)));
126 $sql =
"DELETE FROM custom_phrase WHERE cp_pk = $1";
127 $dbManager->prepare($stmt = __METHOD__ .
".delete", $sql);
128 $dbManager->freeResult($dbManager->execute($stmt, array($phraseId)));
131 $dbManager->commit();
133 return new JsonResponse(array(
135 'message' =>
'Custom text deleted successfully'
137 }
catch (\Exception $e) {
138 $dbManager->rollback();
139 return new JsonResponse(array(
'error' =>
'Failed to delete phrase: ' . $e->getMessage()), 500);
146 private function togglePhraseStatusAjax(Request $request)
148 $phraseId = intval($request->get(
'id'));
149 $status = intval($request->get(
'status'));
152 return new JsonResponse(array(
'error' =>
'Invalid phrase ID'), 400);
157 $dbManager = $this->
getObject(
'db.manager');
159 $sql =
"UPDATE custom_phrase SET is_active = $1 WHERE cp_pk = $2";
160 $dbManager->prepare($stmt = __METHOD__ .
".toggle", $sql);
161 $dbManager->freeResult($dbManager->execute($stmt, array($status ?
'true' :
'false', $phraseId)));
163 return new JsonResponse(array(
165 'message' => $status ?
'Custom text activated' :
'Custom text deactivated'
167 }
catch (\Exception $e) {
168 return new JsonResponse(array(
'error' =>
'Failed to toggle status: ' . $e->getMessage()), 500);
172 private function getTotalPhrasesCount($searchQuery =
'')
175 $dbManager = $this->
getObject(
'db.manager');
177 $sql =
"SELECT COUNT(DISTINCT cp.cp_pk) AS count
178 FROM custom_phrase cp
179 LEFT JOIN users u ON cp.user_fk = u.user_pk
180 LEFT JOIN custom_phrase_license_map cplm ON cp.cp_pk = cplm.cp_fk
181 LEFT JOIN license_ref lr ON cplm.rf_fk = lr.rf_pk";
185 if (!empty($searchQuery)) {
186 $sql .=
" WHERE (cp.text ILIKE $1 OR cp.acknowledgement ILIKE $1 OR cp.comments ILIKE $1"
187 .
" OR u.user_name ILIKE $1 OR lr.rf_shortname ILIKE $1)";
188 $params[] = $searchQuery;
191 $result = $dbManager->getSingleRow($sql, $params, __METHOD__);
192 return $result ? intval($result[
'count']) : 0;
198 private function getPhrasesServerSide($limit, $offset, $searchQuery =
'')
201 $dbManager = $this->
getObject(
'db.manager');
203 $sql =
"SELECT cp.cp_pk, cp.text, cp.text_md5, cp.acknowledgement, cp.comments,
204 cp.user_fk, cp.group_fk, cp.created_date, cp.is_active,
206 STRING_AGG(CASE WHEN cplm.removing = false THEN lr.rf_shortname END, ', ' ORDER BY lr.rf_shortname) as licenses_to_add,
207 STRING_AGG(CASE WHEN cplm.removing = true THEN lr.rf_shortname END, ', ' ORDER BY lr.rf_shortname) as licenses_to_remove
208 FROM custom_phrase cp
209 LEFT JOIN users u ON cp.user_fk = u.user_pk
210 LEFT JOIN custom_phrase_license_map cplm ON cp.cp_pk = cplm.cp_fk
211 LEFT JOIN license_ref lr ON cplm.rf_fk = lr.rf_pk";
215 if (!empty($searchQuery)) {
216 $sql .=
" WHERE (cp.text ILIKE $1 OR cp.acknowledgement ILIKE $1 OR cp.comments ILIKE $1 OR u.user_name ILIKE $1 OR lr.rf_shortname ILIKE $1)";
217 $params[] = $searchQuery;
220 $sql .=
" GROUP BY cp.cp_pk, cp.text, cp.text_md5, cp.acknowledgement, cp.comments,
221 cp.user_fk, cp.group_fk, cp.created_date, cp.is_active, u.user_name
222 ORDER BY cp.created_date DESC";
224 $sql .=
" LIMIT $" . (count($params) + 1) .
" OFFSET $" . (count($params) + 2);
228 $result = $dbManager->getRows($sql, $params, __METHOD__);
231 foreach ($result as $row) {
232 $editLink =
'<a href="?mod=admin_custom_text_management&edit=' . $row[
'cp_pk'] .
'"><img border="0" src="images/button_edit.png"></a>';
234 $text = strlen($row[
'text']) > 100 ?
235 substr($row[
'text'], 0, 100) .
'...' :
238 $acknowledgement = strlen($row[
'acknowledgement'] ?:
'') > 50 ?
239 substr($row[
'acknowledgement'], 0, 50) .
'...' :
240 ($row[
'acknowledgement'] ?:
'N/A');
242 $comments = strlen($row[
'comments'] ?:
'') > 50 ?
243 substr($row[
'comments'], 0, 50) .
'...' :
244 ($row[
'comments'] ?:
'N/A');
246 $isActiveFlag = $dbManager->booleanFromDb($row[
'is_active']);
247 $statusToggle =
'<input type="checkbox" class="phrase-status-toggle"'
248 .
' data-phrase-id="' . intval($row[
'cp_pk']) .
'"'
249 .
' data-current-status="' . ($isActiveFlag ?
'1' :
'0') .
'"'
250 . ($isActiveFlag ?
' checked' :
'') .
'/>';
252 $deleteBtn =
'<button type="button" class="custom-text-delete"'
253 .
' data-phrase-id="' . intval($row[
'cp_pk']) .
'">'
254 .
'<img class="delete" src="images/space_16.png" alt="Delete"/></button>';
257 $licensesDisplay =
'';
258 $licensesToAdd = $row[
'licenses_to_add'] ?:
null;
259 $licensesToRemove = $row[
'licenses_to_remove'] ?:
null;
261 if ($licensesToAdd || $licensesToRemove) {
262 if ($licensesToAdd) {
263 $licensesDisplay .=
'<div><strong>To be added:</strong> ' . htmlentities($licensesToAdd) .
'</div>';
265 if ($licensesToRemove) {
266 $licensesDisplay .=
'<div><strong>To be removed:</strong> ' . htmlentities($licensesToRemove) .
'</div>';
269 $licensesDisplay =
'N/A';
275 '<div style="overflow-y:scroll;max-height:100px;margin:0;">' . nl2br(htmlentities($text)) .
'</div>',
276 htmlentities($acknowledgement),
277 htmlentities($comments),
278 htmlentities($row[
'user_name'] ?:
'N/A'),
279 $row[
'created_date'],
294 private function getTotalBulkCount($searchQuery =
'', $userFk = 0)
297 $dbManager = $this->
getObject(
'db.manager');
299 $sql =
"SELECT COUNT(DISTINCT lrb.lrb_pk) AS count
300 FROM license_ref_bulk lrb
301 INNER JOIN license_set_bulk lsb ON lrb.lrb_pk = lsb.lrb_fk
302 INNER JOIN license_ref lr ON lsb.rf_fk = lr.rf_pk
303 LEFT JOIN users u ON lrb.user_fk = u.user_pk
304 LEFT JOIN upload up ON lrb.upload_fk = up.upload_pk
305 WHERE lrb.rf_text IS NOT NULL AND lrb.rf_text != ''";
310 if (!empty($searchQuery)) {
311 $sql .=
" AND (lrb.rf_text ILIKE \$$paramIdx OR lr.rf_shortname ILIKE \$$paramIdx OR u.user_name ILIKE \$$paramIdx OR up.upload_filename ILIKE \$$paramIdx)";
312 $params[] = $searchQuery;
317 $sql .=
" AND lrb.user_fk = \$$paramIdx";
321 $result = $dbManager->getSingleRow($sql, $params, __METHOD__);
322 return $result ? intval($result[
'count']) : 0;
334 private function getBulkDataAjax(Request $request)
336 $page =
max(1, intval($request->query->get(
'page', 1)));
337 $limit =
max(1, intval($request->query->get(
'limit', 10)));
338 $searchTerm =
trim($request->query->get(
'search',
''));
339 $userFk =
max(0, intval($request->query->get(
'user_fk', 0)));
342 if (!empty($searchTerm)) {
343 $searchQuery =
'%' . $searchTerm .
'%';
347 $totalRecords = $this->getTotalBulkCount($searchQuery, $userFk);
348 $totalPages =
max(1, intval(ceil($totalRecords / $limit)));
351 if ($page > $totalPages) {
354 $offset = ($page - 1) * $limit;
357 $dbManager = $this->
getObject(
'db.manager');
362 $whereClause =
"WHERE lrb.rf_text IS NOT NULL AND lrb.rf_text != ''";
366 if (!empty($searchQuery)) {
367 $whereClause .=
" AND (lrb.rf_text ILIKE \$$paramIdx OR lr.rf_shortname ILIKE \$$paramIdx OR u.user_name ILIKE \$$paramIdx OR up.upload_filename ILIKE \$$paramIdx)";
368 $params[] = $searchQuery;
373 $whereClause .=
" AND lrb.user_fk = \$$paramIdx";
379 $subSql =
"SELECT DISTINCT lrb.lrb_pk
380 FROM license_ref_bulk lrb
381 INNER JOIN license_set_bulk lsb ON lrb.lrb_pk = lsb.lrb_fk
382 INNER JOIN license_ref lr ON lsb.rf_fk = lr.rf_pk
383 LEFT JOIN users u ON lrb.user_fk = u.user_pk
384 LEFT JOIN upload up ON lrb.upload_fk = up.upload_pk
386 ORDER BY lrb.lrb_pk DESC
387 LIMIT \$$paramIdx OFFSET \$" . ($paramIdx + 1);
395 lrb.rf_text as bulk_reference_text,
396 lrb.ignore_irrelevant,
400 lr.rf_pk as license_id,
401 lr.rf_shortname as license_shortname,
402 lr.rf_fullname as license_fullname,
403 lr.rf_spdx_id as license_spdx_id,
405 lsb.removing as is_removing_license,
406 lsb.comment as license_comment,
407 lsb.reportinfo as license_reportinfo,
408 lsb.acknowledgement as license_acknowledgement,
410 u.user_name as created_by_user,
411 up.upload_filename as upload_file,
418 FROM license_ref_bulk lrb
419 INNER JOIN license_set_bulk lsb ON lrb.lrb_pk = lsb.lrb_fk
420 INNER JOIN license_ref lr ON lsb.rf_fk = lr.rf_pk
421 LEFT JOIN users u ON lrb.user_fk = u.user_pk
422 LEFT JOIN upload up ON lrb.upload_fk = up.upload_pk
423 WHERE lrb.lrb_pk IN ($subSql)
424 ORDER BY lrb.lrb_pk DESC, lr.rf_shortname";
426 $result = $dbManager->getRows($sql, $params, __METHOD__);
430 $groupedData = array();
431 foreach ($result as $row) {
432 $lrbPk = $row[
'lrb_pk'];
434 if (!isset($groupedData[$lrbPk])) {
435 $text = strlen($row[
'bulk_reference_text']) > 200 ?
436 substr($row[
'bulk_reference_text'], 0, 200) .
'...' :
437 $row[
'bulk_reference_text'];
439 $ignoreIrrelevant = $dbManager->booleanFromDb($row[
'ignore_irrelevant']);
440 $scanFindings = $dbManager->booleanFromDb($row[
'scan_findings']);
442 $groupedData[$lrbPk] = array(
444 'user_fk' => $row[
'user_fk'],
445 'group_fk' => $row[
'group_fk'],
446 'bulk_reference_text' => $row[
'bulk_reference_text'],
447 'text_preview' => $text,
448 'upload_fk' => $row[
'upload_fk'],
449 'uploadtree_fk' => $row[
'uploadtree_fk'],
450 'ignore_irrelevant' => $ignoreIrrelevant,
451 'bulk_delimiters' => $row[
'bulk_delimiters'],
452 'scan_findings' => $scanFindings,
453 'created_by_user' => $row[
'created_by_user'] ?:
'Unknown',
454 'upload_file' => $row[
'upload_file'] ?:
'Unknown',
455 'licenses' => array(),
456 'all_acknowledgements' => array(),
457 'all_comments' => array()
462 $isRemoving = $dbManager->booleanFromDb($row[
'is_removing_license']);
463 $licenseInfo = array(
464 'license_id' => $row[
'license_id'],
465 'license_shortname' => $row[
'license_shortname'],
466 'license_fullname' => $row[
'license_fullname'],
467 'license_spdx_id' => $row[
'license_spdx_id'],
468 'is_removing_license' => $isRemoving,
469 'license_comment' => $row[
'license_comment'],
470 'license_reportinfo' => $row[
'license_reportinfo'],
471 'license_acknowledgement' => $row[
'license_acknowledgement']
474 $groupedData[$lrbPk][
'licenses'][] = $licenseInfo;
477 if (!empty($row[
'license_acknowledgement'])) {
478 $groupedData[$lrbPk][
'all_acknowledgements'][] = $row[
'license_acknowledgement'];
480 if (!empty($row[
'license_comment'])) {
481 $groupedData[$lrbPk][
'all_comments'][] = $row[
'license_comment'];
486 foreach ($groupedData as $entry) {
487 $licenseNames = array();
488 $addedLicenses = array();
489 $removedLicenses = array();
491 foreach ($entry[
'licenses'] as $license) {
492 $licenseNames[] = $license[
'license_shortname'];
493 if ($license[
'is_removing_license']) {
494 $removedLicenses[] = $license[
'license_shortname'];
496 $addedLicenses[] = $license[
'license_shortname'];
501 $entry[
'license_summary'] = implode(
', ', $licenseNames);
502 $entry[
'added_licenses'] = implode(
', ', $addedLicenses);
503 $entry[
'removed_licenses'] = implode(
', ', $removedLicenses);
504 $entry[
'acknowledgement_summary'] = implode(
'; ', array_unique($entry[
'all_acknowledgements']));
505 $entry[
'comment_summary'] = implode(
'; ', array_unique($entry[
'all_comments']));
507 $bulkData[] = $entry;
510 return new JsonResponse(array(
512 'totalRecords' => $totalRecords,
513 'totalPages' => $totalPages,
514 'currentPage' => $page,
520 register_plugin(
new AdminCustomTextList());
Contains the constants and helpers for authentication of user.
static getUserId()
Get the current user's id.
static getGroupId()
Get the current user's group id.
static isAdmin()
Check if user is admin.
render($templateName, $vars=null, $headers=null)
fetchDataServerSide(Request $request)
Traceback_uri()
Get the URI without query to this location.
char * trim(char *ptext)
Trimming whitespace.
FUNCTION int max(int permGroup, int permPublic)
Get the maximum group privilege.