Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../PluginsMakefile.mk
164 changes: 164 additions & 0 deletions inc/questiontype.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php

/**
* -------------------------------------------------------------------------
* Tag plugin for GLPI
* -------------------------------------------------------------------------
*
* LICENSE
*
* This file is part of Tag.
*
* Tag is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tag is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tag. If not, see <http://www.gnu.org/licenses/>.
* -------------------------------------------------------------------------
* @copyright Copyright (C) 2014-2023 by Teclib'.
* @license GPLv2 https://www.gnu.org/licenses/gpl-2.0.html
* @link https://github.com/pluginsGLPI/tag
* -------------------------------------------------------------------------
*/

use Glpi\Application\View\TemplateRenderer;
use Glpi\Form\Form;
use Glpi\Form\Migration\FormQuestionDataConverterInterface;
use Glpi\Form\Question;
use Glpi\Form\QuestionType\AbstractQuestionType;
use Glpi\Form\QuestionType\QuestionTypeCategoryInterface;

class PluginTagQuestionType extends AbstractQuestionType implements FormQuestionDataConverterInterface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class PluginTagQuestionType extends AbstractQuestionType implements FormQuestionDataConverterInterface
final class PluginTagQuestionType extends AbstractQuestionType implements FormQuestionDataConverterInterface

{
#[Override]
public function getCategory(): QuestionTypeCategoryInterface
{
return new PluginTagQuestionTypeCategory();
}

#[Override]
public function isAllowedForUnauthenticatedAccess(): bool
{
return true;
}

#[Override]
public function formatDefaultValueForDB(mixed $value): ?string
{
if (!is_array($value)) {
return null;
}

return implode(',', $value);
}

#[Override]
public function formatRawAnswer(mixed $answer, Question $question): string
{
if (!is_array($answer)) {
throw new LogicException('Answer must be an array');
}

if (count(array_filter($answer, 'is_numeric')) !== count($answer)) {
throw new LogicException('Answer must be an array of numeric IDs');
}

$tags = PluginTagTag::getByIds($answer);
$tag_names = array_map(fn($tag) => $tag->fields['name'], $tags);

return implode(',', $tag_names);
}

#[Override]
public function renderAdministrationTemplate(?Question $question): string
{
[$available_tags, $available_tags_color] = $this->getAvailableTags();

$twig = TemplateRenderer::getInstance();
return $twig->render('@tag/question_dropdown.html.twig', [
'input_name' => 'default_value',
'selected_tags' => empty($question?->fields['default_value']) ? [] : explode(',', $question->fields['default_value']),
'available_tags' => $available_tags,
'tags_color' => $available_tags_color,
'dropdown_params' => [
'no_label' => true,
'init' => $question !== null,
],
]);
}

#[Override]
public function renderEndUserTemplate(Question $question): string
{
[$available_tags, $available_tags_color] = $this->getAvailableTags($question->getForm());

$twig = TemplateRenderer::getInstance();
return $twig->render('@tag/question_dropdown.html.twig', [
'input_name' => $question->getEndUserInputName(),
'selected_tags' => empty($question?->fields['default_value']) ? [] : explode(',', $question->fields['default_value']),
'available_tags' => $available_tags,
'tags_color' => $available_tags_color,
'show_search_tooltip' => false,
'dropdown_params' => [
'no_label' => true,
'init' => true,
],
]);
}

#[Override]
public function beforeConversion(array $rawData): void {}

#[Override]
public function convertDefaultValue(array $rawData): null
{
return null;
}

#[Override]
public function convertExtraData(array $rawData): null
{
return null;
}

#[Override]
public function getTargetQuestionType(array $rawData): string
{
return self::class;
}

private function getAvailableTags(?Form $form = null): array
{
$active_entities_ids = Session::getActiveEntities();
if ($active_entities_ids === [] && $form) {
$active_entities_ids = [$form->getEntityID()];
}

$tag = new PluginTagTag();
$available_tags = [];
$available_tags_color = [];
$result = $tag->find([
'is_active' => 1,
'OR' => [
['type_menu' => ['LIKE', '%\"Ticket\"%']],
['type_menu' => ['LIKE', '%\"Change\"%']],
['type_menu' => ['LIKE', '%\"Problem\"%']],
['type_menu' => '0'],
['type_menu' => ''],
['type_menu' => 'NULL'],
],
] + getEntitiesRestrictCriteria('', '', $active_entities_ids, true), 'name');
foreach ($result as $id => $data) {
$available_tags[$id] = $data['name'];
$available_tags_color[$id] = $data['color'] ?: '#DDDDDD';
}
return [$available_tags, $available_tags_color];
}
}
38 changes: 14 additions & 24 deletions front/tag.form.php → inc/questiontypecategory.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,32 +28,22 @@
* -------------------------------------------------------------------------
*/

use Glpi\Exception\Http\AccessDeniedHttpException;
use Glpi\Form\QuestionType\QuestionTypeCategoryInterface;

Session::checkRight(PluginTagTag::$rightname, UPDATE);

if (!Plugin::isPluginActive("tag")) {
throw new AccessDeniedHttpException();
}

if (isset($_POST['add'])) {
$item = new PluginTagTagItem();
class PluginTagQuestionTypeCategory implements QuestionTypeCategoryInterface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class PluginTagQuestionTypeCategory implements QuestionTypeCategoryInterface
final class PluginTagQuestionTypeCategory implements QuestionTypeCategoryInterface

{
public function getLabel(): string
{
return __('Tag', 'tag');
}

// Check unicity :
if (isset($_REQUEST['plugin_tag_tags_id'])) {
$found = $item->find([
'plugin_tag_tags_id' => $_REQUEST['plugin_tag_tags_id'],
'items_id' => $_REQUEST['items_id'],
'itemtype' => $_REQUEST['itemtype'],
]);
public function getIcon(): string
{
return 'ti ti-tag';
}

if (count($found) == 0) {
$item->add($_REQUEST);
}
} else {
$item->add($_REQUEST);
public function getWeight(): int
{
return 1000;
}
}

$dropdown = new PluginTagTag();
include(GLPI_ROOT . "/front/dropdown.common.form.php");
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ parameters:
level: 5
paths:
- ajax
- front
- inc
- stubs
- tests
- hook.php
- setup.php
scanDirectories:
Expand Down
1 change: 0 additions & 1 deletion psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
>
<projectFiles>
<directory name="ajax" />
<directory name="front" />
<directory name="inc" />
<directory name="stubs" />
<directory name="tests" />
Expand Down
96 changes: 96 additions & 0 deletions public/js/modules/TagDropdownColorizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* -------------------------------------------------------------------------
* Tag plugin for GLPI
* -------------------------------------------------------------------------
*
* LICENSE
*
* This file is part of Tag.
*
* Tag is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tag is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tag. If not, see <http://www.gnu.org/licenses/>.
* -------------------------------------------------------------------------
* @copyright Copyright (C) 2014-2023 by Teclib'.
* @license GPLv2 https://www.gnu.org/licenses/gpl-2.0.html
* @link https://github.com/pluginsGLPI/tag
* -------------------------------------------------------------------------
*/

export class GlpiPluginTagTagDropdownColorizer {
constructor(tagsColor, selector, $container) {
this.tagsColor = tagsColor;
this.selector = selector;
this.$container = $container;

this.init();
}

isDark(hexColor) {
if (!hexColor) return false;
hexColor = hexColor.replace('#', '');
const r = parseInt(hexColor.substr(0, 2), 16);
const g = parseInt(hexColor.substr(2, 2), 16);
const b = parseInt(hexColor.substr(4, 2), 16);
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
return luminance < 0.5;
}

applyTagColors($select) {
const selectedIds = $select.find('option:selected').map(function() {
return $(this).val();
}).get();

const $container = $select.nextAll('.select2').find('.select2-selection__rendered');
$container.find('.select2-selection__choice').each((index, element) => {
const id = selectedIds[index];
const color = this.tagsColor[id];
if (color) {
$(element).css('background-color', color);
$(element).css('color', this.isDark(color) ? '#eeeeee' : '');

// Also style the remove button for better visibility
$(element).find('.select2-selection__choice__remove').css('color', this.isDark(color) ? '#eeeeee' : '');
}
});
}

init() {
const $select = this.$container.find(this.selector);

$select.each((index, element) => {
this.applyTagColors($(element));
});

$select.on('change select2:select select2:unselect', (event) => {
this.applyTagColors($(event.target));
});

$select.on('select2:open', () => {
setTimeout(() => {
$('.select2-results__option').each((index, element) => {
const matches = element.id.match(/result-[^-]+-(\d+)$/);
if (matches && matches[1]) {
const color = this.tagsColor[matches[1]];
// Cible uniquement le span SANS la classe select2-rendered__match
$(element).find('span:not(.select2-rendered__match)').css({
'background-color': color ? color : '',
'padding': color ? '2px' : '',
'color': (color && this.isDark(color)) ? '#fff' : '',
'border-radius': '2px'
});
}
});
}, 0);
});
}
}
1 change: 0 additions & 1 deletion rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
return RectorConfig::configure()
->withPaths([
__DIR__ . '/ajax',
__DIR__ . '/front',
__DIR__ . '/inc',
__DIR__ . '/stubs',
__DIR__ . '/tests',
Expand Down
24 changes: 24 additions & 0 deletions setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@
* @link https://github.com/pluginsGLPI/tag
* -------------------------------------------------------------------------
*/

use Glpi\Application\ImportMapGenerator;
use Glpi\Form\Form;
use Glpi\Form\Migration\TypesConversionMapper;
use Glpi\Form\QuestionType\QuestionTypesManager;
use Glpi\Plugin\Hooks;

define('PLUGIN_TAG_VERSION', '2.13.0');
Expand Down Expand Up @@ -150,10 +154,15 @@ function plugin_init_tag()
$PLUGIN_HOOKS[Hooks::ADD_JAVASCRIPT]['tag'][] = 'js/entity.js';
}

ImportMapGenerator::getInstance()->registerModulesPath('tag', '/public/js/modules');

Plugin::registerClass('PluginTagProfile', ['addtabon' => ['Profile']]);
Plugin::registerClass('PluginTagConfig', ['addtabon' => 'Config']);

$PLUGIN_HOOKS['use_rules']['tag'] = ['RuleTicket'];

// Register tag question type
registerPluginTypes();
}
}

Expand Down Expand Up @@ -203,3 +212,18 @@ function plugin_tag_geturl(): string
global $CFG_GLPI;
return sprintf('%s/plugins/tag', $CFG_GLPI['url_base']);
}

function registerPluginTypes(): void
{
$types = QuestionTypesManager::getInstance();
$type_mapper = TypesConversionMapper::getInstance();

// Register question type category
$types->registerPluginCategory(new PluginTagQuestionTypeCategory());

// Register question type
$types->registerPluginQuestionType(new PluginTagQuestionType());

// Register mapper for legacy question type
$type_mapper->registerPluginQuestionTypeConverter('tag', new PluginTagQuestionType());
}
Loading