N°5874 - Quick create: improvement for newcomer

This commit is contained in:
XavierGR
2024-11-21 15:11:55 +01:00
parent 0d8a20c35e
commit dc2064450b
22 changed files with 175 additions and 10 deletions

View File

@@ -1436,6 +1436,14 @@ class Config
'quick_create.max_history_results' => [
'type' => 'integer',
'description' => 'Max. number of elements in the history',
'default' => 5,
'value' => 5,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'quick_create.max_popular_results' => [
'type' => 'integer',
'description' => 'Max. number of elements in the popular',
'default' => 10,
'value' => 10,
'source_of_value' => '',

View File

@@ -15,5 +15,6 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Open quick create~~',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'You haven\'t create any object yet~~',
'UI:Component:QuickCreate:Recents:Title' => 'Recents~~',
'UI:Component:QuickCreate:MostPopular:Title' => 'Populární',
'UI:Component:QuickCreate:Tooltip' => 'Quickly create any type of object~~',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Select object type...~~',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Open quick create~~',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'You haven\'t create any object yet~~',
'UI:Component:QuickCreate:MostPopular:Title' => 'Most popular~~',
'UI:Component:QuickCreate:Recents:Title' => 'Recents~~',
'UI:Component:QuickCreate:Tooltip' => 'Quickly create any type of object~~',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Wählen Sie einen Objekttyp aus...',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Quick-Create-Menu öffnen',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'Sie haben noch keine Objekte erstellt',
'UI:Component:QuickCreate:MostPopular:Title' => 'Am beliebtesten',
'UI:Component:QuickCreate:Recents:Title' => 'Letzte',
'UI:Component:QuickCreate:Tooltip' => 'Erstellen Sie schnell jeden Typ von Objekt',
]);

View File

@@ -23,6 +23,7 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Component:QuickCreate:Input:Placeholder' => 'Select object type...',
'UI:Component:QuickCreate:Recents:Title' => 'Recents',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'You haven\'t create any object yet',
'UI:Component:QuickCreate:MostPopular:Title' => 'Most popular',
'UI:Component:QuickCreate:HistoryDisabled' => 'History is disabled',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Open quick create',
));

View File

@@ -10,6 +10,7 @@ Dict::Add('EN GB', 'British English', 'British English', array(
'UI:Component:QuickCreate:Input:Placeholder' => 'Select object type...',
'UI:Component:QuickCreate:Recents:Title' => 'Recents',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'You haven\'t create any object yet',
'UI:Component:QuickCreate:MostPopular:Title' => 'Most popular',
'UI:Component:QuickCreate:HistoryDisabled' => 'History is disabled',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Open quick create',
));

View File

@@ -12,6 +12,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Seleccionar tipo de objeto...',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Abrir creación rápida',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'Aún no has creado ningún objeto.',
'UI:Component:QuickCreate:MostPopular:Title' => 'Más popular',
'UI:Component:QuickCreate:Recents:Title' => 'Recientes',
'UI:Component:QuickCreate:Tooltip' => 'Crear rápidamente cualquier tipo de objeto',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('FR FR', 'French', 'Français', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Type d\'objet...',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Ouvrir la création rapide',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'Vous n\'avez pas encore créé d\'objet',
'UI:Component:QuickCreate:MostPopular:Title' => 'Populaires',
'UI:Component:QuickCreate:Recents:Title' => 'Types récents',
'UI:Component:QuickCreate:Tooltip' => 'Création rapide de n\'importe quel objet',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Objektumtípus kiválasztása...',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Gyors létrehozás megnyitása',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'Még nem hozott létre objektumot',
'UI:Component:QuickCreate:MostPopular:Title' => 'Legnépszerűbb',
'UI:Component:QuickCreate:Recents:Title' => 'Legutóbbiak',
'UI:Component:QuickCreate:Tooltip' => 'Bármilyen típusú objektum gyors létrehozása',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Seleziona il tipo di oggetto...',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Apri la creazione rapida',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'Non hai ancora creato alcun oggetto',
'UI:Component:QuickCreate:MostPopular:Title' => 'Più popolari',
'UI:Component:QuickCreate:Recents:Title' => 'Recenti',
'UI:Component:QuickCreate:Tooltip' => 'Crea rapidamente qualsiasi tipo di oggetto',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Select object type...~~',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Open quick create~~',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'You haven\'t create any object yet~~',
'UI:Component:QuickCreate:MostPopular:Title' => 'Most popular~~',
'UI:Component:QuickCreate:Recents:Title' => 'Recents~~',
'UI:Component:QuickCreate:Tooltip' => 'Quickly create any type of object~~',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Selecteer een objectsoort…',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Open snel object aanmaken',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'Je hebt nog geen objecten gemaakt',
'UI:Component:QuickCreate:MostPopular:Title' => 'Meest populair',
'UI:Component:QuickCreate:Recents:Title' => 'Recente',
'UI:Component:QuickCreate:Tooltip' => 'Snel een object aanmaken',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Wybierz typ obiektu...',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Otwórz szybkie tworzenie',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'Nie utworzyłeś jeszcze żadnego obiektu',
'UI:Component:QuickCreate:MostPopular:Title' => 'Najbardziej popularne',
'UI:Component:QuickCreate:Recents:Title' => 'Ostatnie',
'UI:Component:QuickCreate:Tooltip' => 'Szybko utwórz dowolny rodzaj obiektu',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Selecione o tipo de objeto...',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Abrir criação rápida',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'Você ainda não criou nenhum objeto',
'UI:Component:QuickCreate:MostPopular:Title' => 'Mais popular',
'UI:Component:QuickCreate:Recents:Title' => 'Recentes',
'UI:Component:QuickCreate:Tooltip' => 'Crie rapidamente qualquer tipo de objeto',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Выбрать тип объекта...',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Открыть быстрое создание объекта',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'Вы ещё не создавали объекты',
'UI:Component:QuickCreate:MostPopular:Title' => 'Популярные',
'UI:Component:QuickCreate:Recents:Title' => 'Недавние',
'UI:Component:QuickCreate:Tooltip' => 'Быстрое создание объекта любого типа',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Select object type...~~',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Open quick create~~',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'You haven\'t create any object yet~~',
'UI:Component:QuickCreate:MostPopular:Title' => 'Most popular~~',
'UI:Component:QuickCreate:Recents:Title' => 'Recents~~',
'UI:Component:QuickCreate:Tooltip' => 'Quickly create any type of object~~',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'UI:Component:QuickCreate:Input:Placeholder' => 'Select object type...~~',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => 'Open quick create~~',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => 'You haven\'t create any object yet~~',
'UI:Component:QuickCreate:MostPopular:Title' => 'Most popular~~',
'UI:Component:QuickCreate:Recents:Title' => 'Recents~~',
'UI:Component:QuickCreate:Tooltip' => 'Quickly create any type of object~~',
]);

View File

@@ -14,6 +14,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
'UI:Component:QuickCreate:Input:Placeholder' => '请选择对象类型...',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => '打开快速创建',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => '您尚未创建任何对象',
'UI:Component:QuickCreate:MostPopular:Title' => '最常用',
'UI:Component:QuickCreate:Recents:Title' => '最近',
'UI:Component:QuickCreate:Tooltip' => '快速创建任意类型的对象',
]);

View File

@@ -59,10 +59,14 @@ class QuickCreate extends UIBlock implements iKeyboardShortcut
protected $aLastClasses;
/** @var int $iMaxAutocompleteResults Max. number of elements returned by the autocomplete */
protected $iMaxAutocompleteResults;
/** @var bool $bShowHistory Whether or not to display the elements in the history */
/** @var bool $bShowHistory Whether to display the elements in the history */
protected $bShowHistory;
/** @var int $iMaxHistoryResults Max. number of elements in the history */
protected $iMaxHistoryResults;
/** @var int $iMaxPopularResults Max. number of elements in popular */
protected $iMaxPopularResults;
/** @var array $aPopularClasses */
protected $aPopularClasses;
/**
* QuickCreate constructor.
@@ -82,6 +86,8 @@ class QuickCreate extends UIBlock implements iKeyboardShortcut
$this->iMaxAutocompleteResults = (int) MetaModel::GetConfig()->Get('quick_create.max_autocomplete_results');
$this->bShowHistory = (bool) MetaModel::GetConfig()->Get('quick_create.show_history');
$this->iMaxHistoryResults = (int) MetaModel::GetConfig()->Get('quick_create.max_history_results');
$this->iMaxPopularResults = (int) MetaModel::GetConfig()->Get('quick_create.max_popular_results');
$this->aPopularClasses = QuickCreateHelper::GetPopularClasses();
}
/**
@@ -89,7 +95,7 @@ class QuickCreate extends UIBlock implements iKeyboardShortcut
* If $bRelativeUrl is true, then $sEndpoint will be complete with the app_root_url
*
* @param string $sEndpoint URL to the endpoint
* @param bool $bRelativeUrl Whether or not the $sEndpoint parameter is a relative URL
* @param bool $bRelativeUrl Whether the $sEndpoint parameter is a relative URL
*
* @return $this
* @throws \Exception
@@ -203,6 +209,24 @@ class QuickCreate extends UIBlock implements iKeyboardShortcut
return $this->iMaxHistoryResults;
}
/**
* @see $aMaxPopularClasses
* @return array
*/
public function GetPopularClasses(): array
{
return $this->aPopularClasses;
}
/**
* @see $iMaxPopularResults
* @return int
*/
public function GetMaxPopularResults(): int
{
return $this->iMaxPopularResults;
}
public static function GetShortcutKeys(): array
{
return [['id' => 'ibo-open-quick-create', 'label' => 'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer', 'key' => 'c', 'event' => 'open_drawer']];

View File

@@ -24,6 +24,7 @@ use appUserPreferences;
use DBObject;
use MetaModel;
use utils;
use UserRights;
/**
* Class QuickCreateHelper
@@ -71,7 +72,7 @@ class QuickCreateHelper
array_unshift($aHistoryEntries, $aNewEntry);
// Truncate history
static::TruncateHistory($aHistoryEntries);
static::Truncate($aHistoryEntries, 'quick_create.max_history_results');
appUserPreferences::SetPref(static::USER_PREF_CODE, $aHistoryEntries);
}
@@ -88,7 +89,8 @@ class QuickCreateHelper
{
/** @var array $aHistoryEntries */
$aHistoryEntries = appUserPreferences::GetPref(static::USER_PREF_CODE, []);
static::TruncateHistory($aHistoryEntries);
static::Truncate($aHistoryEntries, 'quick_create.max_history_results');
for($iIdx = 0; $iIdx < count($aHistoryEntries); $iIdx++)
{
@@ -125,16 +127,70 @@ class QuickCreateHelper
}
/**
* Truncate $aHistoryEntries to 'global_search.max_history_results' entries
* Return an array of popular object classes
*
* @param array $aHistoryEntries
* @return array
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
protected static function TruncateHistory(array &$aHistoryEntries): void
public static function GetPopularClasses(): array
{
$iMaxHistoryResults = (int) MetaModel::GetConfig()->Get('quick_create.max_history_results');
if(count($aHistoryEntries) > $iMaxHistoryResults)
$aHistoryEntries = appUserPreferences::GetPref(static::USER_PREF_CODE, []);
static::Truncate($aHistoryEntries, 'quick_create.max_history_results');
$aPopularClassesNames = UserRights::GetAllowedClasses(UR_ACTION_CREATE, array('popular'), false);
// Prevent classes in both Popular and Recent to appear twice
for($iIdx = 0; $iIdx < count($aHistoryEntries); $iIdx++)
{
$aHistoryEntries = array_slice($aHistoryEntries, 0, $iMaxHistoryResults);
if (($key = array_search($aHistoryEntries[$iIdx]['class'], $aPopularClassesNames)) !== false) {
unset($aPopularClassesNames[$key]);
}
}
//die(var_dump($aPopularClassesNames));
static::Truncate($aPopularClassesNames, 'quick_create.max_popular_results');
$aPopularClasses = array();
foreach($aPopularClassesNames as $sClass)
{
if (!MetaModel::IsValidClass($sClass)) {
continue;
}
// Add class icon
$sClassIconUrl = MetaModel::GetClassIcon($sClass, false);
// Mind that some classes don't have an icon
$sClassIconUrl = !empty($sClassIconUrl) ? $sClassIconUrl : null;
// Add class label
$sLabelHtml = utils::EscapeHtml(MetaModel::GetName($sClass));
// Add URL
$sTargetUrl = DBObject::ComputeStandardUIPage($sClass).'?operation=new&class='.$sClass;
$aPopularClasses[] = array(
'class' => $sClass,
'icon_url' => $sClassIconUrl,
'label_html' => $sLabelHtml,
'target_url' => $sTargetUrl
);
}
return $aPopularClasses;
}
/**
* Truncate $aHistoryEntries to 'global_search.max_history_results' entries OR $aPopularClasses to 'global_search.max_popular_results'
*
* @param array $aEntries
* @param string $sMaxEntriesParam
*/
protected static function Truncate(array &$aEntries, string $sMaxEntriesParam = 'global_search.max_history_results'): void
{
$iMaxResults = (int) MetaModel::GetConfig()->Get($sMaxEntriesParam);
if(count($aEntries) > $iMaxResults)
{
$aEntries = array_slice($aEntries, 0, $iMaxResults);
}
}
}

View File

@@ -48,6 +48,22 @@
</div>
{% endif %}
</div>
<div class="ibo-quick-create--compartment-title" data-role="ibo-quick-create--compartment-title">
<span>{{ 'UI:Component:QuickCreate:MostPopular:Title'|dict_s }}</span>
</div>
<div class="ibo-quick-create--compartment-content" data-role="ibo-quick-create--compartment-content">
{% if oUIBlock.GetPopularClasses()|length > 0 %}
{% for aClass in oUIBlock.GetPopularClasses() %}
<a href="{{ aClass.target_url }}" class="ibo-quick-create--compartment-element" data-role="ibo-quick-create--compartment-element" data-class-code="{{ aQuery.class }}">
{% if aClass.icon_url is defined and aClass.icon_url is not null%}
{# Mind the empty "alt" attribute https://www.w3.org/WAI/tutorials/images/decorative/ #}
<img src="{{ aClass.icon_url}}" alt="" class="ibo-quick-create--compartment-element-image">
{% endif %}
{{ aClass.label_html|raw }}
</a>
{% endfor %}
{% endif %}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,44 @@
<?php
namespace Application\UI\Base\Components\QuickCreate;
use Combodo\iTop\Application\UI\Base\Component\QuickCreate\QuickCreateHelper;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
class QuickCreateHelperTest extends ItopDataTestCase
{
/**
* Test class removal from popular when it is in recent classes
*/
public function testNoDuplicateInPopularAndLast()
{
$sPopularClass = 'Server';
$aClasses = ['ApplicationSolution', 'BusinessProcess', 'DatabaseSchema', 'MiddlewareInstance', 'Enclosure'];
$aPopularClassesInitial = QuickCreateHelper::GetPopularClasses();
QuickCreateHelper::AddClassToHistory($sPopularClass);
$aPopularClassNoParam = QuickCreateHelper::GetPopularClasses();
for($iIdx = 0; $iIdx < count($aPopularClassNoParam); $iIdx++)
{
$this->assertNotEquals($aPopularClassNoParam[$iIdx]['class'], $sPopularClass);
}
return [$aClasses, $aPopularClassesInitial];
}
/**
* Test class addition in popular after being removed from recent classes
* @depends testNoDuplicateInPopularAndLast
*/
public function testPopularClassBackAfterRecent(array $aNoDuplicateResult){
[$aClasses, $aPopularClassesInitial] = $aNoDuplicateResult;
foreach($aClasses as $sClass)
{
QuickCreateHelper::AddClassToHistory($sClass);
}
$aPopularClassesFinal = QuickCreateHelper::GetPopularClasses();
$this->assertEquals($aPopularClassesInitial, $aPopularClassesFinal);
}
}