commit
						45bd9ae5ee
					
				@ -0,0 +1,58 @@ | 
				
			||||
<template> | 
				
			||||
  <div class="border border-gray-300 p-1 rounded-md flex flex-wrap items-center"> | 
				
			||||
    <div class="flex flex-wrap items-center p-1 flex-grow min-h-[38px] outline-none border-none"> | 
				
			||||
      <div v-for="(tag, index) in tags" :key="index" class="bg-blue-500 text-white mr-1 mb-1 px-2.5 py-1 rounded-full flex items-center text-sm"> | 
				
			||||
        {{ tag }} | 
				
			||||
        <span class="ml-2 cursor-pointer font-bold" @click.stop="removeTag(index)">×</span> | 
				
			||||
      </div> | 
				
			||||
      <input | 
				
			||||
        ref="tagInput" | 
				
			||||
        v-model="newTag" | 
				
			||||
        @keyup="checkInputKey" | 
				
			||||
        @keydown.delete="deleteLastTag" | 
				
			||||
        placeholder="Add a tag" | 
				
			||||
        class="flex-grow outline-none border-none p-0 m-0 text-sm" | 
				
			||||
      /> | 
				
			||||
    </div> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script setup> | 
				
			||||
import { ref, onMounted } from 'vue' | 
				
			||||
 | 
				
			||||
const tags = ref([]) | 
				
			||||
const newTag = ref('') | 
				
			||||
const tagInput = ref(null) | 
				
			||||
 | 
				
			||||
function focusInput() { | 
				
			||||
  tagInput.value.focus() | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function addTag() { | 
				
			||||
  if (newTag.value.trim() && !tags.value.includes(newTag.value.trim())) { | 
				
			||||
    tags.value.push(newTag.value.trim()) | 
				
			||||
    newTag.value = '' | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function removeTag(index) { | 
				
			||||
  tags.value.splice(index, 1) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function deleteLastTag(event) { | 
				
			||||
  if (newTag.value === '' && event.key === 'Backspace' && tags.value.length > 0) { | 
				
			||||
    tags.value.pop() | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
function checkInputKey(event) { | 
				
			||||
  if (event.key === 'Enter' || event.key === ' ') { | 
				
			||||
    event.preventDefault() | 
				
			||||
    addTag() | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
onMounted(() => { | 
				
			||||
  focusInput() | 
				
			||||
}) | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,67 @@ | 
				
			||||
<template> | 
				
			||||
  <div class="field"> | 
				
			||||
    <div class="p-float-label"> | 
				
			||||
      <MultiSelect | 
				
			||||
        id="multiSelect" | 
				
			||||
        v-model="selectedValues" | 
				
			||||
        :options="options" | 
				
			||||
        optionLabel="name" | 
				
			||||
        optionValue="id" | 
				
			||||
        display="chip" | 
				
			||||
        @update:model-value="updateModelValue" | 
				
			||||
        @focus="isFocused = true" | 
				
			||||
        @blur="isFocused = false" | 
				
			||||
        panelClass="multi-select-panel" | 
				
			||||
      /> | 
				
			||||
      <label :for="inputId" v-text="label" /> | 
				
			||||
    </div> | 
				
			||||
      <small v-if="isInvalid" :class="{ 'p-error': isInvalid }" v-text="errorText" /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script setup> | 
				
			||||
import { ref, watch } from 'vue' | 
				
			||||
import MultiSelect from 'primevue/multiselect' | 
				
			||||
const props = defineProps({ | 
				
			||||
  modelValue: { | 
				
			||||
    type: Array, | 
				
			||||
    default: () => [] | 
				
			||||
  }, | 
				
			||||
  options: { | 
				
			||||
    type: Array, | 
				
			||||
    default: () => [] | 
				
			||||
  }, | 
				
			||||
  placeholder: String, | 
				
			||||
  inputId: { | 
				
			||||
    type: String, | 
				
			||||
    required: true, | 
				
			||||
    default: "", | 
				
			||||
  }, | 
				
			||||
  label: { | 
				
			||||
    type: String, | 
				
			||||
    required: true, | 
				
			||||
    default: "", | 
				
			||||
  }, | 
				
			||||
  errorText: { | 
				
			||||
    type: String, | 
				
			||||
    required: false, | 
				
			||||
    default: null, | 
				
			||||
  }, | 
				
			||||
  isInvalid: { | 
				
			||||
    type: Boolean, | 
				
			||||
    required: false, | 
				
			||||
    default: false, | 
				
			||||
  }, | 
				
			||||
}) | 
				
			||||
const emit = defineEmits(['update:modelValue']) | 
				
			||||
const selectedValues = ref([...props.modelValue]) | 
				
			||||
const isFocused = ref(false) | 
				
			||||
 | 
				
			||||
watch(() => props.modelValue, (newValue) => { | 
				
			||||
  selectedValues.value = [...newValue] | 
				
			||||
}) | 
				
			||||
 | 
				
			||||
const updateModelValue = (newValue) => { | 
				
			||||
  emit('update:modelValue', newValue) | 
				
			||||
} | 
				
			||||
</script> | 
				
			||||
@ -1,26 +1,29 @@ | 
				
			||||
<template> | 
				
			||||
  <Toolbar :class="toolbarClass"> | 
				
			||||
    <template #start> | 
				
			||||
      <slot></slot> | 
				
			||||
    <template v-slot:start> | 
				
			||||
      <slot name="start"></slot> | 
				
			||||
    </template> | 
				
			||||
    <template v-slot:end> | 
				
			||||
      <slot name="end"></slot> | 
				
			||||
    </template> | 
				
			||||
  </Toolbar> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script setup> | 
				
			||||
import Toolbar from "primevue/toolbar"; | 
				
			||||
import { computed } from "vue"; | 
				
			||||
import Toolbar from "primevue/toolbar" | 
				
			||||
import { computed } from "vue" | 
				
			||||
 | 
				
			||||
const props = defineProps({ | 
				
			||||
  showTopBorder: { | 
				
			||||
    type: Boolean, | 
				
			||||
    default: false, | 
				
			||||
  }, | 
				
			||||
}); | 
				
			||||
}) | 
				
			||||
 | 
				
			||||
const toolbarClass = computed(() => { | 
				
			||||
  if (props.showTopBorder) { | 
				
			||||
    return "pt-5 border-t border-b"; | 
				
			||||
    return "pt-5 border-t border-b" | 
				
			||||
  } | 
				
			||||
  return ""; | 
				
			||||
}); | 
				
			||||
  return "p-toolbar" | 
				
			||||
}) | 
				
			||||
</script> | 
				
			||||
 | 
				
			||||
@ -1,174 +1,174 @@ | 
				
			||||
<template> | 
				
			||||
  <v-form> | 
				
			||||
    <v-container fluid> | 
				
			||||
      <v-row> | 
				
			||||
        <v-col cols="12" sm="6" md="6"> | 
				
			||||
          <v-text-field | 
				
			||||
                  v-model="item.title" | 
				
			||||
                  :error-messages="titleErrors" | 
				
			||||
                  :label="$t('Title')" | 
				
			||||
                  required | 
				
			||||
                  @input="$v.item.title.$touch()" | 
				
			||||
                  @blur="$v.item.title.$touch()" | 
				
			||||
          /> | 
				
			||||
        </v-col> | 
				
			||||
 | 
				
			||||
        <v-col cols="12" sm="6" md="6"> | 
				
			||||
          <v-text-field | 
				
			||||
                  v-model="item.code" | 
				
			||||
                  :error-messages="codeErrors" | 
				
			||||
                  :label="$t('code')" | 
				
			||||
                  required | 
				
			||||
                  @input="$v.item.code.$touch()" | 
				
			||||
                  @blur="$v.item.code.$touch()" | 
				
			||||
          /> | 
				
			||||
        </v-col> | 
				
			||||
      </v-row> | 
				
			||||
 | 
				
			||||
      <v-row> | 
				
			||||
        <v-col cols="12" sm="6" md="6"> | 
				
			||||
          <v-combobox | 
				
			||||
                  v-model="item.category" | 
				
			||||
                  :items="categorySelectItems" | 
				
			||||
                  :error-messages="categoryErrors" | 
				
			||||
                  :no-data-text="$t('No results')" | 
				
			||||
                  :label="$t('category')" | 
				
			||||
                  item-text="name" | 
				
			||||
                  item-value="@id" | 
				
			||||
          /> | 
				
			||||
        </v-col> | 
				
			||||
 | 
				
			||||
        <v-col cols="12" sm="6" md="6"> | 
				
			||||
          <v-text-field | 
				
			||||
                  v-model.number="item.visibility" | 
				
			||||
                  :error-messages="visibilityErrors" | 
				
			||||
                  :label="$t('visibility')" | 
				
			||||
                  required | 
				
			||||
                  @input="$v.item.visibility.$touch()" | 
				
			||||
                  @blur="$v.item.visibility.$touch()" | 
				
			||||
          /> | 
				
			||||
        </v-col> | 
				
			||||
      </v-row> | 
				
			||||
    </v-container> | 
				
			||||
  </v-form> | 
				
			||||
  <div class="course-form-container"> | 
				
			||||
    <div class="form-header"> | 
				
			||||
      <BaseInputText | 
				
			||||
        id="course-name" | 
				
			||||
        :label="t('Course name')" | 
				
			||||
        :help-text="t('Write a short and striking course name, For example: Innovation Management')" | 
				
			||||
        v-model="courseName" | 
				
			||||
        :error-text="courseNameError" | 
				
			||||
        :is-invalid="isCourseNameInvalid" | 
				
			||||
        required | 
				
			||||
      /> | 
				
			||||
      <BaseAdvancedSettingsButton v-model="showAdvancedSettings"></BaseAdvancedSettingsButton> | 
				
			||||
    </div> | 
				
			||||
    <div v-if="showAdvancedSettings" class="advanced-settings"> | 
				
			||||
      <BaseMultiSelect | 
				
			||||
        id="category-multiselect" | 
				
			||||
        v-model="courseCategory" | 
				
			||||
        :options="categoryOptions" | 
				
			||||
        :label="t('Category')" | 
				
			||||
        input-id="multiselect-category" | 
				
			||||
      /> | 
				
			||||
      <BaseInputText | 
				
			||||
        id="course-code" | 
				
			||||
        :label="t('Course code')" | 
				
			||||
        :help-text="t('Only letters (a-z) and numbers (0-9)')" | 
				
			||||
        v-model="courseCode" | 
				
			||||
        :maxlength="40" | 
				
			||||
        :error-text="courseCodeError" | 
				
			||||
        :is-invalid="isCodeInvalid" | 
				
			||||
        validation-message="Only letters (a-z) and numbers (0-9) are allowed." | 
				
			||||
      /> | 
				
			||||
      <BaseDropdown | 
				
			||||
        name="language" | 
				
			||||
        v-model="courseLanguage" | 
				
			||||
        :options="languageOptions" | 
				
			||||
        :placeholder="t('Select Language')" | 
				
			||||
        input-id="language-dropdown" | 
				
			||||
        :label="t('Language')" | 
				
			||||
        option-label="name" | 
				
			||||
      /> | 
				
			||||
      <BaseCheckbox | 
				
			||||
        id="demo-content" | 
				
			||||
        :label="t('Fill with demo content')" | 
				
			||||
        v-model="fillDemoContent" | 
				
			||||
       name="" | 
				
			||||
      /> | 
				
			||||
      <BaseAutocomplete | 
				
			||||
        id="template" | 
				
			||||
        v-model="courseTemplate" | 
				
			||||
        :label="t('Select Template')" | 
				
			||||
        :search="searchTemplates" | 
				
			||||
      /> | 
				
			||||
    </div> | 
				
			||||
    <!-- Form Footer --> | 
				
			||||
    <div class="form-footer"> | 
				
			||||
      <BaseButton | 
				
			||||
        label="Back" | 
				
			||||
        icon="back" | 
				
			||||
        type="secondary" | 
				
			||||
        @click="goBack" | 
				
			||||
        class="mr-4" | 
				
			||||
      /> | 
				
			||||
      <BaseButton | 
				
			||||
        :label="t('Create this course')" | 
				
			||||
        icon="plus" | 
				
			||||
        type="primary" | 
				
			||||
        @click="submitForm" | 
				
			||||
      /> | 
				
			||||
    </div> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
  import has from 'lodash/has'; | 
				
			||||
  import useVuelidate from '@vuelidate/core'; | 
				
			||||
  import { required } from '@vuelidate/validators'; | 
				
			||||
  import { mapActions } from 'vuex'; | 
				
			||||
  import { mapFields } from 'vuex-map-fields'; | 
				
			||||
 | 
				
			||||
  export default { | 
				
			||||
    name: 'CourseForm', | 
				
			||||
    setup () { | 
				
			||||
      return { v$: useVuelidate() } | 
				
			||||
    }, | 
				
			||||
    props: { | 
				
			||||
      values: { | 
				
			||||
        type: Object, | 
				
			||||
        required: true | 
				
			||||
      }, | 
				
			||||
 | 
				
			||||
      errors: { | 
				
			||||
        type: Object, | 
				
			||||
        default: () => {} | 
				
			||||
      }, | 
				
			||||
 | 
				
			||||
      initialValues: { | 
				
			||||
        type: Object, | 
				
			||||
        default: () => {} | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    data() { | 
				
			||||
      return { | 
				
			||||
        title: null, | 
				
			||||
        code: null, | 
				
			||||
        category: null, | 
				
			||||
        visibility: null, | 
				
			||||
      }; | 
				
			||||
    }, | 
				
			||||
    computed: { | 
				
			||||
      ...mapFields('coursecategory', { | 
				
			||||
        categorySelectItems: 'selectItems' | 
				
			||||
      }), | 
				
			||||
 | 
				
			||||
      // eslint-disable-next-line | 
				
			||||
      item() { | 
				
			||||
        return this.initialValues || this.values; | 
				
			||||
      }, | 
				
			||||
 | 
				
			||||
      titleErrors() { | 
				
			||||
        const errors = []; | 
				
			||||
 | 
				
			||||
        if (!this.$v.item.title.$dirty) return errors; | 
				
			||||
 | 
				
			||||
        has(this.violations, 'title') && errors.push(this.violations.title); | 
				
			||||
 | 
				
			||||
        !this.$v.item.title.required && errors.push(this.$t('Field is required')); | 
				
			||||
 | 
				
			||||
        return errors; | 
				
			||||
      }, | 
				
			||||
      codeErrors() { | 
				
			||||
        const errors = []; | 
				
			||||
 | 
				
			||||
        if (!this.$v.item.code.$dirty) return errors; | 
				
			||||
 | 
				
			||||
        has(this.violations, 'code') && errors.push(this.violations.code); | 
				
			||||
 | 
				
			||||
        !this.$v.item.code.required && errors.push(this.$t('Field is required')); | 
				
			||||
 | 
				
			||||
        return errors; | 
				
			||||
      }, | 
				
			||||
      categoryErrors() { | 
				
			||||
        const errors = []; | 
				
			||||
 | 
				
			||||
        if (!this.$v.item.category.$dirty) return errors; | 
				
			||||
 | 
				
			||||
        has(this.violations, 'category') && errors.push(this.violations.category); | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return errors; | 
				
			||||
      }, | 
				
			||||
      visibilityErrors() { | 
				
			||||
        const errors = []; | 
				
			||||
 | 
				
			||||
        if (!this.$v.item.visibility.$dirty) return errors; | 
				
			||||
 | 
				
			||||
        has(this.violations, 'visibility') && errors.push(this.violations.visibility); | 
				
			||||
 | 
				
			||||
        !this.$v.item.visibility.required && errors.push(this.$t('Field is required')); | 
				
			||||
 | 
				
			||||
        return errors; | 
				
			||||
      }, | 
				
			||||
 | 
				
			||||
      violations() { | 
				
			||||
        return this.errors || {}; | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    mounted() { | 
				
			||||
      this.categoryGetSelectItems(); | 
				
			||||
    }, | 
				
			||||
    methods: { | 
				
			||||
      ...mapActions({ | 
				
			||||
        categoryGetSelectItems: 'coursecategory/load' | 
				
			||||
      }), | 
				
			||||
    }, | 
				
			||||
    validations: { | 
				
			||||
      item: { | 
				
			||||
        title: { | 
				
			||||
          required, | 
				
			||||
        }, | 
				
			||||
        code: { | 
				
			||||
          required, | 
				
			||||
        }, | 
				
			||||
        category: { | 
				
			||||
        }, | 
				
			||||
        visibility: { | 
				
			||||
          required, | 
				
			||||
        }, | 
				
			||||
      } | 
				
			||||
    } | 
				
			||||
  }; | 
				
			||||
<script setup> | 
				
			||||
import { onMounted, ref, watch } from "vue" | 
				
			||||
import BaseInputText from "../basecomponents/BaseInputText.vue" | 
				
			||||
import BaseAdvancedSettingsButton from "../basecomponents/BaseAdvancedSettingsButton.vue" | 
				
			||||
import BaseDropdown from "../basecomponents/BaseDropdown.vue" | 
				
			||||
import BaseCheckbox from "../basecomponents/BaseCheckbox.vue" | 
				
			||||
import BaseButton from "../basecomponents/BaseButton.vue" | 
				
			||||
import { useRouter } from "vue-router" | 
				
			||||
import courseService from "../../services/courseService" | 
				
			||||
import languageService from "../../services/languageService" | 
				
			||||
import BaseAutocomplete from "../basecomponents/BaseAutocomplete.vue" | 
				
			||||
import BaseMultiSelect from "../basecomponents/BaseMultiSelect.vue" | 
				
			||||
import { useI18n } from "vue-i18n" | 
				
			||||
 | 
				
			||||
const { t } = useI18n() | 
				
			||||
const courseName = ref('') | 
				
			||||
const courseCategory = ref([]) | 
				
			||||
const courseCode = ref('') | 
				
			||||
const courseLanguage = ref(null) | 
				
			||||
const fillDemoContent = ref(false) | 
				
			||||
const courseTemplate = ref(null); | 
				
			||||
const showAdvancedSettings = ref(false) | 
				
			||||
const router = useRouter() | 
				
			||||
 | 
				
			||||
const categoryOptions = ref([]) | 
				
			||||
const languageOptions = ref([]) | 
				
			||||
 | 
				
			||||
const courseNameError = ref('') | 
				
			||||
const courseCodeError = ref('') | 
				
			||||
const isCodeInvalid = ref(false) | 
				
			||||
const isCourseNameInvalid = ref(false) | 
				
			||||
 | 
				
			||||
const formSubmitted = ref(false) | 
				
			||||
 | 
				
			||||
const emit = defineEmits(['submit']) | 
				
			||||
 | 
				
			||||
const validateCourseCode = () => { | 
				
			||||
  const pattern = /^[a-zA-Z0-9]*$/ | 
				
			||||
  if (!pattern.test(courseCode.value)) { | 
				
			||||
    isCodeInvalid.value = true | 
				
			||||
    courseCodeError.value = 'Only letters (a-z) and numbers (0-9) are allowed.' | 
				
			||||
    return false | 
				
			||||
  } | 
				
			||||
  courseCodeError.value = '' | 
				
			||||
  return true | 
				
			||||
} | 
				
			||||
 | 
				
			||||
const submitForm = () => { | 
				
			||||
  formSubmitted.value = true | 
				
			||||
  if (!courseName.value) { | 
				
			||||
    isCourseNameInvalid.value = true | 
				
			||||
    courseNameError.value = 'This field is required' | 
				
			||||
    return | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  if (!validateCourseCode()) { | 
				
			||||
    return | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  emit('submit', { | 
				
			||||
    name: courseName.value, | 
				
			||||
    category: courseCategory.value ? courseCategory.value : null, | 
				
			||||
    code: courseCode.value, | 
				
			||||
    language: courseLanguage.value, | 
				
			||||
    template: courseTemplate.value ? courseTemplate.value.value : null, | 
				
			||||
    fillDemoContent: fillDemoContent.value | 
				
			||||
  }) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
onMounted(async () => { | 
				
			||||
  try { | 
				
			||||
    const categoriesResponse = await courseService.getCategories('categories'); | 
				
			||||
    categoryOptions.value = categoriesResponse.map(category => ({ | 
				
			||||
      name: category.name, | 
				
			||||
      id: category.id, | 
				
			||||
    })) | 
				
			||||
 | 
				
			||||
    const languagesResponse = await languageService.findAll() | 
				
			||||
    const data = await languagesResponse.json() | 
				
			||||
    languageOptions.value = data['hydra:member'].map(language => ({ | 
				
			||||
      name: language.englishName, | 
				
			||||
      id: language.isocode, | 
				
			||||
    })) | 
				
			||||
 | 
				
			||||
  } catch (error) { | 
				
			||||
    console.error('Failed to load dropdown data', error) | 
				
			||||
  } | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
const searchTemplates = async (query) => { | 
				
			||||
  if (query && query.length >= 3) { | 
				
			||||
    return courseService.searchTemplates(query) | 
				
			||||
  } else { | 
				
			||||
    return [] | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
const goBack = () => { | 
				
			||||
  router.go(-1) | 
				
			||||
} | 
				
			||||
</script> | 
				
			||||
 | 
				
			||||
@ -1,53 +1,64 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
  <div class="create-course-page m-10"> | 
				
			||||
 | 
				
			||||
    <div class="message-container mb-4"> | 
				
			||||
      <Message severity="info"> | 
				
			||||
        {{ t('Once you click on "Create a course", a course is created with a section for Tests, Project based learning, Assessments, Courses, Dropbox, Agenda and much more. Logging in as teacher provides you with editing privileges for this course.') }} | 
				
			||||
      </Message> | 
				
			||||
    </div> | 
				
			||||
 | 
				
			||||
    <h1 class="page-title text-xl text-gray-90">{{ t('Add a new course') }}</h1> | 
				
			||||
    <hr /> | 
				
			||||
 | 
				
			||||
    <CourseForm | 
				
			||||
      ref="createForm" | 
				
			||||
      :errors="violations" | 
				
			||||
      :values="item" | 
				
			||||
      @submit="submitCourse" | 
				
			||||
    /> | 
				
			||||
    <Loading :visible="isLoading" /> | 
				
			||||
 | 
				
			||||
    <Toolbar | 
				
			||||
      :handle-reset="resetForm" | 
				
			||||
      :handle-submit="onSendForm" | 
				
			||||
    ></Toolbar> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions } from "vuex" | 
				
			||||
import { createHelpers } from "vuex-map-fields" | 
				
			||||
import CourseForm from "../../components/course/Form.vue" | 
				
			||||
import Loading from "../../components/Loading.vue" | 
				
			||||
import Toolbar from "../../components/Toolbar.vue" | 
				
			||||
import CreateMixin from "../../mixins/CreateMixin" | 
				
			||||
<script setup> | 
				
			||||
import { ref, computed } from 'vue' | 
				
			||||
import { useStore } from 'vuex' | 
				
			||||
import CourseForm from '../../components/course/Form.vue' | 
				
			||||
import Loading from '../../components/Loading.vue' | 
				
			||||
import { useRouter } from "vue-router" | 
				
			||||
import Message from 'primevue/message' | 
				
			||||
import courseService from "../../services/courseService" | 
				
			||||
import { useI18n } from "vue-i18n" | 
				
			||||
 | 
				
			||||
const servicePrefix = "Course" | 
				
			||||
const store = useStore() | 
				
			||||
const item = ref({}) | 
				
			||||
const router = useRouter() | 
				
			||||
const { t } = useI18n() | 
				
			||||
 | 
				
			||||
const { mapFields } = createHelpers({ | 
				
			||||
  getterType: "course/getField", | 
				
			||||
  mutationType: "course/updateField", | 
				
			||||
}) | 
				
			||||
const isLoading = computed(() => store.getters['course/getField']('isLoading')) | 
				
			||||
const violations = computed(() => store.getters['course/getField']('violations')) | 
				
			||||
const courseData = ref({}) | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: "CourseCreate", | 
				
			||||
  servicePrefix, | 
				
			||||
  mixins: [CreateMixin], | 
				
			||||
  components: { | 
				
			||||
    Loading, | 
				
			||||
    Toolbar, | 
				
			||||
    CourseForm, | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      item: {}, | 
				
			||||
const submitCourse = async (formData) => { | 
				
			||||
  isLoading.value = true | 
				
			||||
  try { | 
				
			||||
    let tempResponse = await courseService.createCourse(formData) | 
				
			||||
    if (tempResponse.success) { | 
				
			||||
      const courseId = tempResponse.courseId | 
				
			||||
      const sessionId = 0 | 
				
			||||
      await router.push(`/course/${courseId}/home?sid=${sessionId}`) | 
				
			||||
    } else { | 
				
			||||
      console.error(tempResponse.message) | 
				
			||||
    } | 
				
			||||
  } catch (error) { | 
				
			||||
    console.error(error) | 
				
			||||
    if (error.response && error.response.data) { | 
				
			||||
      violations.value = error.response.data | 
				
			||||
    } else { | 
				
			||||
      console.error('An unexpected error occurred.') | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields(["error", "isLoading", "created", "violations"]), | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions("course", ["create", "reset"]), | 
				
			||||
  }, | 
				
			||||
  } finally { | 
				
			||||
    isLoading.value = false | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
</script> | 
				
			||||
</script> | 
				
			||||
 | 
				
			||||
@ -0,0 +1,34 @@ | 
				
			||||
<?php | 
				
			||||
 | 
				
			||||
declare(strict_types=1); | 
				
			||||
 | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
namespace Chamilo\CoreBundle\Migrations\Schema\V200; | 
				
			||||
 | 
				
			||||
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo; | 
				
			||||
use Doctrine\DBAL\Schema\Schema; | 
				
			||||
 | 
				
			||||
class Version20240308225800 extends AbstractMigrationChamilo | 
				
			||||
{ | 
				
			||||
    public function getDescription(): string | 
				
			||||
    { | 
				
			||||
        return 'Adds the auto_remove field to the extra_field table to manage automatic deletion during anonymization, if it does not exist.'; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public function up(Schema $schema): void | 
				
			||||
    { | 
				
			||||
        $table = $schema->getTable('extra_field'); | 
				
			||||
        if (!$table->hasColumn('auto_remove')) { | 
				
			||||
            $this->addSql('ALTER TABLE extra_field ADD auto_remove TINYINT(1) NOT NULL DEFAULT 0'); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public function down(Schema $schema): void | 
				
			||||
    { | 
				
			||||
        $table = $schema->getTable('extra_field'); | 
				
			||||
        if ($table->hasColumn('auto_remove')) { | 
				
			||||
            $this->addSql('ALTER TABLE extra_field DROP COLUMN auto_remove'); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,487 @@ | 
				
			||||
<?php | 
				
			||||
 | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
/* | 
				
			||||
User account synchronisation from LDAP | 
				
			||||
 | 
				
			||||
This script | 
				
			||||
creates new user accounts found in the LDAP directory (if multiURL is enable, it creates the user on the URL for which the LDAP has been configured) | 
				
			||||
disables user accounts not found in the LDAP directory (it disbales the user for all URLs)  | 
				
			||||
or delete the user depending on the variable deleteUsersNotFoundInLDAP (only if the user has auth_source === extldap) | 
				
			||||
updates existing user accounts found in the LDAP directory, re-enabling them if disabled (it applies for all URLs) only if option reenableUsersFoundInLDAP is set to true. | 
				
			||||
anonymizes user accounts disabled for more than 3 years (applies for all URLs) only if the variable is set to true (by default). | 
				
			||||
 | 
				
			||||
This script can be run unattended. | 
				
			||||
 | 
				
			||||
It does not read any parameter from the command line, but uses the global configuration arrays | 
				
			||||
 $extldap_config | 
				
			||||
and | 
				
			||||
 $extldap_user_correspondance | 
				
			||||
defined in app/config/auth.conf.php or overriden in app/config/configuration.php in MultiURL case. | 
				
			||||
 | 
				
			||||
username field is used to identify and match LDAP and Chamilo accounts together. | 
				
			||||
($extldap_user_correspondance['username']) | 
				
			||||
*/ | 
				
			||||
exit; | 
				
			||||
// Change this to the absolute path to chamilo root folder if you move the script out of tests/scripts | 
				
			||||
$chamiloRoot = __DIR__.'/../..'; | 
				
			||||
 | 
				
			||||
// Set to true in order to get a trace of changes made by this script | 
				
			||||
$debug = false; | 
				
			||||
 | 
				
			||||
// Set to test mode by default to only show the output, put this test variable to 0 to enable creation, modificaction and deletion of users | 
				
			||||
$test = 1; | 
				
			||||
 | 
				
			||||
// It defines if the user not find in the LDAP but present in Chamilo should be deleted or disabled. By default it will be disabled. | 
				
			||||
// Set it to true for users to be deleted. | 
				
			||||
$deleteUsersNotFoundInLDAP = false; | 
				
			||||
 | 
				
			||||
// Re-enable users found in LDAP and that where present but inactivated in Chamilo | 
				
			||||
$reenableUsersFoundInLDAP = false; | 
				
			||||
 | 
				
			||||
// Anonymize user accounts disabled for more than 3 years | 
				
			||||
$anonymizeUserAccountsDisbaledFor3Years = false; | 
				
			||||
 | 
				
			||||
// List of username of accounts that should not be disabled or deleted if not present in LDAP  | 
				
			||||
// For exemple the first admin and the anonymous user that has no username ('') | 
				
			||||
//$usernameListNotToTouchEvenIfNotInLDAP = ['admin','','test']; | 
				
			||||
 | 
				
			||||
// List of LDAP attributes that are not in extldap_user_correspondance but are needed in this script | 
				
			||||
//$extraLdapAttributes[0][] = 'description'; | 
				
			||||
//$extraLdapAttributes[0][] = 'userAccountControl'; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use Chamilo\CoreBundle\Entity\ExtraFieldValues; | 
				
			||||
use Chamilo\CoreBundle\Entity\ExtraField; | 
				
			||||
use Chamilo\CoreBundle\Entity\TrackEDefault; | 
				
			||||
use Chamilo\UserBundle\Entity\User; | 
				
			||||
use Doctrine\DBAL\FetchMode; | 
				
			||||
use Doctrine\ORM\OptimisticLockException; | 
				
			||||
 | 
				
			||||
if (php_sapi_name() !== 'cli') { | 
				
			||||
    die("this script is supposed to be run from the command-line\n"); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
require $chamiloRoot.'/cli-config.php'; | 
				
			||||
require_once $chamiloRoot.'/main/inc/lib/api.lib.php'; | 
				
			||||
require_once $chamiloRoot.'/app/config/auth.conf.php'; | 
				
			||||
require_once $chamiloRoot.'/main/inc/lib/database.constants.inc.php'; | 
				
			||||
require_once $chamiloRoot.'/main/auth/external_login/ldap.inc.php'; | 
				
			||||
 | 
				
			||||
ini_set('memory_limit', -1); | 
				
			||||
 | 
				
			||||
// Retreive information from $extldap_user_correspondance and extra fields | 
				
			||||
// into $tableFields, $extraFields, $allFields and $ldapAttributes | 
				
			||||
 | 
				
			||||
$generalTableFieldMap = $extldap_user_correspondance; | 
				
			||||
$multipleUrlLDAPConfig = false; | 
				
			||||
$allLdapUsers = []; | 
				
			||||
const EXTRA_ARRAY_KEY = 'extra'; | 
				
			||||
 | 
				
			||||
// read all users from the internal database | 
				
			||||
 | 
				
			||||
$userRepository = Database::getManager()->getRepository('ChamiloUserBundle:User'); | 
				
			||||
$dbUsers = []; | 
				
			||||
foreach ($userRepository->findAll() as $user) { | 
				
			||||
    if ($user->getId() > 1) { | 
				
			||||
        $username = strtolower($user->getUsername()); | 
				
			||||
        array_key_exists($username, $dbUsers) and die("duplicate username $username found in the database\n"); | 
				
			||||
        $dbUsers[$username] = $user; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
if ($debug) { | 
				
			||||
    echo count($dbUsers) . " users with id > 1 found in internal database\n"; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
if (api_is_multiple_url_enabled()) { | 
				
			||||
    $accessUrls = api_get_access_urls(0,100000,'id'); | 
				
			||||
    $multipleUrlLDAPConfig = true; | 
				
			||||
    if (!empty($extldap_config) && array_key_exists('host', $extldap_config) && !empty($extldap_config['host'])) { | 
				
			||||
        $multipleUrlLDAPConfig = false; | 
				
			||||
    }     | 
				
			||||
} | 
				
			||||
 | 
				
			||||
if (!$multipleUrlLDAPConfig) { | 
				
			||||
    $accessUrls[0]['id'] = 0; | 
				
			||||
    $generalTableFieldMap[0] = $generalTableFieldMap; | 
				
			||||
} | 
				
			||||
if ($debug) { | 
				
			||||
    echo "accessUrls = " . print_r($accessUrls,1); | 
				
			||||
} | 
				
			||||
foreach ($accessUrls as $accessUrl) { | 
				
			||||
    $tableFields = []; | 
				
			||||
    $extraFields = []; | 
				
			||||
    $extraFieldMap = []; | 
				
			||||
    $accessUrlId = $accessUrl['id']; | 
				
			||||
    global $_configuration; | 
				
			||||
    $_configuration['access_url'] = $accessUrlId; | 
				
			||||
    $extldap_config[$accessUrlId] = api_get_configuration_value('extldap_config'); | 
				
			||||
    $generalTableFieldMap[$accessUrlId] = $extldap_user_correspondance[$accessUrlId] = api_get_configuration_value('extldap_user_correspondance'); | 
				
			||||
    $ldapAttributes = $extraLdapAttributes[$accessUrlId]; | 
				
			||||
    if (array_key_exists($accessUrlId, $generalTableFieldMap) && is_array($generalTableFieldMap[$accessUrlId])) { | 
				
			||||
        $tableFieldMap = $generalTableFieldMap[$accessUrlId]; | 
				
			||||
        if (array_key_exists(EXTRA_ARRAY_KEY, $tableFieldMap) and is_array($tableFieldMap[EXTRA_ARRAY_KEY])) { | 
				
			||||
            $extraFieldMap = $tableFieldMap[EXTRA_ARRAY_KEY]; | 
				
			||||
            unset($tableFieldMap[EXTRA_ARRAY_KEY]); | 
				
			||||
        } | 
				
			||||
        $extraFieldRepository = Database::getManager()->getRepository('ChamiloCoreBundle:ExtraField'); | 
				
			||||
        $extraFieldValueRepository = Database::getManager()->getRepository('ChamiloCoreBundle:ExtraFieldValues'); | 
				
			||||
        foreach ([false => $tableFieldMap, true => $extraFieldMap] as $areExtra => $fields) { | 
				
			||||
            foreach ($fields as $name => $value) { | 
				
			||||
                $userField = (object)[ | 
				
			||||
                    'name' => $name, | 
				
			||||
                    'constant' => '!' === $value[0] ? substr($value, 1) : null, | 
				
			||||
                    'function' => 'func' === $value, | 
				
			||||
                    'ldapAttribute' => ('!' !== $value[0] and 'func' !== $value) ? $value : null, | 
				
			||||
                ]; | 
				
			||||
                if (!$userField->constant and !$userField->function) { | 
				
			||||
                    $ldapAttributes[] = $value; | 
				
			||||
                } | 
				
			||||
                if ($areExtra) { | 
				
			||||
                    $userField->extraField = $extraFieldRepository->findOneBy( | 
				
			||||
                        [ | 
				
			||||
                            'extraFieldType' => ExtraField::USER_FIELD_TYPE, | 
				
			||||
                            'variable' => $name, | 
				
			||||
                        ] | 
				
			||||
                    ) or die("Cannot find user extra field '$name'\n"); | 
				
			||||
                    foreach ($extraFieldValueRepository->findBy(['field' => $userField->extraField]) as $extraFieldValue) { | 
				
			||||
                        $userField->extraFieldValues[$extraFieldValue->getItemId()] = $extraFieldValue; | 
				
			||||
                    } | 
				
			||||
                    $extraFields[] = $userField; | 
				
			||||
                } elseif ($name !== 'admin') { | 
				
			||||
                    try { | 
				
			||||
                        $userField->getter = new ReflectionMethod( | 
				
			||||
                            '\Chamilo\UserBundle\Entity\User', | 
				
			||||
                            'get' . str_replace('_', '', ucfirst($name)) | 
				
			||||
                        ); | 
				
			||||
                        $userField->setter = new ReflectionMethod( | 
				
			||||
                            '\Chamilo\UserBundle\Entity\User', | 
				
			||||
                            'set' . str_replace('_', '', ucfirst($name)) | 
				
			||||
                        ); | 
				
			||||
                    } catch (ReflectionException $exception) { | 
				
			||||
                        die($exception->getMessage() . "\n"); | 
				
			||||
                    } | 
				
			||||
                    $tableFields[] = $userField; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        $allFields = array_merge($tableFields, $extraFields); | 
				
			||||
    } | 
				
			||||
    // Retrieve source information from LDAP | 
				
			||||
 | 
				
			||||
    if ($debug) { | 
				
			||||
        echo ' Entering ldap search ' . "\n"; | 
				
			||||
        echo ' extldap_config = ' . print_r($extldap_config,1) . "\n"; | 
				
			||||
    } | 
				
			||||
    if (!$multipleUrlLDAPConfig) { | 
				
			||||
        $extldap_config[$accessUrlId] = $extldap_config; | 
				
			||||
    } | 
				
			||||
    $ldap = false; | 
				
			||||
    if (array_key_exists($accessUrlId, $extldap_config) && is_array($extldap_config[$accessUrlId])) { | 
				
			||||
        foreach ($extldap_config[$accessUrlId]['host'] as $ldapHost) { | 
				
			||||
            $ldap = array_key_exists('port', $extldap_config) | 
				
			||||
                ? ldap_connect($ldapHost, $extldap_config['port']) | 
				
			||||
                : ldap_connect($ldapHost); | 
				
			||||
            if (false !== $ldap) { | 
				
			||||
                break; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        if (false === $ldap) { | 
				
			||||
            die("ldap_connect() failed\n"); | 
				
			||||
        } | 
				
			||||
        if ($debug) { | 
				
			||||
            echo "Connected to LDAP server $ldapHost.\n"; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        ldap_set_option( | 
				
			||||
            $ldap, | 
				
			||||
            LDAP_OPT_PROTOCOL_VERSION, | 
				
			||||
            array_key_exists('protocol_version', $extldap_config[$accessUrlId]) ? $extldap_config[$accessUrlId]['protocol_version'] : 2 | 
				
			||||
        ); | 
				
			||||
     | 
				
			||||
        ldap_set_option( | 
				
			||||
            $ldap, | 
				
			||||
            LDAP_OPT_REFERRALS, | 
				
			||||
            array_key_exists('referrals', $extldap_config[$accessUrlId]) ? $extldap_config[$accessUrlId]['referrals'] : false | 
				
			||||
        ); | 
				
			||||
 | 
				
			||||
        ldap_bind($ldap, $extldap_config[$accessUrlId]['admin_dn'], $extldap_config[$accessUrlId]['admin_password']) | 
				
			||||
        or die('ldap_bind() failed: ' . ldap_error($ldap) . "\n"); | 
				
			||||
        if ($debug) { | 
				
			||||
            $adminDn = $extldap_config[$accessUrlId]['admin_dn']; | 
				
			||||
            echo "Bound to LDAP server as $adminDn .\n"; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
	$baseDn = $extldap_config[$accessUrlId]['base_dn'] | 
				
			||||
        or die("cannot read the LDAP directory base DN where to search for user entries\n"); | 
				
			||||
 | 
				
			||||
	if (!$multipleUrlLDAPConfig) { | 
				
			||||
            $extldap_user_correspondance[$accessUrlId] = $extldap_user_correspondance; | 
				
			||||
        } | 
				
			||||
        $ldapUsernameAttribute = $extldap_user_correspondance[$accessUrlId]['username'] | 
				
			||||
        or die("cannot read the name of the LDAP attribute where to find the username\n"); | 
				
			||||
 | 
				
			||||
        $filter = "$ldapUsernameAttribute=*"; | 
				
			||||
 | 
				
			||||
        if (array_key_exists('filter', $extldap_config[$accessUrlId])) { | 
				
			||||
            $filter = '(&('.$filter.')('.$extldap_config[$accessUrlId]['filter'].'))'; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        $searchResult = ldap_search($ldap, $baseDn, $filter, $ldapAttributes) | 
				
			||||
        or die("ldap_search(\$ldap, '$baseDn', '$filter', [".join(',', $ldapAttributes).']) failed: '.ldap_error($ldap)."\n"); | 
				
			||||
 | 
				
			||||
        if ($debug) { | 
				
			||||
            echo ldap_count_entries($ldap, $searchResult) . " LDAP entries found\n"; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        $ldapUsers = []; | 
				
			||||
 | 
				
			||||
        $entry = ldap_first_entry($ldap, $searchResult); | 
				
			||||
        while (false !== $entry) { | 
				
			||||
            $attributes = ldap_get_attributes($ldap, $entry); | 
				
			||||
	    $ldapUser = []; | 
				
			||||
            foreach ($allFields as $userField) { | 
				
			||||
                if (!is_null($userField->constant)) { | 
				
			||||
                    $value = $userField->constant; | 
				
			||||
		} elseif ($userField->function) { | 
				
			||||
                    $func = "extldap_get_$userField->name"; | 
				
			||||
                    if (function_exists($func)) { | 
				
			||||
                        $value = extldap_purify_string($func($attributes)); | 
				
			||||
                    } else { | 
				
			||||
                        die("'func' not implemented for $userField->name\n"); | 
				
			||||
                    } | 
				
			||||
                } else { | 
				
			||||
                    if (array_key_exists($userField->ldapAttribute, $attributes)) { | 
				
			||||
                        $values = ldap_get_values($ldap, $entry, $userField->ldapAttribute) | 
				
			||||
                        or die( | 
				
			||||
                            'could not read value of attribute ' . $userField->ldapAttribute | 
				
			||||
                            . ' of entry ' . ldap_get_dn($ldap, $entry) | 
				
			||||
                            . "\n" | 
				
			||||
                        ); | 
				
			||||
                        (1 === $values['count']) | 
				
			||||
                        or die( | 
				
			||||
                            $values['count'] . ' values found (expected only one)' | 
				
			||||
                            . ' in attribute ' . $userField->ldapAttribute | 
				
			||||
                            . ' of entry ' . ldap_get_dn($ldap, $entry) | 
				
			||||
                            . "\n" | 
				
			||||
                        ); | 
				
			||||
                        $value = $values[0]; | 
				
			||||
                    } else { | 
				
			||||
                        $value = ''; | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
                $ldapUser[$userField->name] = $value; | 
				
			||||
            } | 
				
			||||
	    $username = strtolower($ldapUser['username']); | 
				
			||||
            array_key_exists($username, $ldapUsers) and die("duplicate username '$username' found in LDAP\n"); | 
				
			||||
            $ldapUsers[$username] = $ldapUser; | 
				
			||||
            if ($debug) { | 
				
			||||
                echo 'Adding user ' . $username . ' to ldapUsersArray ' . "\n"; | 
				
			||||
                echo "ldapUser = " . print_r($ldapUser,1) . "\n"; | 
				
			||||
            } | 
				
			||||
            $entry = ldap_next_entry($ldap, $entry); | 
				
			||||
        } | 
				
			||||
     | 
				
			||||
        ldap_close($ldap); | 
				
			||||
        if ($debug) { | 
				
			||||
            echo "ldapUsers = " . print_r($ldapUsers,1) . "\n"; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // create new user accounts found in the LDAP directory and update the existing ones, re-enabling if necessary | 
				
			||||
        foreach ($ldapUsers as $username => $ldapUser) { | 
				
			||||
            if (array_key_exists($username, $dbUsers)) { | 
				
			||||
                $user = $dbUsers[$username]; | 
				
			||||
                if ($debug) { | 
				
			||||
                    echo "User in DB = " . $username . " and user id = " . $user->getId() . "\n"; | 
				
			||||
                } | 
				
			||||
            } else { | 
				
			||||
                if (!$test) { | 
				
			||||
                    $user = new User(); | 
				
			||||
                    $dbUsers[$username] = $user; | 
				
			||||
                    $user->setUsernameCanonical($username); | 
				
			||||
                } | 
				
			||||
                if ($debug) { | 
				
			||||
                    echo 'Created ' . $username . "\n"; | 
				
			||||
                    echo "ldapUser = " . print_r($ldapUser,1) . "\n"; | 
				
			||||
                } | 
				
			||||
	    } | 
				
			||||
            if ($test) { | 
				
			||||
                if ($debug) { | 
				
			||||
                    echo 'Updated ' . $username . ' fields '."\n"; | 
				
			||||
                } | 
				
			||||
            } else { | 
				
			||||
                foreach ($tableFields as $userField) { | 
				
			||||
                    $value = $ldapUser[$userField->name]; | 
				
			||||
                    if ($userField->getter->invoke($user) !== $value) { | 
				
			||||
                        $userField->setter->invoke($user, $value); | 
				
			||||
                        if ($debug) { | 
				
			||||
                            echo 'Updated ' . $username . ' field '.$userField->name."\n"; | 
				
			||||
                        } | 
				
			||||
                        if ($userField->name == 'email') { | 
				
			||||
                            $user->setEmailCanonical($value); | 
				
			||||
                        } | 
				
			||||
                    } | 
				
			||||
	        } | 
				
			||||
                if (!$user->isActive() and $reenableUsersFoundInLDAP) { | 
				
			||||
                    $user->setActive(true); | 
				
			||||
                } | 
				
			||||
                Database::getManager()->persist($user); | 
				
			||||
                try { | 
				
			||||
                    Database::getManager()->flush(); | 
				
			||||
                } catch (OptimisticLockException $exception) { | 
				
			||||
                    die($exception->getMessage()."\n"); | 
				
			||||
                } | 
				
			||||
                if($debug) { | 
				
			||||
                    echo 'Sent to DB ' . $username . " with user id = " . $user->getId() . "\n"; | 
				
			||||
                } | 
				
			||||
                if ($multipleUrlLDAPConfig) { | 
				
			||||
                    UrlManager::add_user_to_url($user->getId(), $accessUrlId); | 
				
			||||
                } elseif (!api_is_multiple_url_enabled()) { | 
				
			||||
                    //we are adding by default the access_url_user table with access_url_id = 1 | 
				
			||||
                    UrlManager::add_user_to_url($user->getId(), 1); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        // also update extra field values | 
				
			||||
 | 
				
			||||
        if ($test) { | 
				
			||||
            if ($debug) { | 
				
			||||
                echo 'Updated ' . $username . ' extra fields ' . "\n"; | 
				
			||||
            } | 
				
			||||
        } else { | 
				
			||||
            foreach ($ldapUsers as $username => $ldapUser) { | 
				
			||||
                $user = $dbUsers[$username]; | 
				
			||||
                foreach ($extraFields as $userField) { | 
				
			||||
                    $value = $ldapUser[$userField->name]; | 
				
			||||
                    if (array_key_exists($user->getId(), $userField->extraFieldValues)) { | 
				
			||||
                        /** | 
				
			||||
                         * @var ExtraFieldValues $extraFieldValue | 
				
			||||
                         */ | 
				
			||||
                        $extraFieldValue = $userField->extraFieldValues[$user->getId()]; | 
				
			||||
                        if ($extraFieldValue->getValue() !== $value) { | 
				
			||||
                            $extraFieldValue->setValue($value); | 
				
			||||
                            Database::getManager()->persist($extraFieldValue); | 
				
			||||
                            if ($debug) { | 
				
			||||
                                echo 'Updated ' . $username . ' extra field ' . $userField->name . "\n"; | 
				
			||||
                            } | 
				
			||||
                        } | 
				
			||||
                    } else { | 
				
			||||
                        $extraFieldValue = new ExtraFieldValues(); | 
				
			||||
                        $extraFieldValue->setValue($value); | 
				
			||||
                        $extraFieldValue->setField($userField->extraField); | 
				
			||||
                        $extraFieldValue->setItemId($user->getId()); | 
				
			||||
                        Database::getManager()->persist($extraFieldValue); | 
				
			||||
                        $userField->extraFieldValues[$user->getId()] = $extraFieldValue; | 
				
			||||
                        if ($debug) { | 
				
			||||
                            echo 'Created ' . $username . ' extra field ' . $userField->name . "\n"; | 
				
			||||
                        } | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            try { | 
				
			||||
                Database::getManager()->flush(); | 
				
			||||
            } catch (OptimisticLockException $exception) { | 
				
			||||
                die($exception->getMessage()."\n"); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        $allLdapUsers = array_merge($allLdapUsers, $ldapUsers); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// disable or delete user accounts not found in the LDAP directories depending on $deleteUsersNotFoundInLDAP | 
				
			||||
 | 
				
			||||
$now = new DateTime(); | 
				
			||||
foreach (array_diff(array_keys($dbUsers), array_keys($allLdapUsers)) as $usernameToDisable) { | 
				
			||||
    if (in_array($usernameToDisable, $usernameListNotToTouchEvenIfNotInLDAP)) { | 
				
			||||
        if ($debug) { | 
				
			||||
            echo 'User not modified even if not present in LDAP : ' . $usernameToDisable . "\n"; | 
				
			||||
        } | 
				
			||||
    } else { | 
				
			||||
        $user = $dbUsers[$usernameToDisable]; | 
				
			||||
        if ($deleteUsersNotFoundInLDAP) { | 
				
			||||
            if (!$test) { | 
				
			||||
                if (!UserManager::delete_user($user->getId())) { | 
				
			||||
                    if ($debug) { | 
				
			||||
                        echo 'Unable to delete user ' . $usernameToDisable . "\n"; | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            if ($debug) { | 
				
			||||
                echo 'Deleted user ' . $usernameToDisable . "\n"; | 
				
			||||
            }  | 
				
			||||
        } else { | 
				
			||||
            if (!$test) { | 
				
			||||
                if ($user->isActive()) { | 
				
			||||
                    // In order to avoid slow individual SQL updates, we do not call | 
				
			||||
                    // UserManager::disable($user->getId()); | 
				
			||||
                    $user->setActive(false); | 
				
			||||
                    Database::getManager()->persist($user); | 
				
			||||
                    // In order to avoid slow individual SQL updates, we do not call | 
				
			||||
                    // Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $user->getId()); | 
				
			||||
                    $trackEDefault = new TrackEDefault(); | 
				
			||||
                    $trackEDefault->setDefaultUserId(1); | 
				
			||||
                    $trackEDefault->setDefaultDate($now); | 
				
			||||
                    $trackEDefault->setDefaultEventType(LOG_USER_DISABLE); | 
				
			||||
                    $trackEDefault->setDefaultValueType(LOG_USER_ID); | 
				
			||||
                    $trackEDefault->setDefaultValue($user->getId()); | 
				
			||||
                    Database::getManager()->persist($trackEDefault); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            if ($debug) { | 
				
			||||
                echo 'Disabled ' . $user->getUsername() . "\n"; | 
				
			||||
            } | 
				
			||||
        }     | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
if (!$test) { | 
				
			||||
    try { | 
				
			||||
        // Saving everything together | 
				
			||||
        Database::getManager()->flush(); | 
				
			||||
    } catch (OptimisticLockException $exception) { | 
				
			||||
        die($exception->getMessage()."\n"); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
// anonymize user accounts disabled for more than 3 years | 
				
			||||
if ($anonymizeUserAccountsDisbaledFor3Years) { | 
				
			||||
    $longDisabledUserIds = []; | 
				
			||||
    foreach (Database::query( | 
				
			||||
        'select default_value | 
				
			||||
        from track_e_default | 
				
			||||
        where default_event_type=\'user_disable\' and default_value_type=\'user_id\' | 
				
			||||
        group by default_value | 
				
			||||
        having max(default_date) < date_sub(now(), interval 3 year)' | 
				
			||||
    )->fetchAll(FetchMode::COLUMN) as $userId) { | 
				
			||||
        $longDisabledUserIds[] = $userId; | 
				
			||||
    } | 
				
			||||
    $anonymizedUserIds = []; | 
				
			||||
    foreach (Database::query( | 
				
			||||
        'select distinct default_value | 
				
			||||
        from track_e_default | 
				
			||||
        where default_event_type=\'user_anonymized\' and default_value_type=\'user_id\'' | 
				
			||||
    )->fetchAll(FetchMode::COLUMN) as $userId) { | 
				
			||||
        $anonymizedUserIds[] = $userId; | 
				
			||||
    } | 
				
			||||
    foreach (array_diff($longDisabledUserIds, $anonymizedUserIds) as $userId) { | 
				
			||||
        $user = $userRepository->find($userId); | 
				
			||||
        if ($user && !$user->isEnabled()) { | 
				
			||||
            if (!$test) { | 
				
			||||
                try { | 
				
			||||
                    UserManager::anonymize($userId) | 
				
			||||
                    or die("could not anonymize user $userId\n"); | 
				
			||||
                } catch (Exception $exception) { | 
				
			||||
                    die($exception->getMessage()."\n"); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            if ($debug) { | 
				
			||||
                echo "Anonymized user $userId\n"; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue