Fix most glaring bugs and vulnerabilities

This commit is contained in:
Luka Romih
2023-05-08 23:25:25 +02:00
parent 9867875ef2
commit d0e53fee3b
144 changed files with 2231 additions and 2981 deletions
+136 -73
View File
@@ -1,4 +1,4 @@
/* global $, axios, bootstrap, currentPagePath, initPagination, removeAllChildNodes, unsavedData, replaceContainer, i18next */
/* global $, axios, bootstrap, currentPagePath, initPagination, removeAllChildNodes, unsavedData, replaceContainer, isI18nReady, i18next, validator */
// const currentPagePath = location.pathname
@@ -144,62 +144,64 @@ function initAdmin() {
}
if (currentPagePath === '/admin/uporabniki/seznam') {
const resultsListEl = document.getElementById('page-results')
isI18nReady.then(t => {
const resultsListEl = document.getElementById('page-results')
const updatePager = initPagination('pagination', onPageChange)
const updatePager = initPagination('pagination', onPageChange)
async function onPageChange(newPage) {
try {
const { page, numberOfAllPages, results } = await getDataForPage(
newPage
)
removeAllChildNodes(resultsListEl)
renderResults(results)
updatePager(page, numberOfAllPages)
} catch (error) {
let message = i18next.t('Prišlo je do napake.')
if (error.response?.data) {
message = error.response.data
} else if (error.request) {
message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.')
async function onPageChange(newPage) {
try {
const { page, numberOfAllPages, results } = await getDataForPage(
newPage
)
removeAllChildNodes(resultsListEl)
renderResults(results)
updatePager(page, numberOfAllPages)
} catch (error) {
let message = i18next.t('Prišlo je do napake.')
if (error.response?.data) {
message = error.response.data
} else if (error.request) {
message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.')
}
alert(message)
updatePager()
}
alert(message)
updatePager()
}
}
async function getDataForPage(page) {
const url = `/api/v1/users/listAllUsers?p=${page}`
const { data } = await axios.get(url)
return data
}
async function getDataForPage(page) {
const url = `/api/v1/users/listAllUsers?p=${page}`
const { data } = await axios.get(url)
return data
}
function renderResults(results) {
results.forEach(result => {
const rowEl = document.createElement('tr')
const td1 = document.createElement('td')
const td2 = document.createElement('td')
const td3 = document.createElement('td')
const td4 = document.createElement('td')
const aEl = document.createElement('a')
const imgEl = document.createElement('img')
const spanEl = document.createElement('span')
td1.textContent = result.userName
td2.textContent = result.email
td3.textContent = result.status
aEl.classList.add('image-link')
aEl.type = 'link'
aEl.href = `/admin/uporabniki/${result.id}/urejanje`
imgEl.src = '/images/u_edit-alt.svg'
imgEl.alt = i18next.t('Uredi')
spanEl.className = 'normal-gray ms-1'
spanEl.textContent = i18next.t('Uredi')
td4.append(aEl)
aEl.append(imgEl, spanEl)
rowEl.append(td1, td2, td3, td4)
resultsListEl.appendChild(rowEl)
})
}
function renderResults(results) {
results.forEach(result => {
const rowEl = document.createElement('tr')
const td1 = document.createElement('td')
const td2 = document.createElement('td')
const td3 = document.createElement('td')
const td4 = document.createElement('td')
const aEl = document.createElement('a')
const imgEl = document.createElement('img')
const spanEl = document.createElement('span')
td1.textContent = result.userName
td2.textContent = result.email
td3.textContent = t(`userStatus${result.status}`)
aEl.classList.add('image-link')
aEl.type = 'link'
aEl.href = `/admin/uporabniki/${result.id}/urejanje`
imgEl.src = '/images/u_edit-alt.svg'
imgEl.alt = i18next.t('Uredi')
spanEl.className = 'normal-gray ms-1'
spanEl.textContent = i18next.t('Uredi')
td4.append(aEl)
aEl.append(imgEl, spanEl)
rowEl.append(td1, td2, td3, td4)
resultsListEl.appendChild(rowEl)
})
}
})
}
if (currentPagePath === '/admin/slovarji') {
@@ -280,12 +282,16 @@ function initAdmin() {
}
}
if (/\/admin\/slovarji\/\d+\/podatki/.test(currentPagePath)) {
if (
/\/admin\/slovarji\/\d+\/podatki/.test(currentPagePath) ||
/\/slovarji\/\d+\/podatki/.test(currentPagePath)
) {
const formEl = document.getElementById('admin-description')
const imgTrashIcon = document.querySelectorAll('.delete-author-btn')
const inputNewAuthorEl = document.getElementById('input-new-author')
const inputNewAreaEl = document.getElementById('input-new-area')
const dictSideMenu = document.querySelector('.admin-nav-content')
$('.without-addition').on('change', enableButton)
unsavedData(formEl, dictSideMenu)
formEl.addEventListener('input', enableButton)
if (imgTrashIcon !== null) {
@@ -946,7 +952,7 @@ function mobileMoveContent() {
// primaryButton.style.marginRight = '10px'
primaryButton.style.whiteSpace = 'nowrap'
}
navTitle.textContent = siteHeadingTextContent
if (navTitle) navTitle.textContent = siteHeadingTextContent
siteHeading.style.display = 'none'
}
if (document.body.clientWidth > 1200) {
@@ -966,12 +972,14 @@ function mobileMoveContent() {
primaryButton.style.whiteSpace = ''
}
if (
currentPagePath.includes('slovarji') &&
!currentPagePath.includes('admin')
)
navTitle.textContent = i18next.t('Urejanje')
else navTitle.textContent = i18next.t('Administracija')
if (navTitle) {
if (
currentPagePath.includes('slovarji') &&
!currentPagePath.includes('admin')
)
navTitle.textContent = i18next.t('Urejanje')
else navTitle.textContent = i18next.t('Administracija')
}
siteHeading.style.display = 'block'
}
}
@@ -1180,7 +1188,7 @@ $('.summernote').summernote({
const profileForm = document.getElementById('profileForm')
if (profileForm) {
profileForm.addEventListener('change', e => {
profileForm.addEventListener('input', e => {
enableButton()
})
@@ -1189,12 +1197,30 @@ $('.summernote').summernote({
const data = Object.fromEntries(new FormData(e.target))
const notifyAction = () => {
document.getElementById('fpi-text').innerHTML = i18next.t(
'Izpolnite vsa prazna polja.'
)
$('#reset-pass-info').modal('show')
}
if (data.numberOfHits) {
await handleUpdateHitsPerPage(data.numberOfHits)
}
if (data.name && data.surname) {
await handleUpdateUsersName(data.name, data.surname)
if (data.firstName && data.lastName && data.email) {
await handleUpdateBasicData(data)
} else if (location.pathname === '/moj-racun') {
// if missing the required data on endpoint /moj-racun, notify!
notifyAction()
return
}
if (data.passwordOld && data.passwordNew && data.passwordNewRepeat) {
await handleUpdatePassword(data)
} else if (location.pathname === '/spremeni-geslo') {
// if missing the required data on endpoint /spremeni-geslo, notify!
notifyAction()
}
// console.log(data)
@@ -1204,23 +1230,60 @@ $('.summernote').summernote({
async function handleUpdateHitsPerPage(hitAmount) {
try {
await axios.post('/api/v1/users/hitsPerPage', { hitAmount })
} catch (error) {
// console.log(error)
} finally {
location.reload(true)
} catch (error) {
displayError(error)
}
}
async function handleUpdateUsersName(name, surname) {
async function handleUpdateBasicData(payload) {
try {
await axios.post('/api/v1/users/nameAndSurname', {
name,
surname
})
} catch (error) {
// console.log(error)
} finally {
if (!validator.isEmail(payload.email)) {
document.getElementById('fpi-text').innerHTML =
i18next.t('Neveljavna e-pošta')
$('#reset-pass-info').modal('show')
return
}
await axios.post('/api/v1/users/basic-data', payload)
location.reload(true)
} catch (error) {
displayError(error)
}
}
async function handleUpdatePassword(payload) {
try {
if (payload.passwordNew !== payload.passwordNewRepeat) {
document.getElementById('fpi-text').innerHTML = i18next.t(
'Gesli se ne ujemata'
)
$('#reset-pass-info').modal('show')
return
}
if (!validator.isLength(payload.passwordNew, { min: 8 })) {
document.getElementById('fpi-text').innerHTML =
i18next.t('Geslo je prekratko')
$('#reset-pass-info').modal('show')
return
}
await axios.post('/api/v1/users/password', payload)
location.reload(true)
} catch (error) {
displayError(error)
}
}
function displayError(error) {
let message = i18next.t('Prišlo je do napake.')
if (error.response) {
message = error.response.data
} else if (error.request) {
message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.')
}
document.getElementById('fpi-text').textContent = message
$('#reset-pass-info').modal('show')
}
}
@@ -1,188 +0,0 @@
/* global axios */
// Priporočam uporabo block scopa okoli paginacijske logike za čimvečjo izolacijo.
{
// Referenca na element, kamor se izrisuje seznam rezultatov.
const resultsListEl = document.getElementById('page-results')
// Paginacijo inicializiraš s klicem funkcije initPagination:
// 1. parameter: id pager elementa. V demo-paginacija.pug je to #pagination.
// UPDATE: Sedaj sprejme tudi array idjev, če je pager kontrolerjev več (npr. en zgoraj (#pagination-top), en spodaj (#pagination-bottom)).
// 2. parameter: callback funkcijo, ki jo pager pokliče vsakič, ko uporabnik zahteva novo stran. Poliče jo s številko zahtevane strani.
// vrnjena vrednost: funkcija, ki jo (kasneje) kličeš za posodobitev pagerja. Tu jo poimenujem updateDemoPager.
const updateDemoPager = initPagination(
['pagination-top', 'pagination-bottom'],
onPageChange
)
// Za samo en kontroler je bilo:
// const updateDemoPager = initPagination('pagination', onPageChange)
// Fukncija, prejme številko nove strani in naj:
// 1. Pridobi podatke nove strani.
// 2. Izriše seznam elementov te strani.
// 3. Posodobi pager, tako, da kliče funkcijo, ki jo je vrnil klic initPagination (updateDemoPager) z novo stranjo in številom vseh strani.
async function onPageChange(newPage) {
try {
const { page, numberOfAllPages, results } = await getDataForPage(newPage)
removeAllChildNodes(resultsListEl)
renderResults(results)
updateDemoPager(page, numberOfAllPages)
} catch (error) {
let message = 'Prišlo je do napake.'
if (error.response?.data) {
message = error.response.data
} else if (error.request) {
message = 'Strežnik ni dosegljiv. Poskusite kasneje.'
}
alert(message)
updateDemoPager()
}
}
// Primer helper funkcije za pridobitev podatkov želene strani.
async function getDataForPage(page) {
const url = `/api/v1/demo-paginacija/list?p=${page}`
const { data } = await axios.get(url)
return data
}
// Primer helper funkcije za izris seznama novih podatkov.
function renderResults(results) {
results.forEach(result => {
const newListEl = document.createElement('li')
const textNode1 = document.createTextNode('Zanimiva vrednost: ')
const boldedEl = document.createElement('b')
boldedEl.textContent = result.zanimivo
const textNode2 = document.createTextNode(
`. Totalno nezanimivo: ${result.nezanimivo1} in ${result.nezanimivo2}`
)
newListEl.append(textNode1, boldedEl, textNode2)
resultsListEl.appendChild(newListEl)
})
}
}
/** ****************************************************************************************************************************************** **\
* Koda od tu navzdol za vaju ni relevantna. Tu je, da dela zgornja koda. Za realno uporabo sem je skopiral tudi v public/javascripts/scripts.js *
* Na vsaki strani, kjer bo paginacija, jo inicializiraj in uporabljaj po zgledu zgornje kode. *
\** ****************************************************************************************************************************************** **/
function initPagination(paginationRootElIds, onPageChange, currentPage = 1) {
let reqLock = false
let numOfAllPages
const backwardBtnEls = []
const forwardBtnEls = []
const pageInputEls = []
const pagesCountDisplayEls = []
if (Array.isArray(paginationRootElIds)) {
paginationRootElIds.forEach(id => initControls(id))
} else {
initControls(paginationRootElIds)
}
const allControlEls = [...backwardBtnEls, ...forwardBtnEls, ...pageInputEls]
function initControls(paginationRootElId) {
const rootEl = document.getElementById(paginationRootElId)
const btnFirstPage = rootEl.querySelector('.first-page')
const btnPreviousPage = rootEl.querySelector('.previous-page')
const btnNextPage = rootEl.querySelector('.next-page')
const btnLastPage = rootEl.querySelector('.last-page')
const formEl = rootEl.querySelector('form')
const pageInputEl = formEl.querySelector('input')
const pagesCountDisplayEl = formEl.querySelector('.pages-total')
numOfAllPages = +pagesCountDisplayEl.textContent
backwardBtnEls.push(btnFirstPage, btnPreviousPage)
forwardBtnEls.push(btnNextPage, btnLastPage)
pageInputEls.push(pageInputEl)
pagesCountDisplayEls.push(pagesCountDisplayEl)
rootEl.addEventListener('click', e => {
handleButtonClick(e, paginationRootElId)
})
formEl.addEventListener('submit', e => handleFormSubmit(e, pageInputEl))
}
function handleButtonClick({ target }, paginationRootElId) {
if (reqLock) return
const buttonEl = target.closest(`#${paginationRootElId} button`)
if (!buttonEl) return
if (buttonEl.classList.contains('first-page')) {
if (currentPage === 1) return
enableLock()
onPageChange(1)
} else if (buttonEl.classList.contains('previous-page')) {
if (currentPage === 1) return
enableLock()
onPageChange(currentPage - 1)
} else if (buttonEl.classList.contains('next-page')) {
if (currentPage === numOfAllPages) return
enableLock()
onPageChange(currentPage + 1)
} else if (buttonEl.classList.contains('last-page')) {
if (currentPage === numOfAllPages) return
enableLock()
onPageChange(numOfAllPages)
}
}
function handleFormSubmit(e, pageInputEl) {
e.preventDefault()
if (reqLock) return
const inputValue = +pageInputEl.value
if (!(inputValue > 0 && inputValue <= numOfAllPages)) {
alert('Nepravilna vrednost strani')
pageInputEl.value = currentPage
return
}
enableLock()
onPageChange(inputValue)
}
function enableLock() {
reqLock = true
allControlEls.forEach(el => (el.disabled = true))
}
function disableLock() {
reqLock = false
allControlEls.forEach(el => (el.disabled = false))
}
function updatePagerUi(newCurrentPage, newNumOfAllPages) {
disableLock()
if (!newCurrentPage) return
currentPage = newCurrentPage
pageInputEls.forEach(el => (el.value = newCurrentPage))
pagesCountDisplayEls.forEach(el => (el.textContent = newNumOfAllPages))
if (newCurrentPage === 1) {
backwardBtnEls.forEach(el => (el.disabled = true))
} else {
backwardBtnEls.forEach(el => (el.disabled = false))
}
if (newCurrentPage === newNumOfAllPages) {
forwardBtnEls.forEach(el => (el.disabled = true))
} else {
forwardBtnEls.forEach(el => (el.disabled = false))
}
}
return updatePagerUi
}
// Helper function to easily remove all child nodes. Useful for pagination.
function removeAllChildNodes(parent) {
while (parent.firstChild) {
parent.removeChild(parent.firstChild)
}
}
+21 -12
View File
@@ -845,7 +845,7 @@ function initDictionaries() {
}
}
if (listForeignSynonyms.length) {
if (el.synonym != null)
if (el.synonym != null) {
el.synonym.forEach(element => {
if (element.length) {
// eslint-disable-next-line
@@ -861,6 +861,7 @@ function initDictionaries() {
selectSyn.append(newOption).trigger('change')
}
})
}
}
}
})
@@ -2035,19 +2036,21 @@ function loadPreview(info) {
}
const languageContainers = document.querySelectorAll('.preview-one-language')
const langLine = document.querySelector('.language-line')
const langLine = document.querySelector('.start-line')
if (languageContainers) {
const arrLang = Array.from(languageContainers)
arrLang.forEach(el => {
const arrChildren = Array.from(el.children)
if (arrChildren.filter(e => e.classList.contains('d-none')).length > 2) {
el.classList.add('d-none')
langLine.classList.add('d-none')
} else {
el.classList.remove('d-none')
langLine.classList.remove('d-none')
}
} else el.classList.remove('d-none')
})
if (
arrLang.filter(el => el.classList.contains('d-none')).length >=
arrLang.length
) {
langLine.classList.add('d-none')
} else langLine.classList.remove('d-none')
}
changeCollapsedContent()
}
@@ -2110,10 +2113,10 @@ function changeVersionList(versions) {
// eslint-disable-next-line
new bootstrap.Tooltip(dateLabel, {
title:
i18next.t('Verzija') +
`${el.version} <br>` +
i18next.t('Verzija:') +
` ${el.version} <br>` +
i18next.t('Avtor:') +
`${el.version_author}`
` ${el.version_author}`
})
allDatesEl.append(dateRadio)
allDatesEl.appendChild(dateLabel)
@@ -2133,9 +2136,9 @@ function setLatestVersion(data) {
const tooltip = new bootstrap.Tooltip(latestVersionLabel, {
title:
i18next.t('Verzija:') +
`${data.version} <br>` +
` ${data.version} <br>` +
i18next.t('Avtor:') +
`${data.version_author}`,
` ${data.version_author}`,
customClass: 'dark-gray-tooltip',
html: true,
placement: 'bottom'
@@ -2823,6 +2826,12 @@ function deleteContentData(
linkType.value = 'related'
linkText.value = ''
}
if (listForeignDefinitions) {
const foreignDefinitionsFields = document.querySelectorAll(
'.foreign-definition-el'
)
foreignDefinitionsFields.forEach(el => (el.value = ''))
}
}
function activateMe(term) {
@@ -62,6 +62,10 @@
}
function renderResults(results) {
const tableContainer = document.querySelector(
'.list-terminology-candidates'
)
tableContainer.classList.remove('d-none')
results.forEach(([sequentialCount, candidate]) => {
const rowEl = document.createElement('tr')
const tdId = document.createElement('td')
@@ -197,7 +197,7 @@ function updateFileListEl(fileListEl, { status, fileStats, index }) {
deleteImgEl.alt = ''
const deleteSpanEl = document.createElement('span')
deleteSpanEl.className = 'ms-2'
deleteSpanEl.textContent = 'Briši'
deleteSpanEl.textContent = i18next.t('Briši')
const deleteButtonEl = document.createElement('button')
deleteButtonEl.className = 'p-0 delete-file delete-btn-table'
deleteButtonEl.append(deleteImgEl, deleteSpanEl)
@@ -1,128 +0,0 @@
/* global axios */
const fileUploadForm = document.forms['upload-files']
const fileInputEl = fileUploadForm.querySelector('input[type="file"]')
const messageContainerEl = document.getElementById('messages')
const filesListEl = document.getElementById('files-list')
const extractionId = +fileUploadForm.extractionId.value
let apiEndpointBase
switch (location.pathname.split('/').at(-1)) {
case 'besedila':
apiEndpointBase = `/api/v1/extraction/${extractionId}/documents`
break
case 'stop-termini':
apiEndpointBase = `/api/v1/extraction/${extractionId}/stop-terms`
break
default:
throw Error("apiEndpointBase couldn't be determined")
}
fileInputEl.addEventListener('change', submitFiles)
filesListEl.addEventListener('click', handleFileClick)
async function submitFiles() {
// TODO Lock additional submits for the duration of this function execution?
const MAX_FILE_SIZE = 10 ** 9 // 1 GB
const failedUploads = []
displaySpinner()
for (const file of fileInputEl.files) {
if (file.size > MAX_FILE_SIZE) {
const failedUpload = {
filename: file.name,
message: 'File too large. Must not be over 1 GB.'
}
failedUploads.push(failedUpload)
continue
}
const payload = new FormData()
payload.set(fileInputEl.name, file)
try {
await axios.put(apiEndpointBase, payload)
} catch (error) {
const failedUpload = {
filename: file.name,
message: error.response.data
}
failedUploads.push(failedUpload)
}
}
try {
const { data: files } = await axios.get(apiEndpointBase)
updateFilesList(files)
} catch {
alert('Pri posodobljanju seznama naloženih datotek je prišlo do napake.')
}
fileInputEl.value = ''
displayFailedUploads(failedUploads)
hideSpinner()
}
function displaySpinner() {
const messageEl = document.createElement('li')
messageEl.textContent = 'Spinner on'
messageContainerEl.appendChild(messageEl)
}
function hideSpinner() {
const messageEl = document.createElement('li')
messageEl.textContent = 'Spinner off'
messageContainerEl.appendChild(messageEl)
}
function updateFilesList(files) {
removeAllChildNodes(filesListEl)
files.forEach(({ filename, size, timeModified }) => {
const fileEl = document.createElement('li')
const filenameSpanEl = document.createElement('span')
filenameSpanEl.className = 'filename'
filenameSpanEl.textContent = filename
const formattedDate = new Date(timeModified).toLocaleDateString('sl-SL')
const deleteButtonEl = document.createElement('a')
deleteButtonEl.className = 'delete-file'
deleteButtonEl.href = '#'
deleteButtonEl.textContent = 'BRIŠI'
fileEl.append(
'DATOTEKA - Ime: ',
filenameSpanEl,
`, velikost: ${size}, datum: ${formattedDate} `,
deleteButtonEl
)
filesListEl.appendChild(fileEl)
})
}
function displayFailedUploads(failedUploads) {
failedUploads.forEach(({ filename, message }) => {
const messageEl = document.createElement('li')
messageEl.textContent = `NAPAKA - Ime datoteke: ${filename}, razlog: ${message}`
messageContainerEl.appendChild(messageEl)
})
}
async function handleFileClick(e) {
if (e.target.closest('.delete-file')) {
const fileEl = e.target.closest('li')
const filename = fileEl.querySelector('.filename').textContent
try {
await axios.delete(`${apiEndpointBase}/${filename}`)
fileEl.remove()
} catch {
alert('Pri brisanju datoteke je prišlo do napake.')
}
}
}
// Don't copy this one into final JS. It's already defined in scripts.js
function removeAllChildNodes(parent) {
while (parent.firstChild) {
parent.removeChild(parent.firstChild)
}
}
@@ -1,22 +0,0 @@
/* global axios */
const extractionListEl = document.getElementById('extraction-list')
extractionListEl.addEventListener('click', onListClick)
async function onListClick({ target }) {
if (target.classList.contains('btn-delete')) {
const extractionEl = target.closest('li')
const extractionId = extractionEl.dataset.id
await axios.delete(`/api/v1/extraction/${extractionId}`)
extractionEl.remove()
} else if (target.classList.contains('btn-begin')) {
const extractionEl = target.closest('li')
const extractionId = extractionEl.dataset.id
await axios.put(`/api/v1/extraction/${extractionId}/begin`)
} else if (target.classList.contains('btn-duplicate')) {
const extractionEl = target.closest('li')
const extractionId = extractionEl.dataset.id
await axios.post(`/api/v1/extraction/${extractionId}/duplicate`)
}
}
@@ -1,95 +0,0 @@
/* global $, axios */
$('.pick-multiple').select2()
$('.enter-multiple').select2({
tags: true
})
const editStopTermsLink = document.getElementById('edit-stop-terms')
const searchButton = document.getElementById('search-btn')
const searchResultEl = document.getElementById('search-result')
const messageContainerEl = document.getElementById('messages')
const formEl = document.forms[0]
const extractionId = +location.pathname.split('/').at(-1)
editStopTermsLink.addEventListener('click', saveOssParamsFirst)
searchButton.addEventListener('click', handleSearch)
searchResultEl.addEventListener('click', handleSearchResultsClick)
async function saveOssParamsFirst() {
const payload = new URLSearchParams(new FormData(formEl))
navigator.sendBeacon(
`/api/v1/extraction/${extractionId}/oss-save-params`,
payload
)
}
async function handleSearch() {
displaySpinner()
try {
const { data } = await submitSearch()
displaySearchResults(data)
} catch {
handleSearchError()
}
hideSpinner()
}
function displaySpinner() {
const messageEl = document.createElement('li')
messageEl.textContent = 'Spinner on'
messageContainerEl.appendChild(messageEl)
}
function hideSpinner() {
const messageEl = document.createElement('li')
messageEl.textContent = 'Spinner off'
messageContainerEl.appendChild(messageEl)
}
function handleSearchError() {
const messageEl = document.createElement('li')
messageEl.textContent = 'Notify the user of error that occured during search'
messageContainerEl.appendChild(messageEl)
}
async function submitSearch() {
const payload = new URLSearchParams(new FormData(formEl))
return await axios.put(
`/api/v1/extraction/${extractionId}/oss-search`,
payload
)
}
function displaySearchResults({ documentCount, canSave }) {
removeAllChildNodes(searchResultEl)
searchResultEl.textContent = `Število dokumentov: ${documentCount}`
if (canSave) {
const saveButton = document.createElement('button')
saveButton.id = 'save-params'
saveButton.textContent = 'Shrani'
searchResultEl.append(saveButton)
}
}
function handleSearchResultsClick({ target }) {
if (target.closest('#save-params')) confirmParams()
}
async function confirmParams() {
displaySpinner()
try {
await axios.put(`/api/v1/extraction/${extractionId}/oss-confirm-params`)
location = '../poc'
} catch {
alert('Error saving params')
hideSpinner()
}
}
// Don't copy this one into final JS. It's already defined in scripts.js
function removeAllChildNodes(parent) {
while (parent.firstChild) {
parent.removeChild(parent.firstChild)
}
}
@@ -1,144 +0,0 @@
/* global termCandidates, hitsPerPage, numberOfAllPages */
{
const resultsListEl = document.getElementById('page-results')
const updateDemoPager = initPagination('pagination', onPageChange)
function onPageChange(newPage) {
const results = getDataForPage(newPage)
removeAllChildNodes(resultsListEl)
renderResults(results)
updateDemoPager(newPage, numberOfAllPages)
}
function getDataForPage(page) {
const sliceStart = (page - 1) * hitsPerPage
const sliceEnd = page * hitsPerPage
const onePageOfTermCandidates = termCandidates.slice(sliceStart, sliceEnd)
const data = onePageOfTermCandidates.map((candidate, index) => {
const sequentialCount = sliceStart + index + 1
return [sequentialCount, candidate]
})
return data
}
function renderResults(results) {
results.forEach(([sequentialCount, candidate]) => {
const newListEl = document.createElement('li')
newListEl.textContent = `[${sequentialCount}] ${JSON.stringify(
candidate
)}`
resultsListEl.appendChild(newListEl)
})
}
}
// Below code is copied from scripts.js, so it will already be available on the real page. No need to copy it there also.
function initPagination(paginationRootElId, onPageChange, currentPage = 1) {
const rootEl = document.getElementById(paginationRootElId)
const btnFirstPage = rootEl.querySelector('.first-page')
const btnPreviousPage = rootEl.querySelector('.previous-page')
const btnNextPage = rootEl.querySelector('.next-page')
const btnLastPage = rootEl.querySelector('.last-page')
const formEl = rootEl.querySelector('form')
const pageInputEl = formEl.querySelector('input')
const pagesCountDisplayEl = formEl.querySelector('.pages-total')
let reqLock = false
rootEl.addEventListener('click', handleButtonClick)
formEl.addEventListener('submit', handleFormSubmit)
function handleButtonClick({ target }) {
if (reqLock) return
const buttonEl = target.closest(`#${paginationRootElId} button`)
if (!buttonEl) return
const numOfAllPages = +pagesCountDisplayEl.textContent
if (buttonEl.classList.contains('first-page')) {
if (currentPage === 1) return
enableLock()
onPageChange(1)
} else if (buttonEl.classList.contains('previous-page')) {
if (currentPage === 1) return
enableLock()
onPageChange(currentPage - 1)
} else if (buttonEl.classList.contains('next-page')) {
if (currentPage === numOfAllPages) return
enableLock()
onPageChange(currentPage + 1)
} else if (buttonEl.classList.contains('last-page')) {
if (currentPage === numOfAllPages) return
enableLock()
onPageChange(numOfAllPages)
}
}
function handleFormSubmit(e) {
e.preventDefault()
if (reqLock) return
const inputValue = +pageInputEl.value
if (!(inputValue > 0 && inputValue <= pagesCountDisplayEl.textContent)) {
alert('Nepravilna vrednost strani')
pageInputEl.value = currentPage
return
}
enableLock()
onPageChange(inputValue)
}
function enableLock() {
reqLock = true
btnFirstPage.disabled = true
btnPreviousPage.disabled = true
btnNextPage.disabled = true
btnLastPage.disabled = true
pageInputEl.disabled = true
}
function disableLock() {
reqLock = false
btnFirstPage.disabled = false
btnPreviousPage.disabled = false
btnNextPage.disabled = false
btnLastPage.disabled = false
pageInputEl.disabled = false
}
function updatePagerUi(newCurrentPage, newNumOfAllPages) {
disableLock()
if (!newCurrentPage) return
currentPage = newCurrentPage
pageInputEl.value = newCurrentPage
pagesCountDisplayEl.textContent = newNumOfAllPages
if (newCurrentPage === 1) {
btnFirstPage.disabled = true
btnPreviousPage.disabled = true
} else {
btnFirstPage.disabled = false
btnPreviousPage.disabled = false
}
if (newCurrentPage === newNumOfAllPages) {
btnNextPage.disabled = true
btnLastPage.disabled = true
} else {
btnNextPage.disabled = false
btnLastPage.disabled = false
}
}
return updatePagerUi
}
// Helper function to easily remove all child nodes. Useful for pagination.
function removeAllChildNodes(parent) {
while (parent.firstChild) {
parent.removeChild(parent.firstChild)
}
}
+25 -2
View File
@@ -1,11 +1,34 @@
/* global isI18nReady */
/* global isI18nReady, i18next, $, axios */
isI18nReady.then(t => {
const alertText = document.querySelector('#alert-text')
alertText.textContent = t(
'Ali želite zbrisati ta profil. S tem bodo izbrisani vsi podatki, ki ste jih ustvarili'
'Ali res želite izbrisati svoj račun? S tem boste trajno izgubili dostop do podatkov, ki ste jih ustvarili.'
)
})
const redConfirm = document.querySelector('#modal-use-btn')
redConfirm.style.backgroundColor = '#AC7171'
document.querySelector('#modal-alert-label').style.color = '#AC7171'
redConfirm.addEventListener('click', async () => {
// TODO: Optimize, create a common helper function for example
function displayError(error) {
let message = i18next.t('Prišlo je do napake.')
if (error.response) {
message = error.response.data
} else if (error.request) {
message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.')
}
document.querySelector('#info-text').textContent = message
$('#info-modal').modal('show')
}
try {
await axios.delete('/api/v1/users/current')
window.location = '/'
} catch (error) {
displayError(error)
}
})
+39 -32
View File
@@ -1,59 +1,66 @@
/* global $, axios, i18next */
/* global $, axios, validator, i18next */
// Function to verify password and repeat password
function verifyPassword() {
const password = document.getElementById('reset-password').value
const repeatPassword = document.getElementById('reset-password-repeat').value
if (password !== repeatPassword) {
alert(i18next.t('Gesli se ne ujemata')) // 'Passwords do not match')
// alert(i18next.t('Gesli se ne ujemata')) // 'Passwords do not match')
document.querySelector('#reset-password-error').textContent = i18next.t(
'Gesli se ne ujemata'
)
document.querySelector('#reset-password-error').style.visibility = 'visible'
return false
}
if (!validator.isLength(password, { min: 8 })) {
// alert(i18next.t('Geslo je prekratko')) // 'Passwords do not match')
document.querySelector('#reset-password-error').textContent =
i18next.t('Geslo je prekratko')
document.querySelector('#reset-password-error').style.visibility = 'visible'
return false
}
document.querySelector('#reset-password-error').style.visibility = 'invisible'
return true
}
// Handle submit event
document
.querySelector('#reset-and-redirect')
.addEventListener('submit', event => {
.addEventListener('submit', async event => {
event.preventDefault()
if (verifyPassword()) {
const token = document.getElementById('token').value
const password = document.getElementById('reset-password').value
const repeatPassword = document.getElementById(
const passwordRepeat = document.getElementById(
'reset-password-repeat'
).value
const token = document.getElementById('token').value
// const email = document.getElementById('email').value
// const data = { password, repeatPassword, token, email }
axios
.post('/api/v1/users/reset-passwordPLACEHOLDER_URL', {
try {
await axios.post('/api/v1/users/reset-password-submit', {
token,
password,
repeatPassword,
token
})
.then(response => {
if (response.data.success) {
$('#reset-pass-info').modal('show')
// window.location = '/'
} else {
alert(response.data.message)
}
})
.catch(error => {
// Very unlikely, but can happen
alert(i18next.t('Napaka na strežniku'))
console.log(error)
// DEBUG SUCCESS DUE TO NO ENDPOINT $('#reset-pass-info').modal('show') <<- REMOVE
passwordRepeat
})
window.location = '/'
} catch (error) {
displayError(error)
}
}
})
// Handle cancel event
document.querySelector('#cancel-btn').addEventListener('click', event => {
event.preventDefault()
window.location = '/'
})
function displayError(error) {
let message = i18next.t('Prišlo je do napake.')
if (error.response) {
message = error.response.data
} else if (error.request) {
message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.')
}
// Handle redirect on success
document.querySelector('#modal-fp-info-close').addEventListener('click', () => {
window.location = '/'
})
document.querySelector('#fpi-text.normal-gray').textContent = message
$('#reset-pass-info').modal('show')
}
+59 -79
View File
@@ -953,85 +953,45 @@ if (registerSwithcButton) {
$(document).ready(function () {
// TODO refactor wth specific select2
$('.select-search-field').select2({})
initSelect2('.select-domain-field', 'Področje')
initSelect2('.select-src-lang-field', 'Jezik iskanja')
initSelect2('.select-dest-lang-field', 'Ciljni jezik')
initSelect2('.select-dict-field', 'Slovar')
initSelect2('.select-source-field', 'Vir')
isI18nReady.then(t => {
$('.select-search-field').select2({})
$('b[role="presentation"]').hide()
$('.select2-selection__arrow').append(
'<img src="/images/chevron-down-darker.svg" alt="V"></img>'
)
initSelect2('.select-domain-field', t('Področje'))
initSelect2('.select-src-lang-field', t('Jezik iskanja'))
initSelect2('.select-dest-lang-field', t('Ciljni jezik'))
initSelect2('.select-dict-field', t('Slovar'))
initSelect2('.select-source-field', t('Vir'))
$('.select-search-field').on('select2:select', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(e.target.parentNode.children[2])
// const label = e.target.parentNode.children[2]
$('b[role="presentation"]').hide()
$('.select2-selection__arrow').append(
'<img src="/images/chevron-down-darker.svg" alt="V"></img>'
)
// console.log(activeInput)
// console.log(activeInput)
activeInput.addTag(e.params.data._resultId)
activeInput.labelJump()
})
$('.select-search-field').on('select2:select', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(e.target.parentNode.children[2])
// const label = e.target.parentNode.children[2]
$('.select-search-field').on('select2:unselect', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(activeInput)
activeInput.removeTag(e.params.data._resultId)
activeInput.labelJump()
// console.log('DELETED ' + e)
})
// console.log(activeInput)
// console.log(activeInput)
activeInput.addTag(e.params.data._resultId)
activeInput.labelJump()
})
inputs.forEach(e => {
e.input = e.domElement.querySelector('.select2-search__field')
})
})
$('.select-search-field').on('select2:unselect', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(activeInput)
activeInput.removeTag(e.params.data._resultId)
activeInput.labelJump()
// console.log('DELETED ' + e)
})
/* TODO REMOVE OTHER SCRIPTS WHEN YOU FINISH MODULARIZING THINGS THAT COULD BE MODULARIZED */
$(document).ready(function () {
// TODO refactor wth specific select2
$('.select-search-field').select2({})
initSelect2('.select-domain-field', 'Področje')
initSelect2('.select-src-lang-field', 'Jezik iskanja')
initSelect2('.select-dest-lang-field', 'Ciljni jezik')
initSelect2('.select-dict-field', 'Slovar')
initSelect2('.select-source-field', 'Vir')
$('b[role="presentation"]').hide()
$('.select2-selection__arrow').append(
'<img src="/images/chevron-down-darker.svg" alt="V"></img>'
)
$('.select-search-field').on('select2:select', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(e.target.parentNode.children[2])
// const label = e.target.parentNode.children[2]
// console.log(activeInput)
// console.log(activeInput)
activeInput.addTag(e.params.data._resultId)
activeInput.labelJump()
})
$('.select-search-field').on('select2:unselect', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(activeInput)
activeInput.removeTag(e.params.data._resultId)
activeInput.labelJump()
// console.log('DELETED ' + e)
})
inputs.forEach(e => {
e.input = e.domElement.querySelector('.select2-search__field')
inputs.forEach(e => {
e.input = e.domElement.querySelector('.select2-search__field')
})
})
})
@@ -1192,7 +1152,7 @@ const forgottenPasswordInvoker = new StateManagerInvoker(stateManager, () => {
}
function apllyDescriptionAccordingToState() {
$('#alert-text').text(stateManager.getState().fpassWindowDescription)
$('#fpi-text').text(stateManager.getState().fpassWindowDescription)
}
closeAllFPRelatedModals()
@@ -1233,8 +1193,21 @@ function onCancelForgotPassword() {
}
function onSendForgottenEmailRequest() {
if (document.getElementById('forgot-pass-input').value === '') {
stateManager.setState({
fpassWindowDescription: i18next.t(
'Prosimo vnesite vaš elektronski naslov.'
)
})
forgottenPasswordInvoker.setState({
fpassWindowState: ForgotPasswordState.FORGOT_PASSWORD_ERROR
})
return
}
axios
.post('/REPLACETHISDUMMYURL', {
.post('/api/v1/users/reset-password-init', {
usernameOrEmail: document.getElementById('forgot-pass-input').value
})
.then(res => {
@@ -1253,11 +1226,18 @@ function onSendForgottenEmailRequest() {
})
})
.catch(err => {
stateManager.setState({
fpassWindowDescription: i18next.t(
'Prišlo je do napake pri pošiljanju sporočila na vaš elektronski naslov. Poskusite ponovno.'
)
})
if (err.response && err.response.data) {
stateManager.setState({
fpassWindowDescription: err.response.data
})
} else {
stateManager.setState({
fpassWindowDescription: i18next.t(
'Prišlo je do napake pri pošiljanju sporočila na vaš elektronski naslov. Poskusite ponovno.'
)
})
}
forgottenPasswordInvoker.setState({
fpassWindowState: ForgotPasswordState.FORGOT_PASSWORD_ERROR
})
+16 -14
View File
@@ -1,7 +1,7 @@
/* global $, axios, currentPagePath, initPagination,
removeAllChildNodes, transferText, tooltipTriggerList,
tooltipList, createTooltip, resetTooltipTriggerList,
transferTextExtended, i18next */
transferTextExtended, isI18nReady, i18next */
// position correction functions
@@ -278,20 +278,22 @@ window.addEventListener('load', () => {
$('[data-toggle="tooltip"]').tooltip()
})
function handleProperTextDisplay() {
// const BROWSER_UNUSUAL_OFFSET = 17
if (/\/iskanje/.test(currentPagePath)) {
transferText('Iskanje po slovarjih', true, 'site-heading') //,
// BROWSER_UNUSUAL_OFFSET
// )
} else if (/\/termin/.test(currentPagePath)) {
transferText('', true, 'site-heading') // , BROWSER_UNUSUAL_OFFSET)
isI18nReady.then(t => {
function handleProperTextDisplay() {
// const BROWSER_UNUSUAL_OFFSET = 17
if (/\/iskanje/.test(currentPagePath)) {
transferText(t('Iskanje po slovarjih'), true, 'site-heading') //,
// BROWSER_UNUSUAL_OFFSET
// )
} else if (/\/termin/.test(currentPagePath)) {
transferText('', true, 'site-heading') // , BROWSER_UNUSUAL_OFFSET)
}
}
}
window.addEventListener('resize', () => {
window.addEventListener('resize', () => {
handleProperTextDisplay()
adjustOffsetBy()
})
handleProperTextDisplay()
adjustOffsetBy()
})
handleProperTextDisplay()