mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-01 12:19:28 +00:00
expanded title validation in frontend aswell before validation for the same patterns in backend just in case; phrasen alert messages & use backend response instead of frontend validated title
This commit is contained in:
@@ -486,7 +486,17 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
|
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject emojis and pictographs
|
||||||
|
// allows foreign letters, math symbols, accents, and standard punctuation.
|
||||||
|
$emojiPattern = '/[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F900}-\x{1FAFF}\x{23E9}-\x{23EF}\x{2b50}\x{2700}-\x{27BF}]/u';
|
||||||
|
|
||||||
|
// i would like this much more but our server does not recognize this utf-8 character range this way, so hexcodes it is
|
||||||
|
// if (preg_match('/\p{Extended_Pictographic}/u', $titel)) {
|
||||||
|
if (preg_match($emojiPattern, $titel)) {
|
||||||
|
|
||||||
|
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
|
||||||
|
}
|
||||||
|
|
||||||
$this->checkProjektarbeitForFinishedStatus($projektarbeit_id);
|
$this->checkProjektarbeitForFinishedStatus($projektarbeit_id);
|
||||||
|
|
||||||
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
|
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
|
||||||
@@ -542,7 +552,8 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
);
|
);
|
||||||
|
|
||||||
$result = $this->ProjektarbeitModel->load($projektarbeit_id);
|
$result = $this->ProjektarbeitModel->load($projektarbeit_id);
|
||||||
$this->terminateWithSuccess($result);
|
$titel = hasData($result) ? getData($result)[0]->titel : $titel;
|
||||||
|
$this->terminateWithSuccess($titel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import VueDatePicker from '../../vueDatepicker.js.php';
|
|||||||
import ApiAbgabe from '../../../api/factory/abgabe.js'
|
import ApiAbgabe from '../../../api/factory/abgabe.js'
|
||||||
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
||||||
import { formatISODate, getViennaTodayISO } from "./dateUtils.js";
|
import { formatISODate, getViennaTodayISO } from "./dateUtils.js";
|
||||||
|
import { validateThesisTitle } from './titleValidation.js'
|
||||||
|
|
||||||
|
|
||||||
export const AbgabeStudentDetail = {
|
export const AbgabeStudentDetail = {
|
||||||
name: "AbgabeStudentDetail",
|
name: "AbgabeStudentDetail",
|
||||||
@@ -127,10 +129,17 @@ export const AbgabeStudentDetail = {
|
|||||||
this.$refs.modalTitelEdit.show();
|
this.$refs.modalTitelEdit.show();
|
||||||
},
|
},
|
||||||
async saveTitel() {
|
async saveTitel() {
|
||||||
const trimmed = this.editingTitel.trim();
|
|
||||||
if (!trimmed) {
|
const validation = validateThesisTitle(this.editingTitel);
|
||||||
this.$fhcAlert.alertWarning(this.$capitalize(this.$p.t('global/warningEmptyField')));
|
|
||||||
return;
|
if (!validation.isValid) {
|
||||||
|
if (validation.error === 'empty') {
|
||||||
|
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4emptyThesisTitle'))
|
||||||
|
} else if (validation.error === 'invalid_characters') {
|
||||||
|
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4invalidCharactersThesisTitle'))
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmed = await this.$fhcAlert.confirm({
|
const confirmed = await this.$fhcAlert.confirm({
|
||||||
@@ -147,14 +156,14 @@ export const AbgabeStudentDetail = {
|
|||||||
this.$api.call(
|
this.$api.call(
|
||||||
ApiAbgabe.postStudentProjektarbeitTitel(
|
ApiAbgabe.postStudentProjektarbeitTitel(
|
||||||
this.projektarbeit.projektarbeit_id,
|
this.projektarbeit.projektarbeit_id,
|
||||||
trimmed
|
validation.cleanedTitle
|
||||||
)
|
)
|
||||||
).then(res => {
|
).then(res => {
|
||||||
if (res.meta.status === 'success') {
|
if (res.meta.status === 'success') {
|
||||||
this.projektarbeit.titel = trimmed;
|
this.projektarbeit.titel = res.data;
|
||||||
this.$emit('titel-updated', {
|
this.$emit('titel-updated', {
|
||||||
projektarbeit_id: this.projektarbeit.projektarbeit_id,
|
projektarbeit_id: this.projektarbeit.projektarbeit_id,
|
||||||
titel: trimmed
|
titel: res.data
|
||||||
});
|
});
|
||||||
this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4titelSavedSuccess')));
|
this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4titelSavedSuccess')));
|
||||||
this.$refs.modalTitelEdit.hide();
|
this.$refs.modalTitelEdit.hide();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
|||||||
import BsModal from "../../Bootstrap/Modal.js";
|
import BsModal from "../../Bootstrap/Modal.js";
|
||||||
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
||||||
import { getDateStyleClass} from "./getDateStyleClass.js";
|
import { getDateStyleClass} from "./getDateStyleClass.js";
|
||||||
|
import { validateThesisTitle } from './titleValidation.js'
|
||||||
|
|
||||||
export const AbgabetoolStudent = {
|
export const AbgabetoolStudent = {
|
||||||
name: "AbgabetoolStudent",
|
name: "AbgabetoolStudent",
|
||||||
@@ -61,10 +62,16 @@ export const AbgabetoolStudent = {
|
|||||||
this.$refs.modalTitelEdit.show();
|
this.$refs.modalTitelEdit.show();
|
||||||
},
|
},
|
||||||
async saveTitel() {
|
async saveTitel() {
|
||||||
const trimmed = this.editingTitel.trim();
|
const validation = validateThesisTitle(this.editingTitel);
|
||||||
if (!trimmed) {
|
|
||||||
this.$fhcAlert.alertWarning(this.$capitalize(this.$p.t('global/warningEmptyField')));
|
if (!validation.isValid) {
|
||||||
return;
|
if (validation.error === 'empty') {
|
||||||
|
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4emptyThesisTitle'))
|
||||||
|
} else if (validation.error === 'invalid_characters') {
|
||||||
|
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4invalidCharactersThesisTitle'))
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmed = await this.$fhcAlert.confirm({
|
const confirmed = await this.$fhcAlert.confirm({
|
||||||
@@ -81,15 +88,15 @@ export const AbgabetoolStudent = {
|
|||||||
this.$api.call(
|
this.$api.call(
|
||||||
ApiAbgabe.postStudentProjektarbeitTitel(
|
ApiAbgabe.postStudentProjektarbeitTitel(
|
||||||
this.editingProjektarbeit.projektarbeit_id,
|
this.editingProjektarbeit.projektarbeit_id,
|
||||||
trimmed
|
validation.cleanedTitle
|
||||||
)
|
)
|
||||||
).then(res => {
|
).then(res => {
|
||||||
if (res.meta.status === 'success') {
|
if (res.meta.status === 'success') {
|
||||||
// update the local list entry in-place so the accordion header reflects it immediately
|
// update the local list entry in-place so the accordion header reflects it immediately
|
||||||
this.editingProjektarbeit.titel = trimmed;
|
this.editingProjektarbeit.titel = res.data;
|
||||||
// keep the open detail modal in sync if it happens to be showing this projektarbeit
|
// keep the open detail modal in sync if it happens to be showing this projektarbeit
|
||||||
if (this.selectedProjektarbeit?.projektarbeit_id === this.editingProjektarbeit.projektarbeit_id) {
|
if (this.selectedProjektarbeit?.projektarbeit_id === this.editingProjektarbeit.projektarbeit_id) {
|
||||||
this.selectedProjektarbeit.titel = trimmed;
|
this.selectedProjektarbeit.titel = res.data;
|
||||||
}
|
}
|
||||||
this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4titelSavedSuccess')));
|
this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4titelSavedSuccess')));
|
||||||
this.$refs.modalTitelEdit.hide();
|
this.$refs.modalTitelEdit.hide();
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Validates the thesis title on the frontend
|
||||||
|
* @param {string} title - The raw input from the title field
|
||||||
|
* @returns {object} Validation result containing status and cleaned title
|
||||||
|
*/
|
||||||
|
export function validateThesisTitle(title) {
|
||||||
|
if (!title) {
|
||||||
|
return { isValid: false, error: 'empty' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replicate strip_tags / trim
|
||||||
|
const cleanedTitle = title.replace(/<\/?[^>]+(>|$)/g, "").trim();
|
||||||
|
|
||||||
|
if (cleanedTitle === '') {
|
||||||
|
return { isValid: false, error: 'empty' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replicate the emoji/pictograph rejection
|
||||||
|
const emojiRegex = /\p{Extended_Pictographic}/u;
|
||||||
|
if (emojiRegex.test(cleanedTitle)) {
|
||||||
|
return { isValid: false, error: 'invalid_characters' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { isValid: true, cleanedTitle: cleanedTitle };
|
||||||
|
}
|
||||||
@@ -46941,6 +46941,46 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4emptyThesisTitle',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Projektarbeit Titel darf nicht leer sein.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Projekt work title is not allowed to be empty.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4invalidCharactersThesisTitle',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Ungültige Zeichen im Titel der Projektarbeit gefunden.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Invalid characters detected in thesis title.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
// ABGABETOOL PHRASEN END
|
// ABGABETOOL PHRASEN END
|
||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
|
|||||||
Reference in New Issue
Block a user