Minor: Revert unintended changes

pull/5217/head
christianbeeznst 9 months ago
parent f6cb80e25b
commit d92c10b64a
  1. 2
      assets/css/scss/_skill.scss
  2. 3
      assets/css/scss/atoms/_checkbox.scss
  3. 16
      assets/css/scss/atoms/_dropdown.scss
  4. 2
      assets/vue/components/basecomponents/BaseInputText.vue
  5. 16
      assets/vue/components/basecomponents/BaseToolbar.vue
  6. 1
      assets/vue/components/basecomponents/ChamiloIcons.js
  7. 2
      assets/vue/router/index.js
  8. 1
      public/documentation/changelog.html
  9. 648
      public/main/template/default/skill/skill_wheel.html.twig
  10. 59
      src/CoreBundle/Entity/Skill.php
  11. 201
      src/CoreBundle/Repository/SkillRepository.php
  12. 2
      src/CoreBundle/Resources/views/Layout/skill_layout.html.twig

@ -470,5 +470,3 @@
display: inline-block;
}
}

@ -32,6 +32,3 @@
}
}
.p-checkbox .p-checkbox-box {
@apply invisible;
}

@ -1,4 +1,4 @@
.p-dropdown, .p-multiselect {
.p-dropdown {
@apply border border-support-3 bg-white rounded-lg transition w-full duration-200
hover:border-primary hover:text-gray-90 hover:outline-0 hover:outline-none;
@ -29,7 +29,7 @@
}
}
.p-dropdown-panel, .multi-select-panel {
.p-dropdown-panel {
@apply bg-white rounded-lg text-gray-90 shadow-lg;
.p-dropdown-header {
@ -89,15 +89,3 @@
}
}
.multi-select-panel {
@apply border border-gray-300 shadow-md rounded-md p-2;
}
.multi-select-panel .p-checkbox {
@apply mr-1;
}
.p-multiselect-label {
@apply py-2;
}

@ -7,7 +7,6 @@
:class="{ 'p-invalid': isInvalid }"
:aria-label="label"
type="text"
:required="required"
@update:model-value="$emit('update:modelValue', $event)"
/>
<label
@ -55,7 +54,6 @@ const props = defineProps({
required: false,
default: false,
},
required: Boolean,
})
defineEmits(["update:modelValue"])

@ -1,10 +1,7 @@
<template>
<Toolbar :class="toolbarClass">
<template v-slot:start>
<slot name="start"></slot>
</template>
<template v-slot:end>
<slot name="end"></slot>
<template #start>
<slot></slot>
</template>
</Toolbar>
</template>
@ -24,13 +21,6 @@ const toolbarClass = computed(() => {
if (props.showTopBorder) {
return "pt-5 border-t border-b";
}
return "p-toolbar";
return "";
});
</script>
<style scoped>
.p-toolbar {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
</style>

@ -118,5 +118,4 @@ export const chamiloIconToClass = {
"map-search": "mdi mdi-map-search-outline",
"join-group": "mdi mdi-account-multiple-plus",
"add-topic": "mdi mdi-forum-outline",
'badge-account': "mdi mdi-badge-account-horizontal",
};

@ -12,7 +12,6 @@ import toolIntroRoutes from "./ctoolintro"
import pageRoutes from "./page"
import socialNetworkRoutes from "./social"
import termsRoutes from "./terms"
import skillsRoutes from "./skill"
//import courseCategoryRoutes from './coursecategory';
import documents from "./documents"
@ -141,7 +140,6 @@ const router = createRouter({
component: MySessionListUpcoming,
meta: { requiresAuth: true },
},
skillsRoutes,
termsRoutes,
socialNetworkRoutes,
catalogueCourses,

@ -90,7 +90,6 @@
<li>Blogs: The blogs tool is not available. It might be included again in future versions, but there were too many changes required to include it in v2.0.</li>
<li>Dropbox: The dropbox tool is not available. It might be included again in future versions, but there were too many changes required to include it in v2.0.</li>
<li>Global: Sub-folder mode: Chamilo 2 will not support being installed in a sub-folder. Nowadays, defining a subdomain has become very easy and provides higher security and more configuration flexibility, and supporting sub-folders is a complex task.</li>
<li>WYSIWYG: New online editor with different implementation as Chamilo 1. Will take time to re-integrate all toolbar options into v2, so advanced features (math formula, easy integration of YT video, etc) temporarily disabled.</li>
</ul>
<h3>Known issues</h3>

@ -0,0 +1,648 @@
{% import '@ChamiloCore/Macros/box.html.twig' as display %}
{% autoescape false %}
<script>
/* Skill search input in the left menu */
function check_skills_sidebar() {
//Selecting only selected skills
$("#skill_id option").each(function () {
var skill_id = $(this).val();
if (skill_id != "") {
$.ajax({
url: "{{ url }}&a=skill_exists",
data: "skill_id=" + skill_id,
async: false,
success: function (return_value) {
if (return_value == 0) {
alert("{{ 'There is no such skill'|trans }}");
//Deleting select option tag
//$("#skill_id option[value="+skill_id+"]").remove();
$("#skill_id").empty();
//Deleting holder
$("#skill_search .holder li").each(function () {
if ($(this).attr("rel") == skill_id) {
$(this).remove();
}
});
} else {
$("#skill_id option[value=" + skill_id + "]").remove();
//Deleting holder
$("#skill_search .holder li").each(function () {
if ($(this).attr("rel") == skill_id) {
$(this).remove();
}
});
if ($('#skill_to_select_id_' + skill_id).length == 0) {
skill_info = get_skill_info(skill_id);
li = fill_skill_search_li(skill_id, skill_info.title);
$("#skill_holder").append(li);
}
}
}
});
}
});
}
function fill_skill_search_li(skill_id, skill_name, checked) {
var checked_condition = '',
activeCondition = '',
iconClassCondition = 'fa fa-square-o fa-fw';
if (checked) {
checked_condition = 'checked=checked';
activeCondition = 'active';
iconClassCondition = 'fa fa-check-square-o fa-fw';
}
return '\
<tr>\n\
<td>' + skill_name + '</td>\n\
<td class="text-right">\n\
<div class="btn-group btn-group-sm" data-toggle="buttons">\n\
<label class="btn btn--plain ' + activeCondition + '" aria-label="{{ 'Select'|trans }}" title="{{ 'Select to search'|trans }}">\n\
<input id="skill_to_select_id_' + skill_id + '" data-id="' + skill_id + '" name="' + skill_name + '" class="skill_to_select" type="checkbox" autocomplete="off" ' + checked_condition + '>\n\
<span class="' + iconClassCondition + '" aria-hidden="true"></span>\n\
</label>\n\
<button class="btn btn--plain load_wheel" aria-label="{{ 'Search'|trans }}" title="{{ 'Place on the wheel'|trans }}" data-id="' + skill_id + '">\n\
<span class="fa fa-crosshairs fa-fw" aria-hidden="true"></span>\n\
</button>\n\
</div>\n\
</td>\n\
</tr>';
}
function check_skills_edit_form() {
//selecting only selected parents
$("#parent_id option:selected").each(function () {
var skill_id = $(this).val();
if (skill_id != "") {
$.ajax({
async: false,
url: "{{ url }}&a=skill_exists",
data: "skill_id=" + skill_id,
success: function (return_value) {
if (return_value == 0) {
alert("{{ 'SkillDoesNotExist'|trans }}");
//Deleting select option tag
$("#parent_id").find('option').remove();
//Deleting holder
$("#skill_row .holder li").each(function () {
if ($(this).attr("rel") == skill_id) {
$(this).remove();
}
});
} else {
$("#parent_id").empty();
$("#skill_edit_holder").find('li').remove();
//Deleting holder
$("#skill_row .holder li").each(function () {
if ($(this).attr("rel") == skill_id) {
$(this).remove();
}
});
skill = get_skill_info(skill_id);
$("#skill_edit_holder").append('<li class="bit-box" id="skill_option_' + skill_id + '"> ' + skill.title + '</li>');
$("#parent_id").append('<option class="selected" selected="selected" value="' + skill_id + '"></option>');
}
}
});
}
});
}
function check_gradebook() {
//selecting only selected users
$("#gradebook_id option:selected").each(function () {
var gradebook_id = $(this).val();
if (gradebook_id != "") {
$.ajax({
url: "{{ url }}&a=gradebook_exists",
data: "gradebook_id=" + gradebook_id,
success: function (return_value) {
if (return_value == 0) {
alert("{{ 'GradebookDoesNotExist'|trans }}");
//Deleting select option tag
$("#gradebook_id option[value=" + gradebook_id + "]").remove();
//Deleting holder
$("#gradebook_row .holder li").each(function () {
if ($(this).attr("rel") == gradebook_id) {
$(this).remove();
}
});
} else {
//Deleting holder
$("#gradebook_row .holder li").each(function () {
if ($(this).attr("rel") == gradebook_id) {
$(this).remove();
}
});
if ($('#gradebook_item_' + gradebook_id).length == 0) {
gradebook = get_gradebook_info(gradebook_id);
if (gradebook) {
$("#gradebook_holder").append('<li id="gradebook_item_' + gradebook_id + '" class="bit-box"> ' + gradebook.title +
' <a rel="' + gradebook_id + '" class="closebutton" href="#"></a></li>');
}
}
}
}
});
}
});
}
function delete_gradebook_from_skill(skill_id, gradebook_id) {
$.ajax({
url: url + '&a=delete_gradebook_from_skill&skill_id=' + skill_id + '&gradebook_id=' + gradebook_id,
async: false,
success: function (result) {
//if (result == 1) {
$('#gradebook_item_' + gradebook_id).remove();
$("#gradebook_id option").each(function () {
if ($(this).attr("value") == gradebook_id) {
$(this).remove();
}
});
//}
}
});
}
function return_skill_list_from_profile_search() {
var skill_list = {};
if ($("#profile_search li").length != 0) {
$("#profile_search li").each(function (index) {
id = $(this).attr("id").split('_')[3];
if (id) {
skill_list[index] = id;
}
});
}
return skill_list;
}
function submit_profile_search_form() {
$("#skill_wheel").remove();
var skill_list = return_skill_list_from_profile_search();
if (skill_list.length != 0) {
skill_list = {'skill_id': skill_list};
skill_params = $.param(skill_list);
$.ajax({
url: url + '&a=profile_matches&' + skill_params,
async: false,
success: function (html) {
//users = jQuery.parseJSON(users);
$('#wheel_container').html(html);
}
});
}
//return skill;
}
function add_skill_in_profile_list(skill_id, skill_name) {
if ($('#profile_match_item_' + skill_id).length == 0) {
$('#profile_search').append('<li class="bit-box" id="profile_match_item_' + skill_id + '">' + skill_name +
' <a rel="' + skill_id + '" class="closebutton" href="#"></a> </li>');
} else {
$('#profile_match_item_' + skill_id).remove();
}
toogle_save_profile_form();
}
function toogle_save_profile_form() {
//Hiding showing the save this search
if ($('#profile_search li').length == 0) {
$('#profile-options-container').hide();
} else {
$('#profile-options-container').show();
}
}
$(function() {
/* Skill search */
// Tool tip (in exercises)
var tip_options = {
placement: 'right'
}
$('.tooltip_skill').tooltip(tip_options);
/* Skill item list onclick */
$("#skill_holder").on("change", "input.skill_to_select", function () {
var self = $(this);
skill_id = self.data('id') || 0;
skill_name = self.attr('name');
add_skill_in_profile_list(skill_id, skill_name);
if (this.checked) {
self.next('.fa').replaceWith('<span class="fa fa-check-square-o fa-fw" aria-hidden="true"></span>');
} else {
self.next('.fa').replaceWith('<span class="fa fa-square-o fa-fw" aria-hidden="true"></span>');
}
});
/* URL link when searching skills */
$("#skill_holder").on("click", "button.load_wheel", function (e) {
e.preventDefault();
skill_id = $(this).data('id') || 0;
skill_to_load_from_get = 0;
load_nodes(skill_id, main_depth);
});
/* URL link when searching skills */
$("a.load_root").on("click", function (e) {
e.preventDefault();
skill_id = $(this).attr('rel');
skill_to_load_from_get = 0;
load_nodes(skill_id, main_depth);
});
/* Profile matcher */
/* Submit button */
$("#search_profile_form").submit(function () {
submit_profile_search_form();
return false;
});
$("form#save_profile_form_button").on('submit', function (e) {
e.preventDefault();
var profileId = parseInt($('input[name="profile_id"]').val());
var getProfileInfo = $.getJSON(
'{{ url }}',
{
a: 'get_profile',
profile_id: profileId
}
);
$.when(getProfileInfo).done(function (profileInfo) {
$("#name_profile").val(profileInfo.title);
$("#description_profile").val(profileInfo.description);
$('#frm-save-profile').modal('show');
});
});
/* Close button in profile matcher items */
$("#profile_search").on("click", "a.closebutton", function () {
skill_id = $(this).attr('rel');
$('input[id=skill_to_select_id_' + skill_id + ']').attr('checked', false);
$('#profile_match_item_' + skill_id).remove();
submit_profile_search_form();
toogle_save_profile_form();
});
// Fill saved profiles list
update_my_saved_profiles();
/* Click in profile */
$("#saved_profiles").on("click", "button.skill-wheel-load-profile", function (e) {
e.preventDefault();
profile_id = $(this).data('id') || 0;
$('input[name="profile_id"]').val(profile_id);
$.ajax({
url: '{{ url }}&a=get_skills_by_profile&profile_id=' + profile_id,
success: function (json) {
$('#profile_search').empty();
$('#skill_holder').empty();
skill_list = $.parseJSON(json);
$.each(skill_list, function (index, skill_id) {
skill_info = get_skill_info(skill_id);
li = fill_skill_search_li(skill_id, skill_info.title, 1);
$("#skill_holder").append(li);
add_skill_in_profile_list(skill_id, skill_info.title);
});
submit_profile_search_form();
}
});
});
$("#saved_profiles").on('click', 'button.skill-wheel-delete-profile', function (e) {
e.preventDefault();
var self = $(this),
profileId = self.data('id') || 0;
$.getJSON('{{ url }}&a=delete_profile', {
profile: profileId
}, function (response) {
if (response.status) {
update_my_saved_profiles();
}
});
});
/* Wheel skill popup form */
/* Close button in gradebook select */
$("#gradebook_holder").on("click", "a.closebutton", function () {
gradebook_id = $(this).attr('rel');
skill_id = $('input[name="id"]').attr('value');
delete_gradebook_from_skill(skill_id, gradebook_id);
});
$("#skill_id").select2({
ajax: {
url: '{{ url }}&a=find_skills',
processResults: function (data) {
return {
results: data.items
};
}
},
cache: false,
placeholder: '{{ 'Enter the skill name to search'|trans }}'
}).on('change', function () {
check_skills_sidebar();
});
load_nodes(0, main_depth);
function update_my_saved_profiles() {
$.ajax({
url: '{{ url }}&a=get_saved_profiles',
success: function (data) {
$("#saved_profiles").html(data);
}
});
}
/* change background color */
$('#skill-change-background-options li a').on('click', function (e) {
e.preventDefault();
var newBackgroundColor = $(this).data('color') || '#FFF';
$("#page-back").css("background", newBackgroundColor);
});
/* Generated random colour */
/*
function colour(d) {
if (d.children) {
// There is a maximum of two children!
var colours = d.children.map(colour),
a = d3.hsl(colours[0]),
b = d3.hsl(colours[1]);
// L*a*b* might be better here...
return d3.hsl((a.h + b.h) / 2, a.s * 1.2, a.levels_to_show / 1.2);
}
return d.colour || "#fff";
}*/
$('#form-button-edit').on('click', function (e) {
e.preventDefault();
if (SkillWheel.currentSkill === null) {
return;
}
window.location.href = "{{ url('legacy_main', {'name': 'skills/skill_edit.php?id=' }) }}" + SkillWheel.currentSkill.id;
});
$('#form-button-create-child').on('click', function (e) {
e.preventDefault();
if (SkillWheel.currentSkill === null) {
return;
}
window.location.href = "{{ url('legacy_main', {'name': 'skills/skill_create.php?parent=' }) }}" + SkillWheel.currentSkill.id;
});
$('#form-button-add-to-profile').on('click', function (e) {
e.preventDefault();
if (SkillWheel.currentSkill === null) {
return;
}
add_skill_in_profile_list(SkillWheel.currentSkill.id, SkillWheel.currentSkill.title);
});
$('#frm-save-profile').on('hidden.bs.modal', function () {
$("#name_profile").val('');
$("#description_profile").val('');
$('input[name="profile_id"]').val(0);
});
$('#form-button-save-profile').on('click', function (e) {
e.preventDefault();
var saveProfile = $.ajax(
'{{ url }}',
{
data: {
a: 'save_profile',
name: $("#name_profile").val(),
description: $("#description_profile").val(),
skill_id: return_skill_list_from_profile_search(),
profile: $('input[name="profile_id"]').val()
}
}
);
$.when(saveProfile).done(function (response) {
if (parseInt(response) === 1) {
update_my_saved_profiles();
alert("{{ "Saved" | trans }}");
} else {
alert("{{ "Error" | trans }}");
}
$('#frm-save-profile').modal('hide');
});
});
$(".facebook-auto").css("width","100%");
$(".facebook-auto ul").css("width","100%");
$("ul.holder").css("width","100%");
});
</script>
<div id="page-back" class="page-skill">
<div class="container-fluid">
<div class="row">
<div class="col-md-3 skill-options">
<div class="skill-home">
<a class="btn btn-large btn-block btn--primary" href="{{ url('courses') }}">
<em class="fa fa-home"></em> {{ "Return to the course list"|trans }}
</a>
</div>
<div class="card">
<div class="card-body">
<div id="saved_profiles"></div>
<h4 class="page-header">{{ 'What skills are you looking for?'|trans }}</h4>
<div class="search-skill">
<form id="skill_search" class="form-search">
<select id="skill_id" name="skill_id" style="width: 100%;" multiple></select>
<table id="skill_holder" class="table table-condensed"></table>
</form>
</div>
<div class="search-profile-skill">
<ul id="profile_search" class="holder holder_simple hide"></ul>
<form id="search_profile_form" class="form-search">
<button class="btn btn--plain btn-block" type="submit">
<em class="fa fa-search"></em> {{ "Search profile matches"|trans }}
</button>
</form>
<h4 class="page-header">{{ 'Is this what you were looking for?'|trans }}</h4>
<form id="save_profile_form_button" class="form-search">
<button class="btn btn--plain btn-block" type="submit">
<em class="fa fa-floppy-o"></em> {{ "Save this search"|trans }}
</button>
</form>
</div>
<hr>
<p>
<a class="btn btn-block btn--primary load_root" rel="0" href="#">
<em class="fa fa-eye"></em> {{ "View skills wheel"|trans }}
</a>
</p>
</div>
</div>
<div class="accordion" id="wheel-second-accordion" role="tablist" aria-multiselectable="true">
<div class="card">
<div class="card-header" role="tab" id="wheel-legend-heading">
<h4 class="card-title">
<a role="button" data-toggle="collapse" data-parent="#wheel-second-accordion"
href="#wheel-legend-collapse" aria-expanded="true" aria-controls="wheel-legend-collapse">
{{ "Legend"|trans }}
</a>
</h4>
</div>
<div id="wheel-legend-collapse" class="collapse in" role="tabpanel" aria-labelledby="wheel-legend-heading">
<div class="card-body">
<ul class="fa-ul">
<li>
<em class="fa fa-li fa-square skill-legend-basic"></em> {{ "Basic skills"|trans }}
</li>
<li>
<em class="fa fa-li fa-square skill-legend-add"></em> {{ "Skills you can learn"|trans }}
</li>
<li>
<em class="fa fa-li fa-square skill-legend-search"></em> {{ "Skills searched for"|trans }}
</li>
</ul>
</div>
</div>
</div>
<div class="card">
<div class="card-header" role="tab" id="wheel-display-heading">
<h4 class="card-title">
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#wheel-second-accordion"
href="#wheel-display-collapse" aria-expanded="false" aria-controls="wheel-display-collapse">
{{ 'Display options' | trans }}
</a>
</h4>
</div>
<div id="wheel-display-collapse" class="collapse" role="tabpanel" aria-labelledby="wheel-display-heading">
<div class="card-body">
<p>{{ 'Choose a background color' | trans }}</p>
<ul class="list-unstyled" id="skill-change-background-options">
<li><a href="#" data-color="#FFFFFF">{{ 'White' | trans }}</a></li>
<li><a href="#" data-color="#000000">{{ 'Black' | trans }}</a></li>
<li><a href="#" data-color="#A9E2F3">{{ 'Light blue' }}</a></li>
<li><a href="#" data-color="#848484">{{ 'Gray' | trans }}</a></li>
<li><a href="#" data-color="#F7F8E0">{{ 'Corn' | trans }}</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div id="wheel_container" class="col-md-9">
<div id="skill_wheel">
<img src="">
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="frm-skill" tabindex="-1" role="dialog" aria-labelledby="form-skill-title" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="{{ "Close" | trans }}">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title" id="form-skill-title">{{ "Skill" | trans }}</h4>
</div>
<div class="modal-body">
{{ dialogForm }}
</div>
<div class="modal-footer">
<button id="form-button-edit" class="btn btn--primary">
<em class="fa fa-edit"></em> {{ "Edit" | trans }}
</button>
<button id="form-button-create-child" class="btn btn--primary">
<em class="fa fa-plus"></em> {{ "Create child skill" | trans }}
</button>
<button id="form-button-add-to-profile" class="btn btn--primary">
<em class="fa fa-check"></em> {{ "Add skill to profile search" | trans }}
</button>
<button type="button" class="btn btn--primary" data-dismiss="modal">
<em class="fa fa-close"></em> {{ "Close" | trans }}
</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="frm-save-profile" tabindex="-1" role="dialog" aria-labelledby="form-save-profile-title" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="{{ "Close" | trans }}">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title" id="form-save-profile-title">
{{ "Skill profile" | trans }}
</h4>
</div>
<div class="modal-body">
{{ save_profile_form }}
</div>
<div class="modal-footer">
<button id="form-button-save-profile" class="btn btn--primary">
<em class="fa fa-save"></em> {{ "Save" | trans }}
</button>
<button type="button" class="btn btn--primary" data-dismiss="modal">
<em class="fa fa-close"></em> {{ "Close" | trans }}
</button>
</div>
</div>
</div>
</div>
{% endautoescape %}

@ -9,15 +9,8 @@ namespace Chamilo\CoreBundle\Entity;
use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use Chamilo\CoreBundle\DataProvider\SkillDataProvider;
use Chamilo\CoreBundle\Dto\SkillInputDto;
use Chamilo\CoreBundle\Repository\SkillRepository;
use Chamilo\CoreBundle\State\SkillPostProcessor;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
@ -27,55 +20,7 @@ use Stringable;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
#[ApiResource(
operations: [
new GetCollection(
uriTemplate: '/skills/options',
normalizationContext: ['groups' => ['skill:read']],
security: "is_granted('ROLE_USER')",
name: 'get_skill_options',
provider: SkillDataProvider::class
),
new GetCollection(
uriTemplate: '/skills/gradebook/options',
normalizationContext: ['groups' => ['skill:read']],
security: "is_granted('ROLE_USER')",
name: 'get_skill_gradebook_options',
provider: SkillDataProvider::class
),
new GetCollection(
uriTemplate: '/skills/all',
normalizationContext: ['groups' => ['skill:read']],
security: "is_granted('ROLE_USER')",
name: 'get_all_skills',
provider: SkillDataProvider::class
),
new GetCollection(
uriTemplate: '/skills/user/{user_id}',
normalizationContext: ['groups' => ['skill:read']],
security: "is_granted('ROLE_USER')",
name: 'get_skills_by_user',
provider: SkillDataProvider::class
),
new Post(
securityPostDenormalize: "is_granted('ROLE_USER')",
input: SkillInputDto::class,
processor: SkillPostProcessor::class
),
new Get(
security: "is_granted('ROLE_USER')"
),
new Delete(security: "is_granted('DELETE', object)"),
new Put(security: "is_granted('EDIT', object)"),
],
normalizationContext: [
'groups' => ['skill:read'],
],
denormalizationContext: [
'groups' => ['skill:write'],
],
security: "is_granted('ROLE_USER')",
)]
#[ApiResource(security: 'is_granted(\'ROLE_ADMIN\')', normalizationContext: ['groups' => ['skill:read']])]
#[ApiFilter(SearchFilter::class, properties: ['issuedSkills.user' => 'exact'])]
#[ORM\Table(name: 'skill')]
#[ORM\Entity(repositoryClass: SkillRepository::class)]
@ -134,7 +79,6 @@ class Skill implements Stringable
#[ORM\Column(name: 'description', type: 'text', nullable: false)]
protected string $description;
#[Assert\NotNull]
#[Groups(['skill:read', 'skill:write'])]
#[ORM\Column(name: 'access_url_id', type: 'integer', nullable: false)]
protected int $accessUrlId;
#[Groups(['skill:read', 'skill_rel_user:read'])]
@ -143,7 +87,6 @@ class Skill implements Stringable
#[ORM\ManyToOne(targetEntity: Asset::class, cascade: ['persist', 'remove'])]
#[ORM\JoinColumn(name: 'asset_id', referencedColumnName: 'id')]
protected ?Asset $asset = null;
#[Groups(['skill:read', 'skill:write'])]
#[ORM\Column(name: 'criteria', type: 'text', nullable: true)]
protected ?string $criteria = null;
#[ORM\Column(name: 'status', type: 'integer', nullable: false, options: ['default' => 1])]

@ -6,14 +6,9 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\Repository;
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
use Chamilo\CoreBundle\Entity\Asset;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\GradebookCategory;
use Chamilo\CoreBundle\Entity\Session;
use Chamilo\CoreBundle\Entity\Skill;
use Chamilo\CoreBundle\Entity\SkillRelGradebook;
use Chamilo\CoreBundle\Entity\SkillRelSkill;
use Chamilo\CoreBundle\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Collections\Criteria;
@ -25,12 +20,9 @@ use Doctrine\Persistence\ManagerRegistry;
*/
class SkillRepository extends ServiceEntityRepository
{
private $assetRepository;
public function __construct(ManagerRegistry $registry, AssetRepository $assetRepository)
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Skill::class);
$this->assetRepository = $assetRepository;
}
public function deleteAsset(Skill $skill): void
@ -95,195 +87,4 @@ class SkillRepository extends ServiceEntityRepository
return $qb->getQuery()->getOneOrNullResult();
}
public function getAllSkills($loadUserData = false, $userId = null, $id = null, $parentId = null): array
{
$qb = $this->createQueryBuilder('s');
// Filtrar por ID específico si se proporciona
if (null !== $id) {
$qb->andWhere('s.id = :id')
->setParameter('id', $id);
}
// Filtrar por habilidades relacionadas con un usuario específico si se proporciona
if ($loadUserData && null !== $userId) {
$qb->innerJoin('s.issuedSkills', 'isu')
->andWhere('isu.user = :userId')
->setParameter('userId', $userId);
}
// Filtrar por padre si se proporciona
if (null !== $parentId) {
$qb->innerJoin('s.skills', 'ss', 'WITH', 'ss.parent = :parentId')
->setParameter('parentId', $parentId);
}
// Ordenar por algún criterio si es necesario
$qb->orderBy('s.id', 'ASC');
$query = $qb->getQuery();
$skills = $query->getResult();
// Convertir cada Skill en un array de detalles
$skillsWithDetails = array_map(function ($skill) {
return $this->getSkillDetails($skill); // Asume que getSkillDetails está definido y funciona como se espera
}, $skills);
return $skillsWithDetails;
}
public function getSkillDetails(Skill $skill): array
{
$assetUrl = '/img/icons/64/badges-default.png';
if ($skill->getAsset()) {
$assetUrl = $this->assetRepository->getAssetUrl($skill->getAsset());
}
$skillDetails = [
'id' => $skill->getId(),
'title' => $this->translateName($skill->getTitle()),
'shortCode' => $this->translateCode($skill->getShortCode()),
'description' => $skill->getDescription(),
'icon' => $assetUrl,
];
$skillDetails['parents'] = $this->getSkillParents($skill);
$skillDetails['gradebooks'] = $this->getGradebooksBySkill($skill);
return $skillDetails;
}
public function getParentOptions(): array
{
$qb = $this->createQueryBuilder('s');
$qb->select('s.id', 's.title')
->where('s.status = :status')
->setParameter('status', Skill::STATUS_ENABLED);
return $qb->getQuery()->getArrayResult();
}
public function getGradebookOptions(): array
{
$gradebookRepo = $this->getEntityManager()->getRepository(GradebookCategory::class);
$qb = $gradebookRepo->createQueryBuilder('g');
$qb->select('g.id', 'g.title');
return $qb->getQuery()->getArrayResult();
}
public function addSkill(array $params): ?Skill
{
$em = $this->getEntityManager();
$skill = new Skill();
$skill->setTitle($params['title'])
->setShortCode($params['short_code'] ?? '')
->setDescription($params['description'] ?? '')
->setCriteria($params['criteria'] ?? '')
->setAccessUrlId($params['access_url_id'])
->setIcon($params['icon'] ?? '');
$em->persist($skill);
// Relate with parent skills
if (!empty($params['parent_id'])) {
foreach ($params['parent_id'] as $parentId) {
$parentSkill = $this->find($parentId);
if ($parentSkill) {
$skillRelSkill = new SkillRelSkill();
$skillRelSkill->setSkill($skill)
->setParent($parentSkill)
->setLevel($params['level'] ?? 0)
->setRelationType($params['relation_type'] ?? 0);
$em->persist($skillRelSkill);
}
}
}
// Relate with Gradebooks
if (!empty($params['gradebook_id'])) {
foreach ($params['gradebook_id'] as $gradebookId) {
$gradebook = $em->getRepository(GradebookCategory::class)->find($gradebookId);
if ($gradebook) {
$skillRelGradebook = new SkillRelGradebook();
$skillRelGradebook->setGradeBookCategory($gradebook)
->setSkill($skill);
$em->persist($skillRelGradebook);
}
}
}
$em->flush();
return $skill;
}
/**
* Translates a skill name into the current user's language, if a translation is available.
*/
private function translateName(string $name): string
{
$variable = ChamiloApi::getLanguageVar($name, 'Skill');
return $GLOBALS[$variable] ?? $name;
}
/**
* Translates a skill code into the current user's language, if a translation is available.
*
* This is useful for displaying skill codes in a user-friendly manner, especially when they have specific meanings or abbreviations that may not be immediately clear to all users.
*/
private function translateCode(string $code): string
{
if (empty($code)) {
return '';
}
$variable = ChamiloApi::getLanguageVar($code, 'SkillCode');
return $GLOBALS[$variable] ?? $code;
}
/**
* Retrieves the parent skills of a specific skill.
*/
private function getSkillParents(Skill $skill): array
{
$parents = [];
$currentSkill = $skill;
// While the current skill has a parent, keep looking up the hierarchy
while ($currentSkillRelSkill = $this->getEntityManager()->getRepository(SkillRelSkill::class)->findOneBy(['skill' => $currentSkill])) {
$parentSkill = $currentSkillRelSkill->getParent();
if ($parentSkill) {
$parents[] = $parentSkill;
$currentSkill = $parentSkill; // Move to the next level in the hierarchy
} else {
break; // No more parents
}
}
return $parents;
}
/**
* Fetches gradebook categories associated with a given skill.
*/
private function getGradebooksBySkill(Skill $skill): array
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('g')
->from(GradebookCategory::class, 'g')
->innerJoin(SkillRelGradebook::class, 'sg', 'WITH', 'g.id = sg.gradeBookCategory')
->where('sg.skill = :skill')
->setParameter('skill', $skill);
return $qb->getQuery()->getResult();
}
}

@ -4,7 +4,7 @@
{# topbar #}
{# {% include "@ChamiloCore/Layout/topbar.html.twig" %}#}
{% include 'default/skill/skill_wheel.js.html.twig' %}
{% include 'default/skill/skill_wheel2.html.twig' %}
{% include 'default/skill/skill_wheel.html.twig' %}
{% autoescape false %}
<section id="content-scorm">
{{ content }}

Loading…
Cancel
Save