diff --git a/.gitignore b/.gitignore index d5c6fde..00d692d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ /.env .idea express/public/stylesheets +/draft/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9588c5d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Amebis, d.o.o. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 1801939..1a803ac 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,166 @@ -# Terminološki portal +# Terminology Portal -## Navodila za namesitev +## Quick Overview -Navodila so zaradi dinamičnih sprememb v integriranih zunanjih komponentah še v pripravi in bodo pripravljena, ko bo celota stabilnejša. +An opensource Terminology portal which anybody can set up (hopefully without much trouble). + +The main instance is available at [https://terminoloski.slovenscina.eu](https://terminoloski.slovenscina.eu). + +You can use your own instance completely independently or link it with other such instances, including the main one, and have them share dictionaries and their entries. + +Likewise, the included consultancy module can use its built-in, fully featured tools for your consultants to process terminology questions (default) +or simply be linked with the one at [ISJFR, ZRC SAZU](https://isjfr.zrc-sazu.si) so that their professional consultants will answer the questions. + +## Setting up your own instance of the portal + +_The guide is written for Ubuntu 22.04 LTS. Adjust according to your OS._ + +### 1. Install external dependencies first + +1. Install the [Docker Engine](https://docs.docker.com/engine/install/). +2. Install the [Term Candidate Extraction API](https://github.com/clarinsi/rsdo_luscilnik). +3. Make sure you have a SMTP server/service available, which the portal will require. +4. Install a reverse proxy (e.g. [Nginx](https://nginx.org/en/)). + +### 2. Reconfigure the docker engine + +By default, docker engine doesn't perform log rotation, which will eventually lead to disk exhaustion. +Search indexes will prevent all write operations at 90% full which will make many operations on the portal fail until +disk usage falls under 85% again. + +Also, by default, image build process is not optimized. + +To address both of the above, it is suggested to add the following to the _/etc/docker/daemon.json_ : + +``` +{ + "log-driver": "local", + "features": { + "buildkit": true + } +} +``` + +Even better, set `"log-driver": "journald"` instead, so the logs get sent straight to your journald. +Alternatively, set your own preferred log aggregator as per [the official documentation](https://docs.docker.com/config/containers/logging/configure/). + +**You need to restart the engine for the settings to take effect. Run:** `sudo systemctl restart docker.service` + +### 3. Get and configure the portal + +1. Using [git](https://git-scm.com/) transfer the portal code into desired directory with `git clone https://github.com/clarinsi/rsdo_term_portal.git ` +2. Navigate into that directory and copy the _.env_ file from the _dev_ folder one level up (into the root directory of the portal). +3. Open the copied _.env_ file in a text editor and adjust the parameters of your portal. + +### 4. (Advanced/optional) Adjust data storage locations + +By default, all data is stored and persisted inside Docker's [volumes](https://docs.docker.com/storage/volumes/) +on your host's root partition. +If you have a setup where for any reason you want to store data elsewhere, +you can reconfigure the docker-compose.prod.yml to use [bind mounts](https://docs.docker.com/storage/bind-mounts/), +just be mindful not to break shared volume dependencies. + +_Using bind mounts has not been tested yet and might cause additional complications +regarding file system permissions._ + +### 5. Start the portal service cluster + +1. Inside the portal directory, run the following command in CLI: `docker compose -f docker-compose.prod.yml --profile concordancer-manager up -d --build` +2. It's going to take some time for all the resources to get downloaded and built. + Wait for control to be returned to CLI before continuing. + +### 6. First time concordancer setup + +1. Open a bash session inside the _concordancer-manager_ container by running: + `docker exec -it $(docker ps --format "{{.Names}}" | grep concordancer-manager) bash` +2. In that session, run: `dotnet Rsdo.Concordancer.SystemManager.dll createMasterDb --connectionString="Host=postgres;Username=concordancer;Password=;Database=postgres"`. + Replace `` with the value you set for it in the _.env_ file. +3. After that, also in that session, run: `dotnet Rsdo.Concordancer.SystemManager.dll importSloleks --sourceFile=/sloleks/Sloleks2.0.LMF/sloleks_clarin_2.0.xml`. + This command takes a bit more time than the previous one to finish. +4. Close the session _(Ctrl + D)_ + +### 7. Reverse proxy setup + +1. Forward the majority of trafic to the the main port, + set appropriate headers, needed for normal functioning of the webserver + and increase the maximum allowed request body size. + Example location block for Nginx config: + +``` +location / { + proxy_set_header X-Forwarded-Host $host:$server_port; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + client_max_body_size 1100M; + proxy_pass http://127.0.0.1:; +} +``` + +Replace with whichever port you set as EXPRESS_LISTEN_PORT in the .env file, +or even change the IP address or protocol if you've done done some changes to docker-compose.prod.yml or other system wide configuration and generally know, what you're doing. + +2. Forward concordancer client trafic to the _/korpus_ path + Example location block for Nginx config: + +``` +location /korpus/ { + proxy_pass http://127.0.0.1:/; +} +``` + +Same as above, except replace with the value of CONCORDANCER_LISTEN_PORT. + +3. Don't forget setting up proper SSL termination, compression, ... + +4. Also don't forget to make your changes take effect. + For Nginx, `sudo systemctl reload nginx.service` is usually the easiest way. + +### 8. Done + +Your portal should now be available at whichever address/domain your reverse proxy is set to listen to. + +## Linking your instance with others + +1. Using a web browser, navigate to your portal's main page. +2. In the upper right corner, change language to English. + (the instructions are written using english UI labels) +3. Log in as the portal admin user. +4. Navigate Menu -> Administration -> Links -> Add link. +5. Fill in all the fields: + + - **Portal name** - your choice; it'll be displayed as the source of found entries, when the users use the portal's search tool. + + - **Portal label** - a 2 letter code of your choice; same as portal name, except a shorter version. + + - **URL (dictionaries sync)** - Format: /api/v1/system/inter-instance-sync/dictionaries + + Replace with the origin of the portal, the dictionaries of which you wish to add to your portal. + + To link the main instance of the portal, for example, you would enter: + https://terminoloski.slovenscina.eu/api/v1/system/inter-instance-sync/dictionaries + + - **URL (dictionary entries sync)** - Format: /api/v1/system/inter-instance-sync/dictionary/$SOURCE_ID/entries?lastSynced=$SINCE + + should be the same as for previous URL. + + To link the main instance of the portal, for example, you would enter: + https://terminoloski.slovenscina.eu/api/v1/system/inter-instance-sync/dictionary/$SOURCE_ID/entries?lastSynced=$SINCE + +6. Click _Add link_. You will be returned to the list of linked portals, where the newly created portal will now be listed. +7. Click _Dictionaries_ to see the list of all available dictionaries of the target portal. Enable the ones you wish to be added to your portal, click _Save_. +8. The selected dictionaries and all their entries will be automatically synchronized each night. + +## Linking to ZRC SAZU Terminological counselling + +If you wish professional counselors to answer your users' terminological questions, +you can do so by simply toggling a switch in the administration console. + +1. As previously described, navigate to the Menu -> Administration -> Basic settings -> Consultancy. +2. Select _ZRC SAZU Terminological counselling_. +3. Click Save. + +That's it. +Your questions will now be automatically forwarded to ZRC SAZU Terminological counselling +and their answers back to your portal. diff --git a/concordancer/Dockerfile b/concordancer/Dockerfile index 2a70c6c..67ab6ad 100644 --- a/concordancer/Dockerfile +++ b/concordancer/Dockerfile @@ -2,5 +2,5 @@ FROM alpine:3.16 AS builder WORKDIR /sloleks RUN wget -qO- "https://www.clarin.si/repository/xmlui/bitstream/handle/11356/1230/Sloleks2.0.LMF.zip?sequence=3&isAllowed=y" | unzip - -FROM ghcr.io/clarinsi/rsdo-concordancer-api:v1.0.0 +FROM ghcr.io/clarinsi/rsdo-concordancer-api:v1.0.3 COPY --from=builder /sloleks /sloleks diff --git a/concordancer/Dockerfile.prod b/concordancer/Dockerfile.prod new file mode 100644 index 0000000..c565830 --- /dev/null +++ b/concordancer/Dockerfile.prod @@ -0,0 +1,6 @@ +FROM alpine:3.16 AS builder +WORKDIR /sloleks +RUN wget -qO- "https://www.clarin.si/repository/xmlui/bitstream/handle/11356/1230/Sloleks2.0.LMF.zip?sequence=3&isAllowed=y" | unzip - + +FROM ghcr.io/clarinsi/rsdo-concordancer-api-term-portal:v1.0.3 +COPY --from=builder /sloleks /sloleks diff --git a/dev/.env b/dev/.env index d148165..9246be4 100644 --- a/dev/.env +++ b/dev/.env @@ -1,26 +1,66 @@ -# Initial values are default vaules. -# Change if needed. +# PORTAL PARAMETERS +# Initial values are default vaules. +# Changing at least all passwords and secrets is strongly advised. +# Preferably at least 16 chars longs and randomly generated. + + +# Initial values for the admin user (portal administrator role), +# that will be created on first startup. +PORTAL_ADMIN_INITIAL_EMAIL="admin@email.com" +PORTAL_ADMIN_INITIAL_PASSWORD="admin" + + +# The endpoint where you've made Term Candidate Extraction API available. +# It is required for the extraction module to work properly. +EXTRACTION_API_ORIGIN="http://rsdo.lhrs.feri.um.si:8080" + + +# The main port, which will be exposed on localhost of the of host running the Docker Engine. +# If you need to expose it on all of host's network interfaces, +# modify docker-compose.prod.yml in project's root directory where it is used. EXPRESS_LISTEN_PORT=3000 +# Set it to true for production and properly configure a reverse proxy in front of it. EXPRESS_IS_BEHIND_PROXY=false +# It is used for cookie signing. EXPRESS_SECRET="weak_secret" + +# Used for administration. POSTGRES_ADMIN_PASSWORD="weak_admin_password" # user: postgres +# Used by the express webserver service. POSTGRES_EXPRESS_PASSWORD="weak_express_password" # user: express +# Used by the concordancer service. POSTGRES_CONCORDANCER_PASSWORD="weak_concordancer_password" # user: concordancer +# Exposed on host's localhost for administration purposes. POSTGRES_LISTEN_PORT=5432 + +# Configuration for webserver to communicate with your SMTP provider. SMTP_HOST="maildev" SMTP_PORT=1025 -SMTP_TLS_REJECT_UNAUTHORIZED=true +SMTP_USER="" +SMTP_PASSWORD="" +SMTP_SECURE=false # Most often only "true" for port 465. +SMTP_REQUIRE_TLS=false # If "true" and SMTP_SECURE is "false", only send messages if server supports STARTTLS. +SMTP_TLS_ALLOW_INVALID_CERTS=false SMTP_FROM="Sender name " + +# Exposed on host's localhost for search index administration purposes. +# Used only if docker compose was ran with opensearch-dashboards profile. OS_DASHBOARDS_LISTEN_PORT=3002 + +# The second port, which will be exposed on localhost of the of host running the Docker Engine. +# Used by the concordancer client. CONCORDANCER_LISTEN_PORT=3003 -# Development only settings -MAILDEV_WEB_GUI_PORT=3001 # Production only settings +# The origin, where the portal will be made available. URL_ORIGIN="https://mywebportal.com" + + +# Development only settings (not used in production) +MAILDEV_WEB_GUI_PORT=3001 diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 1914a2d..1f8265e 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -21,16 +21,23 @@ services: PGDATABASE: term_portal SMTP_HOST: "${SMTP_HOST:?}" SMTP_PORT: "${SMTP_PORT:?}" - SMTP_TLS_REJECT_UNAUTHORIZED: "${SMTP_TLS_REJECT_UNAUTHORIZED:?}" + SMTP_USER: "${SMTP_USER?}" + SMTP_PASSWORD: "${SMTP_PASSWORD?}" + SMTP_SECURE: "${SMTP_SECURE:?}" + SMTP_REQUIRE_TLS: "${SMTP_REQUIRE_TLS:?}" + SMTP_TLS_ALLOW_INVALID_CERTS: "${SMTP_TLS_ALLOW_INVALID_CERTS:?}" SMTP_FROM: "${SMTP_FROM:?}" ORIGIN: "${URL_ORIGIN:?}" + PORTAL_ADMIN_INITIAL_EMAIL: "${PORTAL_ADMIN_INITIAL_EMAIL:?}" + PORTAL_ADMIN_INITIAL_PASSWORD: "${PORTAL_ADMIN_INITIAL_PASSWORD:?}" + EXTRACTION_API_ORIGIN: "${EXTRACTION_API_ORIGIN:?}" volumes: - express-data:/usr/src/app/data_files ports: - "127.0.0.1:${EXPRESS_LISTEN_PORT:?}:3000" postgres: - image: postgres:15-alpine + image: postgres:15.1-alpine3.17 restart: always environment: POSTGRES_PASSWORD: "${POSTGRES_ADMIN_PASSWORD:?}" @@ -58,18 +65,18 @@ services: environment: - cluster.name=term-portal - node.name=node-1 - # - bootstrap.memory_lock=true # along with the memlock settings below, disables swapping - # - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM + - bootstrap.memory_lock=true # along with the memlock settings below, disables swapping + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM - "DISABLE_INSTALL_DEMO_CONFIG=true" # disables execution of install_demo_configuration.sh bundled with security plugin, which installs demo certificates and security configurations to OpenSearch - "DISABLE_SECURITY_PLUGIN=true" # disables security plugin entirely in OpenSearch by setting plugins.security.disabled: true in opensearch.yml - "discovery.type=single-node" # disables bootstrap checks that are enabled when network.host is set to a non-loopback address - # ulimits: - # memlock: - # soft: -1 - # hard: -1 - # nofile: - # soft: 65536 # maximum number of open files for the OpenSearch user, set to at least 65536 on modern systems - # hard: 65536 + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 # maximum number of open files for the OpenSearch user, set to at least 65536 on modern systems + hard: 65536 volumes: - opensearch-data:/usr/share/opensearch/data @@ -88,8 +95,10 @@ services: - "127.0.0.1:${OS_DASHBOARDS_LISTEN_PORT:?}:5601" concordancer: - # image: ghcr.io/clarinsi/rsdo-concordancer-api:v1.0.0 - build: concordancer + # image: ghcr.io/clarinsi/rsdo-concordancer-api-term-portal:v1.0.3 + build: + context: concordancer + dockerfile: Dockerfile.prod depends_on: - postgres - opensearch @@ -106,7 +115,7 @@ services: # Disabled by default. Enable if needed. concordancer-manager: - image: ghcr.io/clarinsi/rsdo-concordancer-systemmanager:v1.0.0 + image: ghcr.io/clarinsi/rsdo-concordancer-systemmanager:v1.0.3 profiles: - concordancer-manager depends_on: @@ -137,7 +146,11 @@ services: PGDATABASE: term_portal SMTP_HOST: "${SMTP_HOST:?}" SMTP_PORT: "${SMTP_PORT:?}" - SMTP_TLS_REJECT_UNAUTHORIZED: "${SMTP_TLS_REJECT_UNAUTHORIZED:?}" + SMTP_USER: "${SMTP_USER?}" + SMTP_PASSWORD: "${SMTP_PASSWORD?}" + SMTP_SECURE: "${SMTP_SECURE:?}" + SMTP_REQUIRE_TLS: "${SMTP_REQUIRE_TLS:?}" + SMTP_TLS_ALLOW_INVALID_CERTS: "${SMTP_TLS_ALLOW_INVALID_CERTS:?}" SMTP_FROM: "${SMTP_FROM:?}" entrypoint: ["scheduled/entrypoint.sh"] command: ["crond", "-f", "-l", "2"] diff --git a/docker-compose.yml b/docker-compose.yml index 047b69e..4623294 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,9 +20,16 @@ services: PGDATABASE: term_portal SMTP_HOST: "${SMTP_HOST:?}" SMTP_PORT: "${SMTP_PORT:?}" - SMTP_TLS_REJECT_UNAUTHORIZED: "${SMTP_TLS_REJECT_UNAUTHORIZED:?}" + SMTP_USER: "${SMTP_USER?}" + SMTP_PASSWORD: "${SMTP_PASSWORD?}" + SMTP_SECURE: "${SMTP_SECURE:?}" + SMTP_REQUIRE_TLS: "${SMTP_REQUIRE_TLS:?}" + SMTP_TLS_ALLOW_INVALID_CERTS: "${SMTP_TLS_ALLOW_INVALID_CERTS:?}" SMTP_FROM: "${SMTP_FROM:?}" ORIGIN: http://localhost:${EXPRESS_LISTEN_PORT:?} + PORTAL_ADMIN_INITIAL_EMAIL: "${PORTAL_ADMIN_INITIAL_EMAIL:?}" + PORTAL_ADMIN_INITIAL_PASSWORD: "${PORTAL_ADMIN_INITIAL_PASSWORD:?}" + EXTRACTION_API_ORIGIN: "${EXTRACTION_API_ORIGIN:?}" volumes: # When developing/debugging concordancer, disable express bind mount and node_modules volume and enable express-data. # - express-data:/usr/src/app/data_files @@ -35,7 +42,7 @@ services: tty: true postgres: - image: postgres:15-alpine + image: postgres:15.1-alpine3.17 # Use any of the two custom Dockerfiles below for debugging PL/pgSQL functions. # Disable docker-entrypoint-initdb.d bind mount when doing so. # build: @@ -95,7 +102,7 @@ services: - "127.0.0.1:${OS_DASHBOARDS_LISTEN_PORT:?}:5601" concordancer: - # image: ghcr.io/clarinsi/rsdo-concordancer-api:v1.0.0 + # image: ghcr.io/clarinsi/rsdo-concordancer-api:v1.0.3 build: concordancer profiles: - develop-concordancer @@ -114,7 +121,7 @@ services: - sloleks:/sloleks concordancer-manager: - image: ghcr.io/clarinsi/rsdo-concordancer-systemmanager:v1.0.0 + image: ghcr.io/clarinsi/rsdo-concordancer-systemmanager:v1.0.3 profiles: - develop-concordancer depends_on: @@ -146,7 +153,11 @@ services: # PGDATABASE: term_portal # SMTP_HOST: "${SMTP_HOST:?}" # SMTP_PORT: "${SMTP_PORT:?}" - # SMTP_TLS_REJECT_UNAUTHORIZED: "${SMTP_TLS_REJECT_UNAUTHORIZED:?}" + # SMTP_USER: "${SMTP_USER?}" + # SMTP_PASSWORD: "${SMTP_PASSWORD?}" + # SMTP_SECURE: "${SMTP_SECURE:?}" + # SMTP_REQUIRE_TLS: "${SMTP_REQUIRE_TLS:?}" + # SMTP_TLS_ALLOW_INVALID_CERTS: "${SMTP_TLS_ALLOW_INVALID_CERTS:?}" # SMTP_FROM: "${SMTP_FROM:?}" # volumes: # - /usr/src/app/node_modules diff --git a/express/app.js b/express/app.js index c97fe8f..3f7912c 100644 --- a/express/app.js +++ b/express/app.js @@ -6,12 +6,15 @@ const helmet = require('helmet') const favicon = require('serve-favicon') const cookieParser = require('cookie-parser') const createError = require('http-errors') -const debug = require('debug')('termPortal:app') +const i18next = require('i18next') +const i18nextMiddleware = require('i18next-http-middleware') +// const debug = require('debug')('termPortal:app') // Import own modules. const { isBehindProxy, secret } = require('./config/keys') const helmetConfig = require('./config/helmet') const session = require('./middleware/session') +const i18n = require('./middleware/i18n') const passport = require('./middleware/auth') const user = require('./middleware/user') const settings = require('./middleware/settings') @@ -37,6 +40,7 @@ app.locals.basedir = viewsPath // Other settings. const inDevEnv = app.get('env') === 'development' if (isBehindProxy) app.set('trust proxy', 1) // Trust first proxy. +app.locals.inDevEnv = inDevEnv // Mount middleware. app.use(logger('dev')) @@ -50,10 +54,19 @@ app.use(session) app.use(passport.initialize()) app.use(passport.session()) app.use(passport.authenticate('remember-me')) +app.use(i18n.determineRequestLanguage) +app.use(i18nextMiddleware.handle(i18next)) app.use(user.enhance) app.use(settings.prepareRequiredSettings) app.use(enhanceLocals) +if (inDevEnv) { + app.post( + '/locales/add/:lng/:ns', + i18nextMiddleware.missingKeyHandler(i18next) + ) +} + // Apparently express-debug can't be run inside another middleware and must be run in this file. // To help you debug, temporarily uncomment the next line, but comment the helmet line due to strict CSP. // if (app.get('env') === 'development') require('express-debug')(app) @@ -75,7 +88,8 @@ app.use((req, res, next) => next(createError(404))) // Error handler. app.use((err, req, res, next) => { - if (err.status !== 404) debug(err) + // eslint-disable-next-line no-console + if (err.status !== 404) console.error(err) // Set error info to be displayed to user depending on environment. let message, error @@ -86,8 +100,8 @@ app.use((err, req, res, next) => { } else { message = err.status === 404 - ? 'Stran ne obstaja' - : 'Prišlo je do strežniške napake. Poskusite kasneje.' + ? req.t('Stran ne obstaja') + : req.t('Prišlo je do strežniške napake. Poskusite kasneje.') error = {} } @@ -99,7 +113,7 @@ app.use((err, req, res, next) => { } // Render the error page. - res.render('error', { title: 'Napaka', message, error }) + res.render('error', { title: req.t('Napaka'), message, error }) }) module.exports = app diff --git a/express/bin/www b/express/bin/www index a0b2311..dfac841 100755 --- a/express/bin/www +++ b/express/bin/www @@ -4,12 +4,16 @@ * Module dependencies. */ +// Temporary noop function to be used until i18n is fully in place. +global.__ = str => str + const db = require('../models/db') const cache = require('../models/cache') const searchEngine = require('../models/search-engine') const email = require('../models/email') +const init = require('../config/init') const { seedDummyData } = require('../models/comment') -const { initDemoData } = require('../models/demo-paginacija') +// const { initDemoData } = require('../models/demo-paginacija') const app = require('../app') const debug = require('debug')('termPortal:server') const http = require('http') @@ -37,7 +41,8 @@ const server = http.createServer(app) db.waitForConnection(), cache.waitForConnection(), searchEngine.waitForConnection(), - email.waitForConnection() + email.waitForConnection(), + init() ]) await searchEngine.initEntryIndex() await searchEngine.initConsultancyEntryIndex() @@ -102,5 +107,7 @@ function onError(error) { function onListening() { const addr = server.address() const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port + // eslint-disable-next-line no-console + console.log('Started listening') debug('Listening on ' + bind) } diff --git a/express/config/init.js b/express/config/init.js new file mode 100644 index 0000000..b88529f --- /dev/null +++ b/express/config/init.js @@ -0,0 +1,6 @@ +const { mkdir } = require('fs/promises') +const { TEMP_EXPORT_PATH } = require('./settings') + +module.exports = async () => { + await mkdir(TEMP_EXPORT_PATH, { recursive: true }) +} diff --git a/express/config/keys.js b/express/config/keys.js index 52dd4b4..c294cf2 100644 --- a/express/config/keys.js +++ b/express/config/keys.js @@ -4,8 +4,14 @@ module.exports = { cookiesSecure: process.env.COOKIES_SECURE === 'true', smtpHost: process.env.SMTP_HOST, smtpPort: process.env.SMTP_PORT, - smtpTlsRejectUnauthorized: - process.env.SMTP_TLS_REJECT_UNAUTHORIZED === 'true', + smtpUser: process.env.SMTP_USER, + smtpPassword: process.env.SMTP_PASSWORD, + smtpSecure: process.env.SMTP_SECURE === 'true', + smtpRequireTls: process.env.SMTP_REQUIRE_TLS === 'true', + smtpAllowInvalidCerts: process.env.SMTP_TLS_ALLOW_INVALID_CERTS === 'true', smtpFrom: process.env.SMTP_FROM, - origin: process.env.ORIGIN + origin: process.env.ORIGIN, + portalAdminInitialEmail: process.env.PORTAL_ADMIN_INITIAL_EMAIL, + portalAdminInitialPassword: process.env.PORTAL_ADMIN_INITIAL_PASSWORD, + extractionApiOrigin: process.env.EXTRACTION_API_ORIGIN } diff --git a/express/config/settings.js b/express/config/settings.js index 60459eb..0703e36 100644 --- a/express/config/settings.js +++ b/express/config/settings.js @@ -16,7 +16,11 @@ exports.DEFAULT_HITS_PER_PAGE = 10 exports.EDITOR_MAX_HITS = 10000 -// If you change this one, don't forget to also update the volume mount in docker-compose.prod.yml. -exports.DATA_FILES_PATH = 'data_files' +// If you change this one, don't forget to also update the volume mount in docker-compose.prod.yml +// and nodemon ignore flag in package.json scripts. +const DATA_FILES_PATH = 'data_files' +exports.DATA_FILES_PATH = DATA_FILES_PATH + +exports.TEMP_EXPORT_PATH = `${DATA_FILES_PATH}/export_temp` exports.MAX_EXTRACTIONS_PER_USER = 5 diff --git a/express/controllers/api/v1/consultancy.js b/express/controllers/api/v1/consultancy.js index 62c7360..5199a8f 100644 --- a/express/controllers/api/v1/consultancy.js +++ b/express/controllers/api/v1/consultancy.js @@ -1,11 +1,18 @@ const ConsultancyEntry = require('../../../models/consultancy-entry') +const Domain = require('../../../models/domain') const User = require('../../../models/user') const { promisify } = require('util') +const i18next = require('i18next') const { deleteConsultancyEntryFromIndex } = require('../../../models/search-engine') const email = require('../../../models/email') const helper = require('../../../models/helpers') +const { getInstanceSetting } = require('../../../models/helpers') +const { searchConsultancyEntryIndex } = require('../../../models/search-engine') +const { prepareConsultancyEntries } = require('../../../models/helpers/search') +const { DEFAULT_HITS_PER_PAGE } = require('../../../config/settings') +const generateQuery = require('../../../models/helpers/search/generate-query') const consultancy = {} @@ -23,6 +30,43 @@ consultancy.listNewEntries = async (req, res) => { res.send(data) } +consultancy.sendPaginationData = async (req, res) => { + let requestType = req.query.type + const isAdminPage = req.query.isAdmin === 'true' + let page = +req.query.p || 1 + + if (page < 1) { + page = 1 + } + + if (isAdminPage) { + if ( + req.user && + (req.user.hasRole('portal admin') || + req.user.hasRole('consultancy admin')) + ) { + // Everything is allowed, nothing to do here + } else if (req.user && req.user.hasRole('consultant')) { + if (!(requestType === 'in progress' || requestType === 'published')) { + return res.status(400).send() + } + } else { + // for now, public can only see published entries + requestType = 'published' + } + } + + return await consultancyRequestItems( + req, + res, + requestType || 'published', + 'components/consultancy/api/consultancy-item-rendered', + isAdminPage, + !isAdminPage, // -> In current implementation, it is just the inverse of isAdminPage + page + ) +} + consultancy.createQuestion = async (req, res) => { const q = req.body const consultancyEntry = {} @@ -35,7 +79,7 @@ consultancy.createQuestion = async (req, res) => { const { description } = q if (!description) { - return res.status(400).send('description is a required parameter!') + return res.status(400).send('Description is a required parameter!') } consultancyEntry.description = description @@ -52,24 +96,46 @@ consultancy.createQuestion = async (req, res) => { consultancyEntry[key] = helper.removeHtmlTags(consultancyEntry[key]).trim() }) - const questionId = await ConsultancyEntry.createQuestion(consultancyEntry) - await ConsultancyEntry.indexIntoSearchEngine(questionId, true) + const isOwnConsultancyEnabled = + (await getInstanceSetting('consultancy_type')) === 'own' - // TODO SEND EMAIL - // TODOOOOOOOOO + let domainNameSl + if (consultancyEntry.domainPrimaryIdInitial) { + domainNameSl = ( + await Domain.fetchById(consultancyEntry.domainPrimaryIdInitial) + ).nameSl + } else { + domainNameSl = '' + } - const emails = await ConsultancyEntry.fetchConsultancyAdminEmails() + let emails + let subjectText + if (isOwnConsultancyEnabled) { + const questionId = await ConsultancyEntry.createQuestion(consultancyEntry) + await ConsultancyEntry.indexIntoSearchEngine(questionId, true) + subjectText = req.t('Ustvarjeno novo vprašanje v svetovalnici') + emails = await ConsultancyEntry.fetchConsultancyAdminEmails() + } else { + subjectText = req.t('Novo vprašanje za Terminološko svetovalnico') + emails = await getInstanceSetting('zrc_email') + } const renderAsync = promisify(req.app.render.bind(req.app)) const emailHtml = await renderAsync('email/consultancy-creation-notify', { - propertyToPassGoesHere: 'test1234' + nameAndSurname: `${res.locals.user.firstName} ${res.locals.user.lastName}`, + email: res.locals.user.email, + domain: domainNameSl, + institution: consultancyEntry.institution, + question: consultancyEntry.description, + existingSolutions: consultancyEntry.existingSolutions, + examplesOfUse: consultancyEntry.examplesOfUse }) + // TODO i18n - What language are the email title and content (we already have email translated) await email.send({ to: emails, - subject: 'Ustvarjeno novo vprašanje v svetovalnici', + subject: subjectText, html: emailHtml }) - /// ///////////////// res.status(201).send() } @@ -179,9 +245,10 @@ consultancy.assign = async (req, res) => { const renderAsync = promisify(req.app.render.bind(req.app)) const emailHtml = await renderAsync('email/consultancy-assigned') + // TODO i18n - What language are the email title and content (we already have email translated) await email.send({ to: emails, - subject: 'Novo terminološko vprašanje', + subject: req.t('Novo terminološko vprašanje'), html: emailHtml }) @@ -234,9 +301,10 @@ consultancy.sendToReview = async (req, res) => { const renderAsync = promisify(req.app.render.bind(req.app)) const emailHtml = await renderAsync('email/consultancy-item-review') + // TODO i18n - What language are the email title and content (we already have email translated) await email.send({ to: emails, - subject: 'Potrditev objave', + subject: req.t('Potrditev objave'), html: emailHtml }) @@ -254,6 +322,19 @@ consultancy.publish = async (req, res) => { if (!entry.title) { return res.status(400).send('Answer not completed') } + const author = await User.fetchUser(entry.authorId) + const portalName = await getInstanceSetting(`portal_name_${author.language}`) + const renderAsync = promisify(req.app.render.bind(req.app)) + const emailHtml = await renderAsync('email/consultancy-publish-notify', { + portalName + }) + await email.send({ + to: author.email, + subject: i18next.t('Objava terminološkega vprašanja', { + lng: author.language + }), + html: emailHtml + }) await ConsultancyEntry.publish(questionId, answerAuthors) await ConsultancyEntry.indexIntoSearchEngine(questionId, true) @@ -267,7 +348,9 @@ consultancy.updateQuestion = async (req, res) => { if (!id) return res.status(400).send({}) if (questionTitle === '' || question === '' || answer === '') { - return res.status(422).send('Polja naslov, vprašanje in mnenje so obvezna!') + return res + .status(422) + .send(req.t('Polja naslov, vprašanje in mnenje so obvezna!')) } const entry = await ConsultancyEntry.fetchById(id) @@ -318,4 +401,157 @@ consultancy.deleteQuestion = async (req, res) => { res.send() } +async function consultancyRequestItems( + req, + res, + type, + url, + isAdminPage = false, + privilegeToSeAll = true, // this method seperates consultancy main from admin, so all results get visible TO ALL REGISTERED USERS, not just admins + page +) { + const searchString = req.query.q?.trim() ?? '' + + /// ////////////////////////////////////////// + // let allPrimaryDomains = await Dictionary.fetchAllPrimaryDomains() + + /// filter selected domains for the prompt /// + // Not duplicates of this code arise... + // const pdList = intoDbArray(req.query.pd, 'always') + + // allPrimaryDomains = allPrimaryDomains.map(entry => { + // if (pdList.includes(`${entry.id}`)) { + // entry.selected = true + // } + // return entry + // }) + /// ////////////////////////////////////////// + + let assignedConsultant + + if ( + !privilegeToSeAll && + req.user && + !(req.user.hasRole('portal admin') || req.user.hasRole('consultancy admin')) + ) { + assignedConsultant = req.user.id + } else { + assignedConsultant = undefined + } + + const filters = { + status: type, + assignedConsultant, + primaryDomain: req.query.pd + } + + if (!isAdminPage) { + filters.assignedConsultant = undefined + filters.status = 'published' // guard + } + + const hitsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE + + // const page = +req.query.p > 0 ? +req.query.p : 1 + + const hitsQuery = generateQuery.consultancy( + searchString, + filters, + hitsPerPage, + page + ) + + const hits = await searchConsultancyEntryIndex(hitsQuery) + + const numberOfAllHits = hits.body.hits.total.value + const numberOfAllPages = Math.ceil(numberOfAllHits / hitsPerPage) + + let entries = prepareConsultancyEntries(hits) + // console.log({ entries, numberOfAllHits, numberOfAllPages }) + + entries = entries.map(entry => { + entry.primaryDomain = entry.primaryDomain + ? entry.primaryDomain.nameSl + : req.t('nedefinirano') + + if (entry.assignedConsultants) { + entry.firstName = entry.assignedConsultants[0]?.firstName + entry.lastName = entry.assignedConsultants[0]?.lastName + } + + if (entry.assignedConsultants && entry.assignedConsultants.length > 1) { + entry.sharedAuthors = [] + for (let i = 1; i < entry.assignedConsultants.length; i++) { + // skip first element (moderator) + entry.sharedAuthors.push( + `${entry.assignedConsultants[i].firstName} ${entry.assignedConsultants[i].lastName}` + ) + } + } + + if (entry.timeCreated) { + // TODO i18n date format + const date = new Date(entry.timeCreated) + + entry.formattedTimeCreated = `${date.getDate()}. ${ + date.getMonth() + 1 + }. ${date.getFullYear()}` + } + + return entry + }) + + // const entryList = await mapEntryList(inProgressEntryList) + const userList = await User.fetchConsultants() + + if (!isAdminPage) { + entries.map(entry => { + const MAX_CHARACTER_LENGTH = 200 + let appendAnswer = '' + let appendQuestion = '' + if (entry.answer && entry.answer.length > MAX_CHARACTER_LENGTH) { + appendAnswer = '...' + } + if (entry.question && entry.question.length > MAX_CHARACTER_LENGTH) { + appendQuestion = '...' + } + + entry.answerSummary = `${entry.answer.slice( + 0, + MAX_CHARACTER_LENGTH + )}${appendAnswer}` + + entry.question = `${entry.question.slice( + 0, + MAX_CHARACTER_LENGTH + )}${appendQuestion}` + + return entry + }) + } + + res.append('page', page) + res.append('number-of-all-pages', numberOfAllPages) + res.render(url, { + entries, // entryList, + userList, + numberOfAllPages, + isAdminPage, + section: consultancyAdminPageMapper(type), + queryCount: numberOfAllHits + }) +} + +// mapping required due to the inconsistent naming convention +function consultancyAdminPageMapper(type) { + if (type === 'in progress') { + return { inProgress: true } + } + if (type === 'review') { + return { prepared: true } + } + + return { type: true } +} + module.exports = consultancy diff --git a/express/controllers/api/v1/dictionaries.js b/express/controllers/api/v1/dictionaries.js index dc73630..4cac5e1 100644 --- a/express/controllers/api/v1/dictionaries.js +++ b/express/controllers/api/v1/dictionaries.js @@ -1,5 +1,7 @@ +const { rm } = require('fs/promises') const Dictionary = require('../../../models/dictionary') const Entry = require('../../../models/entry') +const Extraction = require('../../../models/extraction') const { searchEntryIndex, deleteEntryFromIndex, @@ -9,6 +11,7 @@ const genEditorAllQuery = require('../../../models/helpers/search/generate-query const { prepareEditorEntries } = require('../../../models/helpers/search') const { DEFAULT_HITS_PER_PAGE } = require('../../../config/settings') const { minEntriesRequirementCheckAndAct } = require('../../helpers/dictionary') +const { getExportFilesPath } = require('../../../models/helpers/dictionary') const dictionary = {} @@ -115,6 +118,8 @@ dictionary.delete = async (req, res) => { const dictionaryId = +req.params.dictionaryId await Dictionary.delete(dictionaryId) await deleteDictionaryEntriesFromIndex(dictionaryId) + const exportFilesPath = getExportFilesPath(dictionaryId) + await rm(exportFilesPath, { recursive: true, force: true }) res.end() } @@ -167,6 +172,56 @@ dictionary.listDomainLabels = async (req, res) => { res.send({ page, numberOfAllPages, results }) } +dictionary.listFilteredDomainLabels = async (req, res) => { + const resultsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE + const { dictionaryId } = req.params + const page = +req.query.p > 0 ? +req.query.p : 1 + + let { q } = req.query + + if (!q) { + q = '' + } + + const { pages_total: numberOfAllPages, results } = + await Dictionary.fetchFilteredPaginationDomainLabels( + dictionaryId, + q, + resultsPerPage, + page + ) + + res.append('page', page) + res.append('number-of-all-pages', numberOfAllPages) + res.render('utilities/response-pug-wrapper/domainLabelLister', { + dictionary: { id: dictionaryId }, + numberOfAllPages, + results + }) +} + +dictionary.listSecondaryDomainData = async (req, res) => { + const resultsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE + let { q } = req.query + const page = +req.query.p > 0 ? +req.query.p : 1 + + if (!q) { + q = '' + } + + const { pages_total: numberOfAllPages, results } = + await Dictionary.fetchFilteredSecondaryDomains(q, resultsPerPage, page) + + res.append('page', page) + res.append('number-of-all-pages', numberOfAllPages) + + res.render('utilities/response-pug-wrapper/secondaryDomainLister', { + page, + numberOfAllPages, + results + }) +} + dictionary.listSecondaryDomains = async (req, res) => { const resultsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE const page = +req.query.p > 0 ? +req.query.p : 1 @@ -176,14 +231,92 @@ dictionary.listSecondaryDomains = async (req, res) => { res.send({ page, numberOfAllPages, results }) } -dictionary.extractionImport = async (req, res) => { - // TODO Import logic (Luka's task) - // const { id: dictionaryId, extractionId } = req.params - // const { from, to } = req.query - // const fromIndex = +from > 1 ? Math.floor(from) - 1 : 0 - // const toIndex = Number.isInteger(+to) ? Math.abs(to) : undefined - // console.log({ dictionaryId, extractionId, fromIndex, toIndex }) - res.send('IMPORTING') +dictionary.showImportFromFileForm = async (req, res) => { + const resultsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE + const { dictionaryId } = req.params + const page = +req.query.p > 0 ? +req.query.p : 1 + const { pages_total: numberOfAllPages, results } = + await Dictionary.fetchAllImports(dictionaryId, resultsPerPage, page) + + res.send({ page, numberOfAllPages, results }) +} + +dictionary.showExportToFileForm = async (req, res) => { + const resultsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE + const { dictionaryId } = req.params + const page = +req.query.p > 0 ? +req.query.p : 1 + const { pages_total: numberOfAllPages, results } = + await Dictionary.fetchExports(dictionaryId, resultsPerPage, page) + + res.send({ page, numberOfAllPages, results }) +} + +dictionary.importFromExtraction = async (req, res) => { + const { id: dictionaryId, extractionId } = req.params + const { from, to } = req.body + const fromIndex = +from > 1 ? Math.floor(from) - 1 : 0 + const toIndex = Number.isInteger(+(to === '' ? undefined : to)) + ? Math.abs(to) + : undefined + + // TODO Authentication, authorization, validation. + + const termCandidatesToImport = await Extraction.fetchTermCandidatesSlice( + extractionId, + fromIndex, + toIndex + ) + + await Dictionary.importFromExtraction( + dictionaryId, + req.user.id, + termCandidatesToImport + ) + + await Dictionary.indexIntoSearchEngine(dictionaryId) + + res.end() +} + +dictionary.exportBegin = async (req, res) => { + const dictionaryId = req.params.id + const exportParams = { + isValidFilter: + req.body.isValidFilter === 'on' ? undefined : req.body.isValidFilter, + isPublishedFilter: + req.body.isPublishedFilter === 'on' + ? undefined + : req.body.isPublishedFilter, + statusFilter: + req.body.statusFilter === 'complete' + ? 'complete' + : req.body.statusFilter === 'inEdit' + ? 'in_edit' + : undefined, + isTerminologyReviewedFilter: + req.body.isTerminologyReviewedFilter === 'on' + ? undefined + : req.body.isTerminologyReviewedFilter, + isLanguageReviewedFilter: + req.body.isLanguageReviewedFilter === 'on' + ? undefined + : req.body.isLanguageReviewedFilter, + exportFileFormat: req.body.exportFileFormat + } + + const exportId = await Dictionary.beginExport(dictionaryId, exportParams) + + res.end() + + // Explicitcly catch any errors after the response has been sent + // since the the final error handler won't be able to send another. + try { + // TODO Consider delegating processing of export to a seperate process or at least a seperate thread (much like importing, extraction, ...). + await Dictionary.processExport(exportId) + } catch (error) { + // eslint-disable-next-line no-console + console.error(error) + } } module.exports = dictionary diff --git a/express/controllers/api/v1/extraction.js b/express/controllers/api/v1/extraction.js index f6e3a92..59b5212 100644 --- a/express/controllers/api/v1/extraction.js +++ b/express/controllers/api/v1/extraction.js @@ -1,22 +1,27 @@ -const { unlink, rm, mkdir } = require('fs/promises') +const { unlink, rm, mkdir, writeFile } = require('fs/promises') const { promisify } = require('util') const { URLSearchParams } = require('url') const multer = require('multer') +const i18next = require('i18next') const validator = require('validator') const axios = require('axios') const { getExtractionFilesPath, getDocumentsPath, getStopTermsPath, - getConllusPath + getConllusPath, + getFileStats } = require('../../../models/helpers/extraction') const { checkIfcanBegin } = require('../../helpers/extraction') const Extraction = require('../../../models/extraction') const Domain = require('../../../models/domain') const email = require('../../../models/email') const { intoDbArray } = require('../../../models/helpers') -const { origin } = require('../../../config/keys') -const { DEFAULT_HITS_PER_PAGE } = require('../../../config/settings') +const { origin, extractionApiOrigin } = require('../../../config/keys') +const { + DEFAULT_HITS_PER_PAGE, + TEMP_EXPORT_PATH +} = require('../../../config/settings') const MAX_FILE_NAME_LENGTH = 100 const MAX_FILE_SIZE = 10 ** 9 // 1 GB @@ -77,16 +82,19 @@ extraction.docsList = async (req, res) => { extraction.docsUpdate = async (req, res) => { try { await parseExtractionFileBody(req, res) + const fileStats = await getFileStats(req.file.path) + res.send(fileStats) } catch (error) { if ( error instanceof multer.MulterError && error.code === 'LIMIT_FILE_SIZE' ) { - throw Error('File too large. Must not be over 1 GB.') + const customError = Error('File too large. Must not be over 1 GB.') + customError.displayInProd = true + throw customError } throw error } - res.end() } extraction.docDelete = async (req, res) => { @@ -108,16 +116,19 @@ extraction.stopTermsList = async (req, res) => { extraction.stopTermsUpdate = async (req, res) => { try { await parseExtractionFileBody(req, res) + const fileStats = await getFileStats(req.file.path) + res.send(fileStats) } catch (error) { if ( error instanceof multer.MulterError && error.code === 'LIMIT_FILE_SIZE' ) { - throw Error('File too large. Must not be over 1 GB.') + const customError = Error('File too large. Must not be over 1 GB.') + customError.displayInProd = true + throw customError } throw error } - res.end() } extraction.stopTermDelete = async (req, res) => { @@ -141,7 +152,7 @@ extraction.ossSearch = [ ...(ossParams.keywords && { kljucneBesede: ossParams.keywords }), ...(ossParams.domainUdk && { udk: ossParams.domainUdk }) }) - const searchApiUrl = `http://rsdo.lhrs.feri.um.si:8080/oss/steviloBesedilPoIskanju?${searchParams}` + const searchApiUrl = `${extractionApiOrigin}/oss/steviloBesedilPoIskanju?${searchParams}` const { data: documentCount } = await axios.get(searchApiUrl) const canSave = documentCount && documentCount <= MAX_OSS_DOCUMENT_COUNT @@ -183,28 +194,39 @@ extraction.begin = async (req, res) => { res.send(timeStarted) - if (ossParams) { - // TODO This next method is only a temporary solution. - // TODO It should be called before response and its execution delegated to a seperate process or at least a seperate thread. - await Extraction.processOss(extractionId, ossParams.params) - } else { - // TODO This next method is only a temporary solution. - // TODO It should be called before response and its execution delegated to a seperate process or at least a seperate thread. - await Extraction.processOwn(extractionId, extractionName) - } + // Explicitcly catch any errors after the response has been sent + // since the the final error handler won't be able to send another. + try { + if (ossParams) { + // TODO This next method is only a temporary solution. + // TODO It should be called before response and its execution delegated to a seperate process or at least a seperate thread. + await Extraction.processOss(extractionId, ossParams.params) + } else { + // TODO This next method is only a temporary solution. + // TODO It should be called before response and its execution delegated to a seperate process or at least a seperate thread. + await Extraction.processOwn(extractionId, extractionName) + } - const extractionLink = new URL('/luscenje', origin) - const renderAsync = promisify(req.app.render.bind(req.app)) - const authorEmail = await Extraction.fetchAuthorEmail(extractionId) - const emailHtml = await renderAsync('email/extraction-done', { - extractionName, - extractionLink - }) - await email.send({ - to: authorEmail, - subject: 'Luščenje končano', - html: emailHtml - }) + const extractionLink = new URL('/luscenje', origin) + const renderAsync = promisify(req.app.render.bind(req.app)) + const { email: authorEmail, language: authorLanguage } = + await Extraction.fetchAuthorData(extractionId) + const emailHtml = await renderAsync( + `email/extraction-done_${authorLanguage}`, + { + extractionName, + extractionLink + } + ) + await email.send({ + to: authorEmail, + subject: i18next.t('Luščenje končano', { lng: authorLanguage }), + html: emailHtml + }) + } catch (error) { + // eslint-disable-next-line no-console + console.error(error) + } } extraction.duplicate = async (req, res) => { @@ -214,13 +236,36 @@ extraction.duplicate = async (req, res) => { } extraction.termCandidatesExport = async (req, res) => { - // TODO CSV logic (Luka's task) - // const extractionId = req.params.id - // const { from, to } = req.query - // const fromIndex = +from > 1 ? Math.floor(from) - 1 : 0 - // const toIndex = Number.isInteger(+to) ? Math.abs(to) : undefined - // console.log({ extractionId, fromIndex, toIndex }) - res.download('public/images/help-amebis-logo-pug-demo.png') + // TODO More formats (CSV, TSV, TXT, ...) + + const extractionId = req.params.id + const { from, to } = req.query + const fromIndex = +from > 1 ? Math.floor(from) - 1 : 0 + const toIndex = Number.isInteger(+(to === '' ? undefined : to)) + ? Math.abs(to) + : undefined + + // TODO Authentication, authorization, validation. + + const termCandidatesToExport = await Extraction.fetchTermCandidatesSlice( + extractionId, + fromIndex, + toIndex + ) + + const exportFileName = `term_candidates_${extractionId}` + const exportFilePath = `${TEMP_EXPORT_PATH}/${exportFileName}` + await writeFile( + exportFilePath, + JSON.stringify({ terminoloski_kandidati: termCandidatesToExport }) + ) + + const downloadAsync = promisify(res.download.bind(res)) + try { + await downloadAsync(exportFilePath, 'term_candidates.json') + } finally { + await unlink(exportFilePath) + } } extraction.listFinishedForUser = async (req, res) => { @@ -251,8 +296,11 @@ function extractionFileFilter(req, file, cb) { fileType = 'stopTerms' break - default: - return cb(Error('Invalid API endpoint')) + default: { + const customError = Error('Invalid API endpoint') + customError.displayInProd = true + return cb(customError) + } } const filenamePartsArray = file.originalname.split('.') @@ -264,30 +312,32 @@ function extractionFileFilter(req, file, cb) { (fileType === 'stopTerms' && fileExtension !== VALID_STOP_TERMS_FILE_EXTENSION) ) { - return cb(Error('Invalid file type')) + const customError = Error('Invalid file type') + customError.displayInProd = true + return cb(customError) } const fileName = filenamePartsArray.join('.') if (!fileName || fileName.length > MAX_FILE_NAME_LENGTH) { - return cb( - Error( - `Filename must be between 1 and ${MAX_FILE_NAME_LENGTH} characters long.` - ) + const customError = Error( + `Filename must be between 1 and ${MAX_FILE_NAME_LENGTH} characters long.` ) + customError.displayInProd = true + return cb(customError) } if (!validator.isAlphanumeric(fileName[0], 'sl-SI', { ignore: '_' })) { - return cb( - Error( - 'Filename must begin with an alphanumeric character or an underscore.' - ) + const customError = Error( + 'Filename must begin with an alphanumeric character or an underscore.' ) + customError.displayInProd = true + return cb(customError) } if (!validator.isAlphanumeric(fileName, 'sl-SI', { ignore: ' _-.' })) { - return cb( - Error( - 'Filename can only contain alphanumeric characters, spaces, underscores, minuses and periods.' - ) + const customError = Error( + 'Filename can only contain alphanumeric characters, spaces, underscores, minuses and periods.' ) + customError.displayInProd = true + return cb(customError) } req.fileType = fileType diff --git a/express/controllers/api/v1/search.js b/express/controllers/api/v1/search.js index 6c24609..5ec7784 100644 --- a/express/controllers/api/v1/search.js +++ b/express/controllers/api/v1/search.js @@ -66,6 +66,27 @@ exports.listMainEntries = async (req, res) => { {} ) + // duplicated code below for filtering foreignent, optimize later + const categoriesLabels = Object.keys(entriesByCategory) + for ( + let categoryIndex = 0; + categoryIndex < categoriesLabels.length; + categoryIndex++ + ) { + entriesByCategory[categoriesLabels[categoryIndex]] = entriesByCategory[ + categoriesLabels[categoryIndex] + ].map(entry => { + entry.foreignEntries = entry.foreignEntries?.filter(foreignEntry => { + if (filters.targetLanguages.length > 0) { + return filters.targetLanguages.includes(`${foreignEntry.lang.id}`) + } else { + return true + } + }) + return entry + }) + } + // res.send({ page, numberOfAllPages, entries }) res.append('page', page) res.append('number-of-all-pages', numberOfAllPages) diff --git a/express/controllers/consultancy.js b/express/controllers/consultancy.js index 4080805..23dda97 100644 --- a/express/controllers/consultancy.js +++ b/express/controllers/consultancy.js @@ -8,6 +8,7 @@ const { DEFAULT_HITS_PER_PAGE } = require('../config/settings') const generateQuery = require('../models/helpers/search/generate-query') const { searchConsultancyEntryIndex } = require('../models/search-engine') const { prepareConsultancyEntries } = require('../models/helpers/search') +const { getInstanceSetting, intoDbArray } = require('../models/helpers') // const { minEntriesRequirementCheckAndAct } = require('./helpers/dictionary') const consultancy = {} @@ -16,6 +17,9 @@ const consultancyAdmin = {} consultancy.index = async (req, res) => { req.indexHitPageAmount = '5' + res.locals.isOwnConsultancyEnabled = + (await getInstanceSetting('consultancy_type')) === 'own' + return await consultancyRequest( req, res, @@ -25,17 +29,24 @@ consultancy.index = async (req, res) => { } consultancy.search = async (req, res) => { + res.locals.isOwnConsultancyEnabled = + (await getInstanceSetting('consultancy_type')) === 'own' + + res.locals.queryKey = req.query.q + return await consultancyRequest( req, res, 'published', - 'pages/consultancy/search' + 'pages/consultancy/search', + req.t('Odgovori') ) } consultancy.specificQuestion = async (req, res) => { const { id } = req.params + // TODO i18n TIME FORMAT const entry = await ConsultancyEntry.fetchByIdWithFormattedTime(id) // const author = await User.fetchUser(entry.authorId) @@ -44,12 +55,13 @@ consultancy.specificQuestion = async (req, res) => { entry.answerAuthors = entry.answerAuthors.filter(author => author !== '') let authorString + // TODO I18n if (entry.answerAuthors.length === 1) { - authorString = 'Avtor' + authorString = req.t('Avtor') } else if (entry.answerAuthors.length === 2) { - authorString = 'Avtorja' + authorString = req.t('Avtorja') } else { - authorString = 'Avtorji' + authorString = req.t('Avtorji') } entry.domain = allPrimaryDomains.filter( @@ -62,18 +74,26 @@ consultancy.specificQuestion = async (req, res) => { entry.domain = false } + res.locals.isOwnConsultancyEnabled = + (await getInstanceSetting('consultancy_type')) === 'own' + res.render('pages/consultancy/item-details', { allPrimaryDomains, authorString, - entry + entry, + title: req.t('Odgovor') }) } consultancy.new = async (req, res) => { const allPrimaryDomains = await Dictionary.fetchAllPrimaryDomains() + res.locals.isOwnConsultancyEnabled = + (await getInstanceSetting('consultancy_type')) === 'own' + res.render('pages/consultancy/ask', { - allPrimaryDomains + allPrimaryDomains, + title: req.t('Novo vprašanje') }) } @@ -83,6 +103,7 @@ consultancyAdmin.new = async (req, res) => { res, 'new', 'pages/consultancy/admin/index', + req.t('Novo'), true, false ) @@ -95,7 +116,8 @@ consultancyAdmin.users = async (req, res) => { res.render('pages/consultancy/admin/users', { allPrimaryDomains, - users + users, + title: req.t('Svetovalci') }) } @@ -105,6 +127,7 @@ consultancyAdmin.rejected = async (req, res) => { res, 'rejected', 'pages/consultancy/admin/rejected', + req.t('Zavrnjeno'), true, false ) @@ -116,8 +139,10 @@ consultancyAdmin.published = async (req, res) => { res, 'published', 'pages/consultancy/admin/published', + req.t('Objavljeno'), true, - false + false, + 'published' ) } @@ -127,6 +152,7 @@ consultancyAdmin.prepared = async (req, res) => { res, 'review', 'pages/consultancy/admin/prepared', + req.t('Pripravljeno'), true, false ) @@ -142,6 +168,7 @@ consultancyAdmin.inProgress = async (req, res) => { res, 'in progress', 'pages/consultancy/admin/in-progress', + req.t('V delu'), true, false ) @@ -171,6 +198,7 @@ consultancyAdmin.edit = async (req, res) => { } else if (editors.filter(editors => editors.id === req.user.id) < 1) { return res.send('You do not have permsisions to edit this answer') } + // TODO i18n TIME FORMAT const entry = await ConsultancyEntry.fetchByIdWithFormattedTime(id) const allPrimaryDomains = await Dictionary.fetchAllPrimaryDomains() const author = await User.fetchUser(entry.authorId) @@ -186,7 +214,8 @@ consultancyAdmin.edit = async (req, res) => { author, isPublished, // TODO Luka: I suspect this will not work as intended on staging or production environments. Test. - urlPrefix: req.protocol + '://' + req.get('host') + urlPrefix: req.protocol + '://' + req.get('host'), + title: req.t('Urejanje') }) } @@ -198,14 +227,14 @@ function dateMap(obj) { return obj } -async function mapDomainIdToDomainNameSlovene(obj) { +async function mapDomainIdToDomainNameSlovene(obj, t) { try { const area = await Domain.fetchById( obj.domainPrimaryId ? obj.domainPrimaryId : obj.domainPrimaryIdInitial ) obj.area = area.nameSl } catch { - obj.area = 'Ni področja' + obj.area = t('Ni področja') } return obj @@ -227,14 +256,14 @@ function mapInitialValuesAsEmpty(obj) { return obj } -async function mapEntryList(list) { +async function mapEntryList(list, t) { return await Promise.all( list.map(entry => { let entity = utils.compose(dateMap, mapInitialValuesAsEmpty)(entry) // TODO Each mapDomainIdToDomainNameSlovene call leads to one DB query. // TODO Test if and what scenarios can lead to too many calls and how it can be avoided. - entity = utils.composeAsync(mapDomainIdToDomainNameSlovene)(entry) + entity = utils.composeAsync(mapDomainIdToDomainNameSlovene)(entry, t) return entity }) @@ -283,11 +312,26 @@ async function consultancyRequest( res, type, url, + title = req.t('Svetovanje'), isAdminPage = false, privilegeToSeAll = true // this method seperates consultancy main from admin, so all results get visible TO ALL REGISTERED USERS, not just admins ) { const searchString = req.query.q?.trim() ?? '' + let allPrimaryDomains = await Dictionary.fetchAllPrimaryDomains() + + /// filter selected domains for the prompt /// + // Not duplicates of this code arise... + const pdList = intoDbArray(req.query.pd, 'always') + + allPrimaryDomains = allPrimaryDomains.map(entry => { + if (pdList.includes(`${entry.id}`)) { + entry.selected = true + } + return entry + }) + /// ////////////////////////////////////////// + let assignedConsultant if ( @@ -333,11 +377,11 @@ async function consultancyRequest( let entries = prepareConsultancyEntries(hits) // console.log({ entries, numberOfAllHits, numberOfAllPages }) - + // TODO I18n - nameSl entries = entries.map(entry => { entry.primaryDomain = entry.primaryDomain ? entry.primaryDomain.nameSl - : 'nedefinirano' + : req.t('nedefinirano') if (entry.assignedConsultants) { entry.firstName = entry.assignedConsultants[0]?.firstName @@ -365,8 +409,6 @@ async function consultancyRequest( return entry }) - const allPrimaryDomains = await Dictionary.fetchAllPrimaryDomains() - // const entryList = await mapEntryList(inProgressEntryList) const userList = await User.fetchConsultants() @@ -401,7 +443,9 @@ async function consultancyRequest( entries, // entryList, userList, numberOfAllPages, - queryCount: numberOfAllHits + queryCount: numberOfAllHits, + consultancyPageType: type, + title }) } diff --git a/express/controllers/dictionaries.js b/express/controllers/dictionaries.js index 13762ff..6370aa3 100644 --- a/express/controllers/dictionaries.js +++ b/express/controllers/dictionaries.js @@ -9,7 +9,8 @@ const Comment = require('../models/comment') const genEditorAllQuery = require('../models/helpers/search/generate-query/editor/all') const { searchEntryIndex } = require('../models/search-engine') const { prepareEditorEntries } = require('../models/helpers/search') -const { getInstanceSetting } = require('../models/helpers') +const { getInstanceSetting, intoDbArray } = require('../models/helpers') +const { getExportFilesPath } = require('../models/helpers/dictionary') const { DEFAULT_HITS_PER_PAGE, DATA_FILES_PATH } = require('../config/settings') const { statusChangeCheckAndAct, @@ -17,6 +18,7 @@ const { } = require('./helpers/dictionary') const SFDSuggestionImporter = require('./helpers/search-filter-data-suggestion-importer') const Extraction = require('../models/extraction') +const { isGeneratorFunction } = require('util/types') const importFileBodyParser = multer({ dest: `${DATA_FILES_PATH}/dict_import_temp`, @@ -39,7 +41,7 @@ dictionary.list = async (req, res) => { dictionaries = await Dictionary.fetchAllByUser(req.user.id) } res.render('pages/dictionaries/list', { - title: 'Seznam slovarjev', + title: req.t('Seznam slovarjev'), dictionaries }) } @@ -51,11 +53,11 @@ dictionary.new = async (req, res) => { await Promise.all([ Dictionary.fetchAllPrimaryDomains(), Dictionary.fetchAllApprovedSecondaryDomains(), - Dictionary.fetchAllLanguages(language) + Dictionary.fetchAllLanguages(language, true) ]) res.render('pages/dictionaries/new', { - title: 'Nov slovar', + title: req.t('Nov slovar'), allPrimaryDomains, allSecondaryDomains, allLanguages @@ -82,7 +84,7 @@ dictionary.editDescription = async (req, res) => { ]) res.render('pages/dictionaries/description', { - title: 'Ime in opis', + title: req.t('Osnovni podatki'), allPrimaryDomains, allSecondaryDomains, dictionary, @@ -125,7 +127,7 @@ dictionary.editUsers = async (req, res) => { } res.render(viewPath, { - title: 'Uporabniki', + title: req.t('Uporabniki'), dictionary, userRights, entriesCount, @@ -142,6 +144,7 @@ dictionary.updateUsers = async (req, res) => { const newDictStatus = await determineNewStatus(isPublished) + // TODO I18n - nameSl const { nameSl, status: oldDictStatus } = await Dictionary.fetchEditUsers( dictionaryId ) @@ -172,12 +175,13 @@ dictionary.updateUsers = async (req, res) => { dictionary.editStructure = async (req, res) => { // TODO Once english language is implemented, gather selected language (sl/en) from request ~ (cookies?) + // TODO I18n - nameSl const language = 'name_sl' const { dictionaryId } = req.params const [dictionary, associatedLanguages, allLanguages] = await Promise.all([ Dictionary.fetchEditStructure(dictionaryId), Dictionary.fetchLanguages(dictionaryId), - Dictionary.fetchAllLanguages(language) + Dictionary.fetchAllLanguages(language, true) ]) let viewPath @@ -190,7 +194,7 @@ dictionary.editStructure = async (req, res) => { } res.render(viewPath, { - title: 'Struktura slovarskega sestavka', + title: req.t('Struktura slovarskega sestavka'), dictionary, associatedLanguages, allLanguages @@ -223,7 +227,7 @@ dictionary.editAdvanced = async (req, res) => { } res.render(viewPath, { - title: 'Napredno', + title: req.t('Napredno'), dictionary: { id: req.params.dictionaryId }, dictionaryName }) @@ -251,7 +255,7 @@ dictionary.comments = async (req, res) => { } res.render(viewPath, { - title: 'Komentarji', + title: req.t('Komentarji'), numberOfAllPages, dictionary: { id: req.params.dictionaryId }, comments, @@ -261,10 +265,12 @@ dictionary.comments = async (req, res) => { dictionary.showImportFromFileForm = async (req, res) => { const { dictionaryId } = req.params - const [imports, dictionaryName] = await Promise.all([ - Dictionary.fetchAllImports(dictionaryId), - Dictionary.fetchName(dictionaryId) - ]) + const resultsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE + const [{ pages_total: numberOfAllPages, results }, dictionaryName] = + await Promise.all([ + Dictionary.fetchAllImports(dictionaryId, resultsPerPage, 1), + Dictionary.fetchName(dictionaryId) + ]) let viewPath switch (req.baseUrl) { case '/slovarji': @@ -275,9 +281,10 @@ dictionary.showImportFromFileForm = async (req, res) => { } res.render(viewPath, { - title: 'Uvoz iz datoteke', + title: req.t('Uvoz iz datoteke'), dictionary: { id: dictionaryId }, - imports, + numberOfAllPages, + results, dictionaryName }) } @@ -289,7 +296,7 @@ dictionary.listAdminDictionaries = async (req, res) => { await Dictionary.fetchAllAdminDictionaries(resultsPerPage, 1) res.render('pages/admin/dictionaries-list', { - title: 'Struktura slovarjev', + title: req.t('Seznam slovarjev'), numberOfAllPages, results }) @@ -312,7 +319,7 @@ dictionary.adminEditDescription = async (req, res) => { ]) res.render('pages/admin/dictionary-description', { - title: 'Podatki', + title: req.t('Osnovni podatki'), allPrimaryDomains, allSecondaryDomains, dictionary, @@ -364,11 +371,11 @@ dictionary.showImportFromExtractionForm = async (req, res) => { switch (req.baseUrl) { case '/slovarji': viewPath = 'pages/dictionaries/extraction-import' - title = 'Uvoz' + title = req.t('Uvoz iz luščilnika') break case '/admin': viewPath = 'pages/admin/dictionary-extraction-import' - title = 'Uvoz luščenje' + title = req.t('Uvoz iz luščilnika') } res.render(viewPath, { @@ -381,7 +388,12 @@ dictionary.showImportFromExtractionForm = async (req, res) => { dictionary.showExportToFileForm = async (req, res) => { const { dictionaryId } = req.params - const dictionaryName = await Dictionary.fetchName(dictionaryId) + const resultsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE + const [dictionaryName, { pages_total: numberOfAllPages, results }] = + await Promise.all([ + Dictionary.fetchName(dictionaryId), + Dictionary.fetchExports(dictionaryId, resultsPerPage, 1) + ]) let viewPath switch (req.baseUrl) { case '/slovarji': @@ -390,11 +402,12 @@ dictionary.showExportToFileForm = async (req, res) => { case '/admin': viewPath = 'pages/admin/dictionary-export' } - res.render(viewPath, { - title: 'Izvoz', - dictionary: { id: req.params.dictionaryId }, - dictionaryName + title: req.t('Izvoz'), + dictionary: { id: dictionaryId }, + dictionaryName, + numberOfAllPages, + results }) } @@ -418,7 +431,7 @@ dictionary.editDomainLabels = async (req, res) => { } res.render(viewPath, { - title: 'Področne oznake', + title: req.t('Področne oznake'), dictionary: { id: dictionaryId }, numberOfAllPages, results, @@ -448,7 +461,7 @@ dictionary.showContent = async (req, res) => { const terms = prepareEditorEntries(hits) res.render('pages/dictionaries/content', { - title: 'Vsebina slovarja', + title: req.t('Vsebina slovarja'), terms, canPublishEntriesInEdit, dictionaryName, @@ -466,7 +479,7 @@ dictionary.showSecondaryDomains = async (req, res) => { await Dictionary.fetchAllSecondaryDomains(resultsPerPage, 1) res.render('pages/admin/areas', { - title: 'Podpodročja', + title: req.t('Področne oznake'), numberOfAllPages, results }) @@ -501,12 +514,27 @@ dictionary.dictionaryList = async (req, res) => { ) const numberOfAllHits = parseInt( - (await Dictionary.fetchAllDictionariesCount()).count + (await Dictionary.fetchAllDictionariesPublishedCount()).count ) const numberOfAllPages = Math.ceil(numberOfAllHits / hitsPerPage) const allPrimaryDomains = await Dictionary.fetchAllPrimaryDomains() + /* + /// filter selected domains for the prompt /// + // Not duplicates of this code arise... + const pdList = intoDbArray(req.query.pd, 'always') + + allPrimaryDomains = allPrimaryDomains.map(entry => { + if (pdList.includes(`${entry.id}`)) { + entry.selected = true + } + return entry + }) + + /// ////////////////////////////////////////// + */ + dictionaries = dictionaries.map(e => { if (!e.portalcode) { e.portalcode = defaultportalcode @@ -518,7 +546,7 @@ dictionary.dictionaryList = async (req, res) => { const isDictionaryListPage = true res.render('pages/dictionaries/dictlist', { - title: 'Seznam slovarjev', + title: req.t('Seznam slovarjev'), dictionaries, allPrimaryDomains, numberOfAllPages, @@ -532,6 +560,7 @@ dictionary.dictionaryList = async (req, res) => { dictionary.dictionaryDetails = async (req, res) => { const { absolutePrevPath, sentFromEntryId } = req.query const dictId = req.params.dictionaryId + const title = req.t('O slovarju') const { allPrimaryDomains, sourceLanguages, @@ -555,7 +584,7 @@ dictionary.dictionaryDetails = async (req, res) => { // check if it is a local dictionary if (!dictionaryData.portalname && !dictionaryData.portalcode) { - dictionaryData[0].portalname = await getInstanceSetting('portal_name') + dictionaryData[0].portalname = await getInstanceSetting('portal_name_sl') dictionaryData[0].portalcode = await getInstanceSetting('portal_code') } @@ -579,7 +608,7 @@ dictionary.dictionaryDetails = async (req, res) => { ) const structData = { - prevWindowTitle: 'Nazaj', + prevWindowTitle: req.t('Nazaj'), dictName: dictionaryData[0].dictionarysl, portalCode: dictionaryData[0].portalcode, portalName: dictionaryData[0].portalname, @@ -589,13 +618,14 @@ dictionary.dictionaryDetails = async (req, res) => { languages: reducedData.languages ? reducedData.languages.join(', ') : '' } + // TODO I18n if (reducedData.author) { if (reducedData.author.length > 2) { - structData.authorLabel = 'Avtorji' + structData.authorLabel = req.t('Avtorji') } else if (reducedData.author.length === 2) { - structData.authorLabel = 'Avtorja' + structData.authorLabel = req.t('Avtorja') } else if (reducedData.author.length === 1) { - structData.authorLabel = 'Avtor' + structData.authorLabel = req.t('Avtor') } } @@ -623,7 +653,8 @@ dictionary.dictionaryDetails = async (req, res) => { finalData, numberOfAllPages, comments, - commentCount + commentCount, + title }) // todo } @@ -680,8 +711,28 @@ dictionary.importFromFile = async (req, res) => { } } +dictionary.exportDownload = async (req, res) => { + // TODO Add authentication and authorization. + + const { exportId } = req.params + const { exportStatus, dictionaryId, nameString, timeString, fileFormat } = + await Dictionary.fetchExportDownloadMetadata(exportId) + if (exportStatus !== 'finished') { + throw Error("Can't request file for unfinished export") + } + const exportFilesPath = getExportFilesPath(dictionaryId) + const exportFilePath = `${exportFilesPath}/${exportId}` + const exportFileName = `${nameString}_${timeString}.${fileFormat}` + + res.download(exportFilePath, exportFileName) +} + function importFileFilter(req, file, cb) { - if (file.mimetype !== 'text/xml') return cb(Error('Invalid file type')) + if (file.mimetype !== 'text/xml') { + const customError = Error('Invalid file type') + customError.displayInProd = true + return cb(customError) + } cb(null, true) } diff --git a/express/controllers/extraction.js b/express/controllers/extraction.js index e8653a2..1174d33 100644 --- a/express/controllers/extraction.js +++ b/express/controllers/extraction.js @@ -29,7 +29,10 @@ extraction.list = async (req, res) => { ) } - res.render('pages/extraction/list', { title: 'Luščenje seznam', extractions }) + res.render('pages/extraction/list', { + title: req.t('Seznam luščenj'), + extractions + }) } extraction.create = async (req, res) => { @@ -39,7 +42,7 @@ extraction.create = async (req, res) => { return res.redirect(303, 'back') } - const extractionName = `Luščenje ${extractionCount + 1}` + const extractionName = req.t('Luščenje') + `${extractionCount + 1}` const { extractionType } = req.body let extractionId @@ -81,7 +84,7 @@ extraction.edit = async (req, res) => { extraction.keywords = intoDbArray(params.keywords, 'always') res.render('pages/extraction/edit-oss', { - title: 'KAS + dokumenti', + title: req.t('Besedila'), id: extractionId, extraction, allPrimaryDomains, @@ -93,7 +96,7 @@ extraction.edit = async (req, res) => { Extraction.fetchAllStopTermsFilesStats(extractionId) ]) res.render('pages/extraction/edit-own', { - title: 'Besedila', + title: req.t('Besedila'), id: extractionId, extraction, extractionDocuments, @@ -117,6 +120,7 @@ extraction.docsEdit = async (req, res) => { ) res.render('pages/extraction/docs-edit', { + title: req.t('Besedila'), id: extractionId, extractionDocuments }) @@ -129,6 +133,7 @@ extraction.stopTermsEdit = async (req, res) => { ) res.render('pages/extraction/stop-terms-edit', { + title: req.t('Stop termini'), id: extractionId, stopTermsFiles }) @@ -145,6 +150,7 @@ extraction.listTermCandidates = async (req, res) => { const firstPageOfTermCandidates = termCandidates.slice(0, hitsPerPage) res.render('pages/extraction/term-candidates', { + title: req.t('Terminološki kandidati'), extractionId, termCandidatesJson, firstPageOfTermCandidates, diff --git a/express/controllers/helpers/dictionary.js b/express/controllers/helpers/dictionary.js index 31bbd62..fbca42a 100644 --- a/express/controllers/helpers/dictionary.js +++ b/express/controllers/helpers/dictionary.js @@ -1,3 +1,5 @@ +/* global __ */ + const { getInstanceSetting } = require('../../models/helpers') const Dictionary = require('../../models/dictionary') const cache = require('../../models/cache') @@ -56,6 +58,7 @@ exports.minEntriesRequirementCheckAndAct = { if (!isBelowMinEntriesThreshold || wasEmailAlreadySent) return // Prepare and send notification emails. + // TODO I18n - nameSl const [nameSl, adminEmails, dictionariesAdminEmails] = await Promise.all([ Dictionary.fetchName(dictionaryId), Dictionary.fetchAdminEmails(dictionaryId), @@ -68,14 +71,16 @@ exports.minEntriesRequirementCheckAndAct = { const type = 'delete' const renderAsync = promisify(appRef.render.bind(appRef)) + // TODO I18n - nameSl const emailHtml = await renderAsync('email/dictionary-status-change', { type, nameSl }) + // TODO i18n - What language are the email title and content (we already have email translated) await email.send({ to: allEmails, - subject: 'Obvestilo o številu gesel', + subject: __('Obvestilo o številu gesel'), html: emailHtml }) @@ -118,6 +123,7 @@ exports.determineNewStatus = async isPublished => { // Exports actions related to checking and acting on dictionary status changes. exports.statusChangeCheckAndAct = { // Notify dictionaries admins by email on dictionary status changes. + // TODO I18n - nameSl async updateUsers( dictionaryId, isPublishedNew, @@ -133,7 +139,7 @@ exports.statusChangeCheckAndAct = { const dictionariesAdminEmails = await Dictionary.fetchDictionariesAdminEmails() const type = 'unpublish' - + // TODO I18n - nameSl await renderAndSendStatusChangeEmails( appRef, type, @@ -151,6 +157,7 @@ exports.statusChangeCheckAndAct = { const type = isApprovalRequired === 'T' ? 'publish-approval' : 'publish-no-approval' + // TODO I18n - nameSl await renderAndSendStatusChangeEmails( appRef, type, @@ -171,6 +178,7 @@ exports.statusChangeCheckAndAct = { user ) { if (statusOld === 'reviewed' && statusNew !== 'reviewed') { + // TODO I18n - nameSl const [nameSl, adminEmails] = await Promise.all([ Dictionary.fetchName(dictionaryId), Dictionary.fetchAdminEmails(dictionaryId) @@ -189,6 +197,7 @@ exports.statusChangeCheckAndAct = { } // Helper function used by statusChangeCheckAndAct methods. +// TODO I18n - nameSl async function renderAndSendStatusChangeEmails( appRef, type, @@ -203,9 +212,10 @@ async function renderAndSendStatusChangeEmails( nameSl }) + // TODO i18n - What language are the email title and content (we already have email translated) await email.send({ to: targetEmails, - subject: 'Sprememba stanja slovarja', + subject: __('Sprememba stanja slovarja'), html: emailHtml }) } diff --git a/express/controllers/helpers/search-filter-data-suggestion-importer.js b/express/controllers/helpers/search-filter-data-suggestion-importer.js index 1e4e3bc..2581cb7 100644 --- a/express/controllers/helpers/search-filter-data-suggestion-importer.js +++ b/express/controllers/helpers/search-filter-data-suggestion-importer.js @@ -8,12 +8,15 @@ helper.initialize = async () => { const initializers = {} // TODO Once english language is implemented, gather selected language (sl/en) from request ~ (cookies?) + // TODO i18n name_sl const language = 'name_sl' // TODO Consider parallelizing following queries. Single vs pooled clients? initializers.allPrimaryDomains = await Dictionary.fetchAllPrimaryDomains() initializers.sourceLanguages = await Dictionary.fetchAllLanguages(language) - initializers.targetLanguages = initializers.sourceLanguages.filter( + initializers.targetLanguages = ( + await Dictionary.fetchAllLanguages(language) + ).filter( // drop slovene language l => l.id !== 32 ) @@ -22,7 +25,7 @@ helper.initialize = async () => { initializers.portals = [] initializers.portals.push({ - name: await getInstanceSetting('portal_name'), + name: await getInstanceSetting('portal_name_sl'), code: await getInstanceSetting('portal_code') }) diff --git a/express/controllers/index.js b/express/controllers/index.js index d50982d..53044d2 100644 --- a/express/controllers/index.js +++ b/express/controllers/index.js @@ -2,12 +2,16 @@ const Entry = require('../models/entry') const { getInstanceSetting } = require('../models/helpers') const Dictionary = require('../models/dictionary') const Comment = require('../models/comment') -const { searchEntryIndex } = require('../models/search-engine') +const { + searchEntryIndex, + searchConsultancyEntryIndex +} = require('../models/search-engine') const { intoDbArray } = require('../models/helpers') const { prepareEntries, prepareAggregation, - prepareSeachFilterData + prepareSeachFilterData, + prepareConsultancyEntries } = require('../models/helpers/search') const generateQuery = require('../models/helpers/search/generate-query') const { DEFAULT_HITS_PER_PAGE } = require('../config/settings') @@ -16,6 +20,7 @@ const User = require('../models/user') // TODO Luka (note to self): Measure performance, consider caching. exports.index = async (req, res) => { + const { language } = req const { allPrimaryDomains, sourceLanguages, @@ -30,8 +35,10 @@ exports.index = async (req, res) => { englishLanguageEnabled ) - const portalName = await getInstanceSetting('portal_name') - const portalDescription = await getInstanceSetting('portal_description') + const portalName = await getInstanceSetting(`portal_name_${language}`) + const portalDescription = await getInstanceSetting( + `portal_description_${language}` + ) const isRoot = true @@ -53,7 +60,9 @@ exports.search = async (req, res) => { if (!searchString) return res.redirect('/') - const { + const title = req.t('Iskanje') + + let { allPrimaryDomains, sourceLanguages, targetLanguages, @@ -81,6 +90,32 @@ exports.search = async (req, res) => { true ) + // Consultancy + + const hitsQueryConsultancy = generateQuery.consultancy( + searchString, + { + status: 'published', + primaryDomain: filters.primaryDomains[0] + }, + hitsPerPage, + page + ) + + const hitsConsultancy = await searchConsultancyEntryIndex( + hitsQueryConsultancy + ) + + const consultancyHits = hitsConsultancy.body.hits.total.value + + const consultancyURL = `/svetovanje/iskanje?q=${searchString}${ + filters.primaryDomains.length > 0 ? '&pd=' + filters.primaryDomains[0] : '' // consultancy filtering only supports one domain + }` + + // console.log(entries) + + // + const [hits, aggregationRaw] = await Promise.all([ searchEntryIndex(hitsQuery), searchEntryIndex(aggregateQuery) @@ -143,6 +178,30 @@ exports.search = async (req, res) => { sources: false } + // Keep selected items on refresh + sourceLanguages = addSelectedIdentifierToFilters( + sourceLanguages, + filters.sourceLanguages + ) + + targetLanguages = addSelectedIdentifierToFilters( + targetLanguages, + filters.targetLanguages + ) + allPrimaryDomains = addSelectedIdentifierToFilters( + allPrimaryDomains, + filters.primaryDomains + ) + + allDictionaryNames = addSelectedIdentifierToFilters( + allDictionaryNames, + filters.dictionaries + ) + + /// ////////////// + + portals = addSelectedIdentifierToPortals(portals, filters.sources) + if (count < 1) { return res.render('pages/search/no-results', { allPrimaryDomains, @@ -154,6 +213,8 @@ exports.search = async (req, res) => { entriesByCategory, searchFilterData, disabledSideMenuFilters, + consultancyHits, + consultancyURL, // allAggregation, numberOfAllHits, numberOfAllPages, @@ -161,6 +222,74 @@ exports.search = async (req, res) => { }) } + // duplicated code below for filtering foreignentries, optimize later + // Below code filters target languages based on source and target filters for each category + const categoriesLabels = Object.keys(entriesByCategory) + for ( + let categoryIndex = 0; + categoryIndex < categoriesLabels.length; + categoryIndex++ + ) { + entriesByCategory[categoriesLabels[categoryIndex]] = entriesByCategory[ + categoriesLabels[categoryIndex] + ].map(entry => { + entry.foreignEntries = entry.foreignEntries?.filter(foreignEntry => { + if (filters.targetLanguages.length > 0) { + if (aggregation.sourceLanguages.length === 1) { + filters.sourceLanguages = [aggregation.sourceLanguages[0].id] + } + + return ( + filters.targetLanguages.includes(`${foreignEntry.lang.id}`) || + filters.sourceLanguages?.includes(`${foreignEntry.lang.id}`) + ) + } else { + return true + } + }) + return entry + }) + } + + // Disable target languages logic + + if ( + filters.sourceLanguages.length > 1 || // more or equal than 2 source languages + (filters.sourceLanguages.length < 1 && // No filters, but more than 1 source language filters returned + aggregation.sourceLanguages.length > 1) || + (aggregation.targetLanguages.length === 1 && + aggregation.sourceLanguages.length === 1 && + aggregation.targetLanguages[0].id === aggregation.sourceLanguages[0].id) // + ) { + // predicate 1 Disable if 2 or more filters in Source languages are selected + // predicate 2 If none are selected, then check if displayed filters are more than 2 + aggregation.targetLanguages = [] + filters.targetLanguages = [] + targetLanguages = [] + searchFilterData.targetLanguages = [] + disabledSideMenuFilters.targetLanguages = true + } + + // remove source language from target language + filters.sourceLanguages.forEach(entry => { + if ( + aggregation.targetLanguages.filter(toFilter => toFilter.id === entry) + .length > 0 + ) { + aggregation.targetLanguages = aggregation.targetLanguages.filter( + toFilter => toFilter.id !== entry + ) + searchFilterData.targetLanguages = + searchFilterData.targetLanguages.filter( + toFilter => toFilter.id !== entry + ) + } + }) + if (aggregation.targetLanguages.length < 0) { + searchFilterData.targetLanguages = [] + disabledSideMenuFilters.targetLanguages = true + } + // TODO Add a page title? res.render('pages/search/results', { allPrimaryDomains, @@ -172,15 +301,19 @@ exports.search = async (req, res) => { entriesByCategory, searchFilterData, disabledSideMenuFilters, + consultancyHits, + consultancyURL, // allAggregation, numberOfAllHits, numberOfAllPages, - page + page, + title }) } exports.entryDetails = async (req, res) => { const termId = req.params.entryId + const title = req.t('Termin') /* const [entry, domainLabels] = await Promise.all([ Entry.fetchFullWithOrderedForeignLanguages(termId), @@ -188,12 +321,16 @@ exports.entryDetails = async (req, res) => { ]) */ const entry = await Entry.fetchFullWithOrderedForeignLanguages(termId) - /* const entryData = { entry // allDomainLabelsJoined: domainLabels.map(e => e.name).join(', ') } */ + // If external url, just redirect + if (entry.external_url) { + return res.redirect(entry.external_url) + } + // unnecessary legacy assigment, refactor when time is available const entryData = entry @@ -228,8 +365,8 @@ exports.entryDetails = async (req, res) => { /// // check if it is a local dictionary - if (!dictionaryData.portalname && !dictionaryData.portalcode) { - dictionaryData[0].portalname = await getInstanceSetting('portal_name') + if (!dictionaryData.portalnamesl && !dictionaryData.portalcode) { + dictionaryData[0].portalname = await getInstanceSetting('portal_name_sl') dictionaryData[0].portalcode = await getInstanceSetting('portal_code') } @@ -258,7 +395,7 @@ exports.entryDetails = async (req, res) => { // struct data contains important data and re-maps for unification (maybe refactor later) const structData = { termId: termId, - prevWindowTitle: 'Iskanje', + prevWindowTitle: req.t('Iskanje'), prevHref: '/iskanje', portalCode: dictionaryData[0].portalcode, portalName: dictionaryData[0].portalname, @@ -270,13 +407,14 @@ exports.entryDetails = async (req, res) => { languages: reducedData.languages ? reducedData.languages.join(', ') : '' } + // TODO I18n if (reducedData.author) { if (reducedData.author.length > 2) { - structData.authorLabel = 'Avtorji' + structData.authorLabel = req.t('Avtorji') } else if (reducedData.author.length === 2) { - structData.authorLabel = 'Avtorja' + structData.authorLabel = req.t('Avtorja') } else if (reducedData.author.length === 1) { - structData.authorLabel = 'Avtor' + structData.authorLabel = req.t('Avtor') } } @@ -321,27 +459,63 @@ exports.entryDetails = async (req, res) => { selectedDomainLabelsForEntryString, numberOfAllPages, comments, - commentCount + commentCount, + title }) } exports.myProfile = async (req, res) => { - res.render('pages/profile/my-profile', { title: 'Moj račun' }) + res.render('pages/profile/my-profile', { title: req.t('Osnovni podatki') }) +} + +exports.deleteProfile = async (req, res) => { + res.render('pages/profile/delete-profile', { title: req.t('Izbriši račun') }) } exports.changePassword = async (req, res) => { - res.render('pages/profile/change-password', { title: 'Spremeni geslo' }) + res.render('pages/profile/change-password', { + title: req.t('Spremeni geslo') + }) +} + +exports.resetPassword = async (req, res) => { + const { token } = req.query + + // console.log(token) + res.render('pages/reset-password/reset-password', { + // title: 'Pozabljeno geslo' + isValidToken: token === '123', + token + }) } exports.userSettings = async (req, res) => { const hitsPerPageArr = await User.fetchAllowedHitsPerPage() res.render('pages/profile/change-profile-settings', { - title: 'Nastavitve računa', + title: req.t('Nastavitve računa'), hitsPerPageArr, hitsForUser: req.user?.hitsPerPage }) } +exports.changeUserLanguage = async (req, res) => { + const { languageCode } = req.params + const validCodes = ['sl', 'en'] + + if (!validCodes.includes(languageCode)) { + throw Error(`Invalid language: ${languageCode}`) + } + + const { user } = req + if (user) { + await User.updateLanguage(user.id, languageCode) + } else { + req.session.language = languageCode + } + + res.redirect('/') +} + function mergeDomains( domainList, aggregationFn = (acc, n) => { @@ -384,3 +558,23 @@ function filterResults(entries) { return [termLst, ftermLst, otherLst] } + +function addSelectedIdentifierToFilters(source, selectedIDs) { + return source.map(entry => { + if (selectedIDs.includes(`${entry.id}`)) { + entry.selected = true + } + + return entry + }) +} + +function addSelectedIdentifierToPortals(source, selectedIDs) { + return source.map(entry => { + if (selectedIDs.includes(`${entry.code}`)) { + entry.selected = true + } + + return entry + }) +} diff --git a/express/controllers/portals.js b/express/controllers/portals.js index 4c15990..af79e33 100644 --- a/express/controllers/portals.js +++ b/express/controllers/portals.js @@ -8,7 +8,7 @@ portal.instanceSettings = async (req, res) => { const portal = await Portal.fetchInstanceSettings() res.render('pages/admin/portal', { - title: 'Nastavitve portala', + title: req.t('Nastavitve portala'), portal }) } @@ -24,7 +24,7 @@ portal.instanceDictSettings = async (req, res) => { const dictionary = await Portal.fetchInstanceDictSettings() res.render('pages/admin/settings-dictionaries', { - title: 'Nastavitve slovarjev', + title: req.t('Nastavitve slovarjev'), dictionary }) } @@ -39,7 +39,7 @@ portal.updateInstanceDictSettings = async (req, res) => { portal.instanceConsultancySettings = async (req, res) => { const consultancy = await Portal.fetchInstanceConsultancySettings() res.render('pages/admin/portal-consultancy-settings', { - title: 'Nastavitve svetovalnice', + title: req.t('Nastavitve svetovalnice'), consultancy }) } @@ -53,7 +53,7 @@ portal.updateInstanceConusltacySettings = async (req, res) => { portal.new = async (req, res) => { res.render('pages/admin/new-connection', { - title: 'Nova povezava' + title: req.t('Nova povezava') }) } @@ -61,7 +61,7 @@ portal.list = async (req, res) => { const allLinkedPortals = await Portal.fetchAll() res.render('pages/admin/connections-list', { - title: 'Seznam povezav', + title: req.t('Seznam povezav'), allLinkedPortals }) } @@ -70,7 +70,10 @@ portal.fetchPortal = async (req, res) => { const portalId = req.params.portalId const portal = await Portal.fetchPortal(portalId) - res.render('pages/admin/portal-edit', { title: 'Uredi povezavo', portal }) + res.render('pages/admin/portal-edit', { + title: req.t('Uredi povezavo'), + portal + }) } portal.updatePortal = async (req, res) => { @@ -88,7 +91,7 @@ portal.fetchSelectedLinkedDictionaries = async (req, res) => { await Portal.fetchSelectedLinkedDictionaries(linkedId, resultsPerPage, 1) res.render('pages/admin/portal-list-dict', { - title: 'Slovarji portala', + title: req.t('Slovarji portala'), linkedId, numberOfAllPages, results @@ -108,7 +111,7 @@ portal.fetchAllLinkedDictionaries = async (req, res) => { await Portal.fetchAllLinkedDictionaries(resultsPerPage, 1) res.render('pages/admin/portals-all-linked-dictionaries', { - title: 'Povezani', + title: req.t('Povezani slovarji'), numberOfAllPages, results }) @@ -130,7 +133,7 @@ portal.comments = async (req, res) => { 1 ) res.render('pages/admin/comments', { - title: 'Komentarji', + title: req.t('Komentarji'), numberOfAllPages, dictionary: { id: req.params.dictionaryId }, comments diff --git a/express/controllers/users.js b/express/controllers/users.js index 3ad7f86..5602e1e 100644 --- a/express/controllers/users.js +++ b/express/controllers/users.js @@ -12,7 +12,10 @@ const user = {} user.register = async (req, res) => { // TODO Add validation. - const userId = await User.create(req.body) + const userId = await User.create({ + ...req.body, + language: req.session.language + }) const activationToken = (await RandomBytesAsync(32)).toString('hex') await User.saveActivationToken(userId, activationToken) const { email: userEmail, username } = req.body @@ -20,16 +23,16 @@ user.register = async (req, res) => { activationLink.searchParams.set('token', activationToken) activationLink = activationLink.href const renderAsync = promisify(req.app.render.bind(req.app)) - const emailHtml = await renderAsync('email/user-activation', { + const emailHtml = await renderAsync(`email/user-activation_${req.language}`, { username, activationLink }) await email.send({ to: userEmail, - subject: 'Aktivacija računa', + subject: req.t('Aktivacija računa'), html: emailHtml }) - res.send('Registracija uspešna') + res.send(req.t('Registracija uspešna')) } user.activateAccount = async (req, res) => { @@ -39,6 +42,12 @@ user.activateAccount = async (req, res) => { await User.activateAccount(user) const loginAsync = promisify(req.login.bind(req)) await loginAsync(user) + + if (req.session.language) { + await User.updateLanguage(user.id, req.session.language) + delete req.session.language + } + res.redirect('/') } @@ -46,8 +55,9 @@ user.login = async (req, res, next) => { passport.authenticate( 'local', { - badRequestMessage: + badRequestMessage: req.t( 'Nepravilno uporabniško ime, elektronski naslov ali geslo.' + ) }, async (err, user, info) => { if (err) return next(err) @@ -69,7 +79,12 @@ user.login = async (req, res, next) => { res.cookie('remember_me', rememberMeToken, rememberMeCookieSettings) } - res.send('Prijava uspešna') + if (req.session.language) { + await User.updateLanguage(user.id, req.session.language) + delete req.session.language + } + + res.send(req.t('Prijava uspešna')) } )(req, res, next) } @@ -82,6 +97,8 @@ user.logout = async (req, res) => { await User.clearRememberMeToken(rememberMeToken) } + req.session.language = req.user.language + req.logout() // Manually clear session.passport due to bug in current passport version. delete req.session.passport.user @@ -96,7 +113,7 @@ user.list = async (req, res) => { 1 ) res.render('pages/admin/user-list', { - title: 'Seznam slovarjeva', + title: req.t('Seznam uporabnikov'), numberOfAllPages, results }) @@ -104,7 +121,10 @@ user.list = async (req, res) => { user.listAllWithPortalRoles = async (req, res) => { const users = await User.fetchAllWithPortalRoles() - res.render('pages/admin/user-portals', { title: 'Seznam slovarjev', users }) + res.render('pages/admin/user-portals', { + title: req.t('Skrbniki portala'), + users + }) } user.findByUsernameOrEmail = async (req, res) => { @@ -125,7 +145,7 @@ user.adminEdit = async (req, res) => { User.fetchUserRoles(userId) ]) res.render('pages/admin/user-edit', { - title: 'Urejanje uporabnikov', + title: req.t('Uporabnik'), userData, userRoles }) diff --git a/express/middleware/auth.js b/express/middleware/auth.js index 6e832c4..b8fc94f 100644 --- a/express/middleware/auth.js +++ b/express/middleware/auth.js @@ -29,8 +29,8 @@ passport.deserializeUser(async (id, done) => { passport.use( new LocalStrategy( - { usernameField: 'usernameOrEmail' }, - async (usernameOrEmail, password, done) => { + { passReqToCallback: true, usernameField: 'usernameOrEmail' }, + async (req, usernameOrEmail, password, done) => { try { const { rows } = await db.query( 'SELECT id, status, bcrypt_hash FROM "user" WHERE username = $1 OR email = $1', @@ -40,14 +40,17 @@ passport.use( if (!user) { return done(null, false, { - message: 'Nepravilno uporabniško ime, elektronski naslov ali geslo.' + message: req.t( + 'Nepravilno uporabniško ime, elektronski naslov ali geslo.' + ) }) } if (user.status !== 'active') { return done(null, false, { - message: + message: req.t( 'Uporabniški račun še ni aktiviran. Kliknite aktivacijsko povezavo, katero smo vam poslali po elektronski pošti.' + ) }) } @@ -57,7 +60,9 @@ passport.use( ) if (!isCorrectPassword) { return done(null, false, { - message: 'Nepravilno uporabniško ime, elektronski naslov ali geslo.' + message: req.t( + 'Nepravilno uporabniško ime, elektronski naslov ali geslo.' + ) }) } diff --git a/express/middleware/i18n.js b/express/middleware/i18n.js new file mode 100644 index 0000000..258f901 --- /dev/null +++ b/express/middleware/i18n.js @@ -0,0 +1,47 @@ +const i18next = require('i18next') +const i18nextMiddleware = require('i18next-http-middleware') +const i18nextBackend = require('i18next-fs-backend') + +// If you change this one, don't forget to also update the nodemon ignore flag in package.json scripts. +const LOCALES_PATH = 'public/locales' +const DEFAULT_LANGUAGE = 'sl' + +exports.determineRequestLanguage = (req, res, next) => { + const language = + req.user?.language || req.session.language || DEFAULT_LANGUAGE + + req.determinedLanguage = language + res.locals.determinedLanguage = language + next() +} + +const customLanguageDetector = { + name: 'ownDetector', + lookup(req) { + return req.determinedLanguage + } +} +const languageDetector = new i18nextMiddleware.LanguageDetector() +languageDetector.addDetector(customLanguageDetector) + +const inDevEnv = process.env.NODE_ENV === 'development' +i18next + .use(languageDetector) + .use(i18nextBackend) + .init({ + // debug: true, + supportedLngs: ['sl', 'en', 'dev'], + preload: ['sl', 'en'], + ns: ['core', 'extended'], + defaultNS: 'core', + // TODO Reenable separators once keys are refactored (also in frontend init in scripts.js) + nsSeparator: false, + keySeparator: false, + ...(inDevEnv && { saveMissing: true }), + ...(!inDevEnv && { fallbackLng: false }), + detection: { order: ['ownDetector'] }, + backend: { + loadPath: `${LOCALES_PATH}/{{lng}}/{{ns}}.json`, + addPath: `${LOCALES_PATH}/{{lng}}/{{ns}}.json` + } + }) diff --git a/express/middleware/index.js b/express/middleware/index.js index 5a5d861..b7a1730 100644 --- a/express/middleware/index.js +++ b/express/middleware/index.js @@ -2,5 +2,6 @@ const { getInstanceSetting } = require('../models/helpers') exports.enhanceLocals = async (req, res, next) => { res.locals.portalCode = await getInstanceSetting('portal_code') + next() } diff --git a/express/middleware/user.js b/express/middleware/user.js index c71fc72..7ad13d5 100644 --- a/express/middleware/user.js +++ b/express/middleware/user.js @@ -14,6 +14,13 @@ user.enhance = (req, res, next) => { next() } +user.isAuthenticated = (req, res, next) => { + if (req.isAuthenticated()) return next() + + if (req.isAjax) return res.status(400).end() + res.redirect(req.baseUrl || '/') +} + user.isDictionaryAdmin = (req, res, next) => { const { dictionaryId } = req.params const isAdmin = req.user.hasDictionaryRole(dictionaryId, 'administration') @@ -21,7 +28,7 @@ user.isDictionaryAdmin = (req, res, next) => { if (isAdmin) return next() if (req.isAjax) return res.status(400).end() - res.redirect(req.baseUrl) + res.redirect(req.baseUrl || '/') } user.isDictionaryEditor = (req, res, next) => { @@ -31,7 +38,18 @@ user.isDictionaryEditor = (req, res, next) => { if (isEditor) return next() if (req.isAjax) return res.status(400).end() - res.redirect(req.baseUrl) + res.redirect(req.baseUrl || '/') +} + +user.canContentEdit = (req, res, next) => { + const { dictionaryId } = req.params + const isEditor = req.user.hasAnyDictionaryRole(dictionaryId) + const isPortalAdmin = req.user.hasRole('portal admin') + const isDictionariesAdmin = req.user.hasRole('dictionaries admin') + if (isEditor || isPortalAdmin || isDictionariesAdmin) return next() + + if (req.isAjax) return res.status(400).end() + res.redirect(req.baseUrl || '/') } /** diff --git a/express/models/comment.js b/express/models/comment.js index 366c845..5547f2d 100644 --- a/express/models/comment.js +++ b/express/models/comment.js @@ -1,6 +1,10 @@ const db = require('./db') const debug = require('debug')('termPortal:models/comment') const User = require('../models/user') +const { + portalAdminInitialEmail, + portalAdminInitialPassword +} = require('../config/keys') class Comment { // Deserialize flat data into an organized comment object. @@ -311,24 +315,21 @@ async function seedMockUsersInDb() { } async function seedPortalAdmin() { - const MOCK_ADMIN_BASE = 'admin' - const { - rows: [mockAdmin] - } = await db.query('SELECT id FROM "user" WHERE username = $1', [ - MOCK_ADMIN_BASE - ]) + rows: [{ exists }] + } = await db.query( + "SELECT EXISTS (SELECT 1 FROM user_role WHERE role_name = 'portal admin')" + ) - if (mockAdmin) { - return `Portal admin already exists (username: ${MOCK_ADMIN_BASE}, password: ${MOCK_ADMIN_BASE})` - } + if (exists) return 'Skipping creation of portal admin (already exists)' + const MOCK_ADMIN_BASE = 'admin' const adminUser = { username: MOCK_ADMIN_BASE, firstName: MOCK_ADMIN_BASE, lastName: MOCK_ADMIN_BASE, - password: MOCK_ADMIN_BASE, - email: `${MOCK_ADMIN_BASE}@rsdo.com` + password: portalAdminInitialPassword, + email: portalAdminInitialEmail } const userId = await User.create(adminUser) const assignAdminRole = db.query( @@ -345,7 +346,7 @@ async function seedPortalAdmin() { [adminUser.username] ) await Promise.all([assignAdminRole, activateAdminUser]) - return `Successfully seeded portal admin (username: ${MOCK_ADMIN_BASE}, password: ${MOCK_ADMIN_BASE})` + return `Successfully created portal admin (username: ${adminUser.username}, password: ${adminUser.password})` } async function seedConsultants() { diff --git a/express/models/consultancy-entry.js b/express/models/consultancy-entry.js index 9dae4e2..8e44506 100644 --- a/express/models/consultancy-entry.js +++ b/express/models/consultancy-entry.js @@ -70,6 +70,7 @@ class ConsultancyEntry { // Fetch consultancy entry by ID // to_char(time_created,'HH24:MI:SS DD/MM/YYYY') + // TODO i18n date format static async fetchByIdWithFormattedTime(id) { const { rows: fetchedConsEntry } = await db.query( ` @@ -679,6 +680,7 @@ class ConsultancyEntry { } // (Re)index specific consultancy entry into consultancy search index. + // TODO i18n name_sl static async indexIntoSearchEngine(entryId, shouldWait) { const values = [entryId] const text = ` diff --git a/express/models/dictionary.js b/express/models/dictionary.js index df38606..77c7daa 100644 --- a/express/models/dictionary.js +++ b/express/models/dictionary.js @@ -1,8 +1,15 @@ +const { mkdir, open, unlink } = require('fs/promises') const Cursor = require('pg-cursor') const db = require('./db') const { intoDbArray, getInstanceSetting } = require('./helpers') -const { deserialize, bulkIndex } = require('./helpers/dictionary') +const { + deserialize, + bulkIndex, + getExportFilesPath +} = require('./helpers/dictionary') const { readFileIntoDb } = require('./helpers/dictionary/import-file') +const { transformAndAppend } = require('./helpers/dictionary/export-file') +const { origin } = require('../config/keys') // const debug = require('debug')('termPortal:models/dictionary') class Dictionary { @@ -25,6 +32,7 @@ class Dictionary { } // Fetch all dictionaries from DB. + // TODO i18n name_sl static async fetchAll() { // TODO Implement SQL stored procedures or functions. const { rows: fetchedDictionaries } = await db.query(` @@ -65,6 +73,7 @@ class Dictionary { } */ + // TODO i18n name_sl // Fetch all dictionaries from DB for which the user has at least one dictionary role. static async fetchAllByUser(userId) { // TODO Implement SQL stored procedures or functions. @@ -75,7 +84,12 @@ class Dictionary { time_modified, status, count_entries, - count_comments + count_comments, + ( + SELECT administration + FROM user_role + WHERE user_id = $1 and dictionary_id = id + ) as is_admin FROM dictionary WHERE id IN ( SELECT dictionary_id @@ -86,8 +100,8 @@ class Dictionary { ` const values = [userId] const { rows: fetchedDictionaries } = await db.query(text, values) - const deserializedDictionaries = fetchedDictionaries.map( - dictionary => new this(dictionary) + const deserializedDictionaries = fetchedDictionaries.map(dictionary => + deserialize.dictionary(dictionary) ) return deserializedDictionaries } @@ -138,13 +152,13 @@ class Dictionary { } // Fetch all languages from DB. - static async fetchAllLanguages(lang) { + static async fetchAllLanguages(lang, excludeSlovene) { // TODO Implement SQL stored procedures or functions. const { rows: fetchedLanguages } = await db.query(` SELECT id, ${lang} - FROM language + FROM language${excludeSlovene ? "\nWHERE code <> 'sl'" : ''} ORDER BY ${lang}`) const deserializedLanguages = fetchedLanguages.map(language => deserialize.language(language) @@ -413,15 +427,15 @@ class Dictionary { description, l.name_sl languageSl, ds.name_sl domainSecondarySl, - lp.name portalname, + lp.name portalnamesl, lp.code portalcode FROM dictionary d - INNER JOIN dictionary_language dl ON dl.dictionary_id = d.id - INNER JOIN language l ON dl.language_id = l.id + LEFT JOIN dictionary_language dl ON dl.dictionary_id = d.id + LEFT JOIN language l ON dl.language_id = l.id LEFT JOIN dictionary_domain_secondary dds on dds.dictionary_id = d.id LEFT JOIN domain_secondary ds ON dds.domain_secondary_id = ds.id - INNER JOIN domain_primary dp ON d.domain_primary_id = dp.id + LEFT JOIN domain_primary dp ON d.domain_primary_id = dp.id LEFT JOIN linked_dictionary ld ON ld.target_dictionary_id = d.id LEFT JOIN linked_portal lp ON ld.linked_portal_id = lp.id WHERE d.id = $1` @@ -447,7 +461,7 @@ class Dictionary { d.author, dp.name_sl domain_primary, description, - lp.name portalname, + lp.name portalnamesl, lp.code portalcode FROM dictionary d @@ -509,7 +523,7 @@ class Dictionary { d.author, dp.name_sl domain_primary, description, - lp.name portalname, + lp.name portalnamesl, lp.code portalcode FROM dictionary d @@ -577,12 +591,13 @@ class Dictionary { distinct (d.id), d.${lang} dictionarysl, d.count_entries, + d.count_comments, to_char(d.time_modified,'YYYY-MM-DD') time_modified, d.issn, d.author, dp.name_sl domain_primary, description, - lp.name portalname, + lp.name portalnamesl, lp.code portalcode FROM dictionary d @@ -590,7 +605,7 @@ class Dictionary { INNER JOIN domain_primary dp ON d.domain_primary_id = dp.id LEFT JOIN linked_dictionary ld ON ld.target_dictionary_id = d.id LEFT JOIN linked_portal lp ON ld.linked_portal_id = lp.id - WHERE d.name_sl LIKE '%' || $1 || '%' ${queryAppend} + WHERE d.status = 'published' AND LOWER(d.name_sl) LIKE '%' || LOWER($1) || '%' ${queryAppend} ORDER BY ${orderBy} LIMIT $2 OFFSET $3` @@ -643,7 +658,7 @@ class Dictionary { INNER JOIN domain_primary dp ON d.domain_primary_id = dp.id LEFT JOIN linked_dictionary ld ON ld.target_dictionary_id = d.id LEFT JOIN linked_portal lp ON ld.linked_portal_id = lp.id - WHERE d.name_sl LIKE '%' || $1 || '%' ${queryAppend}` + WHERE d.status = 'published' AND d.name_sl LIKE '%' || $1 || '%' ${queryAppend}` const { rows } = await db.query(text, [searchQuery]) @@ -656,8 +671,8 @@ class Dictionary { count(d.id) FROM dictionary d - INNER JOIN dictionary_language dl ON dl.dictionary_id = d.id - INNER JOIN domain_primary dp ON d.domain_primary_id = dp.id + LEFT JOIN dictionary_language dl ON dl.dictionary_id = d.id + LEFT JOIN domain_primary dp ON d.domain_primary_id = dp.id LEFT JOIN linked_dictionary ld ON ld.target_dictionary_id = d.id LEFT JOIN linked_portal lp ON ld.linked_portal_id = lp.id` @@ -666,6 +681,23 @@ class Dictionary { return rows[0] } + static async fetchAllDictionariesPublishedCount() { + const text = ` + SELECT + count(d.id) + FROM + dictionary d + LEFT JOIN dictionary_language dl ON dl.dictionary_id = d.id + LEFT JOIN domain_primary dp ON d.domain_primary_id = dp.id + LEFT JOIN linked_dictionary ld ON ld.target_dictionary_id = d.id + LEFT JOIN linked_portal lp ON ld.linked_portal_id = lp.id + WHERE d.status = 'published'` + + const { rows } = await db.query(text) + + return rows[0] + } + // Fetch single dictionary data for editing structure from DB. static async fetchDictionaryWithEditStructure(dictionaryId) { const text = ` @@ -734,10 +766,11 @@ class Dictionary { SELECT d.id, ${lang} dictionarysl, - dp.name_sl domain_primary + dp.name_sl domain_primary, + d.count_comments FROM dictionary d INNER JOIN domain_primary dp ON d.domain_primary_id = dp.id - where d.time_published is not NULL + where d.time_published is not NULL and d.status = 'published' ORDER BY d.time_published desc limit 3` @@ -984,15 +1017,62 @@ class Dictionary { } } + // Import entries from extraction into DB. + static async importFromExtraction(dictionaryId, userId, termCandidates) { + await db.transaction(async dbClient => { + // TODO Depending on performance, consider batching requests. + for (const { + kanonicnaoblika: term, + definicija: other + } of termCandidates) { + const values = [ + dictionaryId, + false, + 'suggestion', + term || null, + userId, + null, + null, + null, + null, + [], + null, + null, + null, + [], + other || null, + [], + null, + null, + null + ] + + const text = `SELECT entry_new (${db.genParamStr(values)})` + + await dbClient.query(text, values) + } + + await this.updateMetadataAfterModifyingEntries(dictionaryId, dbClient) + }) + } + // Index entries of specific dictionary from DB into search engine. static async indexIntoSearchEngine(dictionaryId) { - // TODO Luka: I expect "source" needing a rework once linked portals and dictionaries start working. - const source = { - code: await getInstanceSetting('portal_code'), - name: await getInstanceSetting('portal_name') - } const dbClient = await db.getClient() try { + const { + rows: [{ code: linkedPortalCode, name: linkedPortalName } = {}] + } = await dbClient.query( + 'SELECT p.code, p.name FROM linked_dictionary d LEFT JOIN linked_portal p ON p.id = d.linked_portal_id WHERE d.id = $1', + [dictionaryId] + ) + + // TODO i18n Luka: index portal name for both languages? + const source = { + code: linkedPortalCode ?? (await getInstanceSetting('portal_code')), + name: linkedPortalName ?? (await getInstanceSetting('portal_name_sl')) + } + const queryValues = [dictionaryId] const dictionaryQueryText = ` @@ -1060,7 +1140,7 @@ class Dictionary { ) ) FROM entry_foreign ef - LEFT JOIN LANGUAGE l ON l.id = ef.language_id + LEFT JOIN language l ON l.id = ef.language_id WHERE entry_id = e.id ) foreign_entries FROM entry e @@ -1154,6 +1234,41 @@ class Dictionary { return result } + static async fetchFilteredPaginationDomainLabels( + dictionaryId, + query, + resultsPerPage, + page + ) { + const { + rows: [{ result }] + } = await db.query( + ` + SELECT jsonb_build_object( + 'pages_total', ( + SELECT CEIL(COUNT(*) / $3::float) + FROM domain_label + WHERE dictionary_id = $1 and name LIKE '%' || $2 || '%' + ), + 'results', ARRAY( + SELECT jsonb_build_object( + 'id', id, + 'name', name, + 'isVisible', is_visible + ) + FROM domain_label + WHERE dictionary_id = $1 and LOWER(name) LIKE '%' || LOWER($2) || '%' + ORDER BY name + LIMIT $3 + OFFSET $4 + ) + ) result`, + [dictionaryId, query, resultsPerPage, resultsPerPage * (page - 1)] + ) + + return result + } + // Fetch all secondary domains from DB. static async fetchAllSecondaryDomains(resultsPerPage, page) { const { @@ -1184,6 +1299,38 @@ class Dictionary { return result } + // Fetch filtered secondary domains from DB. + static async fetchFilteredSecondaryDomains(query, resultsPerPage, page) { + const { + rows: [{ result }] + } = await db.query( + ` + SELECT jsonb_build_object( + 'pages_total', ( + SELECT CEIL(COUNT(*) / $2::float) + FROM domain_secondary + WHERE name_sl LIKE '%' || $1 || '%' + ), + 'results', ARRAY( + SELECT jsonb_build_object( + 'id', id, + 'isApproved', approved, + 'nameSl', name_sl, + 'nameEn', name_en + ) + FROM domain_secondary + WHERE LOWER(name_sl) LIKE '%' || LOWER($1) || '%' + ORDER BY name_sl + LIMIT $2 + OFFSET $3 + ) + ) result`, + [query, resultsPerPage, resultsPerPage * (page - 1)] + ) + + return result + } + static async updateDomainLabel(dictionaryId, data) { const dbClient = await db.getClient() try { @@ -1343,24 +1490,54 @@ class Dictionary { } } - static async fetchAllImports(dictionaryId) { - const text = ` - SELECT - time_started, - status, - delete_existing_entries, - file_format, - count_valid_entries - FROM import_file_job - WHERE dictionary_id = $1` + // static async fetchAllImports(dictionaryId) { + // const text = ` + // SELECT + // time_started, + // status, + // delete_existing_entries, + // file_format, + // count_valid_entries + // FROM import_file_job + // WHERE dictionary_id = $1` - const value = [dictionaryId] - const { rows: fetchedImports } = await db.query(text, value) + // const value = [dictionaryId] + // const { rows: fetchedImports } = await db.query(text, value) - const deserializedImports = fetchedImports.map(oneImport => - deserialize.imports(oneImport) + // const deserializedImports = fetchedImports.map(oneImport => + // deserialize.imports(oneImport) + // ) + // return deserializedImports + // } + + static async fetchAllImports(dictionaryId, resultsPerPage, page) { + const { + rows: [{ result }] + } = await db.query( + ` + SELECT jsonb_build_object( + 'pages_total', ( + SELECT CEIL(COUNT(*) / $2::float) + FROM import_file_job + WHERE dictionary_id = $1 + ), + 'results', ARRAY( + SELECT jsonb_build_object( + 'time_started', time_started, + 'status', status, + 'delete_existing_entries', delete_existing_entries, + 'file_format', file_format, + 'count_valid_entries', count_valid_entries + ) + FROM import_file_job + WHERE dictionary_id = $1 + LIMIT $2 + OFFSET $3 + ) + ) result`, + [dictionaryId, resultsPerPage, resultsPerPage * (page - 1)] ) - return deserializedImports + return result } static async delete(dictionaryId) { @@ -1368,6 +1545,464 @@ class Dictionary { const value = [dictionaryId] await db.query(text, value) } + + // static async fetchExports(dictionaryId) { + // const text = ` + // SELECT + // id, + // status, + // to_char(time_created, 'FMDD. FMMM. YYYY') date_created, + // entry_count, + // is_valid_filter, + // is_published_filter, + // is_terminology_reviewed_filter, + // is_language_reviewed_filter, + // status_filter, + // export_file_format + // FROM dictionary_export + // WHERE dictionary_id = $1 + // ORDER BY id DESC` + // const value = [dictionaryId] + + // const { rows: fetchedExports } = await db.query(text, value) + + // const deserializedExports = fetchedExports.map(eachExport => + // deserialize.exports(eachExport) + // ) + // return deserializedExports + // } + + static async fetchExports(dictionaryId, resultsPerPage, page) { + const { + rows: [{ result }] + } = await db.query( + ` + SELECT jsonb_build_object( + 'pages_total', ( + SELECT CEIL(COUNT(*) / $2::float) + FROM dictionary_export + WHERE dictionary_id = $1 + ), + 'results', ARRAY( + SELECT jsonb_build_object( + 'id', id, + 'status', status, + 'time_created', time_created, + 'entry_count', entry_count, + 'is_valid_filter', is_valid_filter, + 'is_published_filter', is_published_filter, + 'is_terminology_reviewed_filter', is_terminology_reviewed_filter, + 'is_language_reviewed_filter', is_language_reviewed_filter, + 'status_filter', status_filter, + 'export_file_format', export_file_format + ) + FROM dictionary_export + WHERE dictionary_id = $1 + LIMIT $2 + OFFSET $3 + ) + ) result`, + [dictionaryId, resultsPerPage, resultsPerPage * (page - 1)] + ) + return result + } + + static async beginExport(dictionaryId, exportParams) { + const text = ` + INSERT INTO dictionary_export ( + dictionary_id, + is_valid_filter, + is_published_filter, + is_terminology_reviewed_filter, + is_language_reviewed_filter, + status_filter, + export_file_format + ) + VALUES ($1, $2, $3, $4, $5, $6, $7) + RETURNING id + ` + + const values = [ + dictionaryId, + exportParams.isValidFilter, + exportParams.isPublishedFilter, + exportParams.isTerminologyReviewedFilter, + exportParams.isLanguageReviewedFilter, + exportParams.statusFilter, + exportParams.exportFileFormat + ] + + const { + rows: [{ id }] + } = await db.query(text, values) + + return id + } + + static async processExport(exportId) { + const dbClient = await db.getClient() + let exportFilePath + let exportFile + let cursor + + try { + const exportQueryText = ` + UPDATE dictionary_export + SET status = 'in progress', time_started = NOW() + WHERE id = $1 + RETURNING + dictionary_id, + is_valid_filter, + is_published_filter, + is_terminology_reviewed_filter, + is_language_reviewed_filter, + status_filter, + export_file_format + ` + + const exportQueryParams = [exportId] + + const { + rows: [ + { + dictionary_id: dictionaryId, + is_valid_filter: isValidFilter, + is_published_filter: isPublishedFilter, + is_terminology_reviewed_filter: isTerminologyReviewedFilter, + is_language_reviewed_filter: isLanguageReviewedFilter, + status_filter: statusFilter, + export_file_format: exportFileFormat + } + ] + } = await dbClient.query(exportQueryText, exportQueryParams) + + const dictionaryQueryText = ` + SELECT + entries_have_domain_labels, + entries_have_label, + entries_have_definition, + entries_have_synonyms, + entries_have_links, + entries_have_other, + entries_have_foreign_languages, + entries_have_foreign_definitions, + entries_have_foreign_synonyms, + entries_have_images, + entries_have_audio, + entries_have_videos + FROM dictionary + WHERE id = $1 + ` + + const dictionaryQueryParams = [dictionaryId] + + const { + rows: [dictionaryStructure] + } = await dbClient.query(dictionaryQueryText, dictionaryQueryParams) + + const exportFields = { + domainLabels: dictionaryStructure.entries_have_domain_labels, + label: dictionaryStructure.entries_have_label, + definition: dictionaryStructure.entries_have_definition, + synonyms: dictionaryStructure.entries_have_synonyms, + links: dictionaryStructure.entries_have_links, + other: dictionaryStructure.entries_have_other, + foreignTerms: dictionaryStructure.entries_have_foreign_languages, + foreignDefinitions: + dictionaryStructure.entries_have_foreign_definitions, + foreignSynonyms: dictionaryStructure.entries_have_foreign_synonyms, + images: dictionaryStructure.entries_have_images, + audio: dictionaryStructure.entries_have_audio, + videos: dictionaryStructure.entries_have_videos + } + + let entryQueryText = ` + SELECT + term${ + exportFields.label + ? `, + label` + : '' + }${ + exportFields.definition + ? `, + definition` + : '' + }${ + exportFields.synonyms + ? `, + synonym synonyms` + : '' + }${ + exportFields.other + ? `, + other` + : '' + }${ + exportFields.images + ? `, + image` + : '' + }${ + exportFields.audio + ? `, + audio` + : '' + }${ + exportFields.videos + ? `, + video` + : '' + }${ + exportFields.domainLabels + ? `, + ARRAY( + SELECT name + FROM entry_domain_label edl + LEFT JOIN domain_label dl ON dl.id = edl.domain_label_id + WHERE entry_id = e.id + ) domain_labels` + : '' + }${ + exportFields.links + ? `, + ARRAY( + SELECT jsonb_build_object( + 'link', link, + 'type', type) + FROM entry_link + WHERE entry_id = e.id + ) links` + : '' + }${ + exportFields.foreignTerms + ? `, + ARRAY( + SELECT jsonb_build_object( + 'lang_code', l.code, + 'terms', ef.term${ + exportFields.foreignDefinitions + ? `, + 'definition', ef.definition` + : '' + }${ + exportFields.foreignSynonyms + ? `, + 'synonyms', ef.synonym` + : '' + } + ) + FROM dictionary_language dl + LEFT JOIN language l ON l.id = dl.language_id + LEFT JOIN entry_foreign ef ON ef.language_id = l.id + WHERE dl.dictionary_id = $1 AND ef.entry_id = e.id + ORDER BY dl.selection_order + ) foreign_entries` + : '' + } + FROM entry e + WHERE + e.dictionary_id = $1` + + const entryQueryParams = [dictionaryId] + + if (isValidFilter !== null) { + entryQueryParams.push(isValidFilter) + entryQueryText += ` AND is_valid = $${entryQueryParams.length}` + } + if (isPublishedFilter !== null) { + entryQueryParams.push(isPublishedFilter) + entryQueryText += ` AND is_published = $${entryQueryParams.length}` + } + if (isTerminologyReviewedFilter !== null) { + entryQueryParams.push(isTerminologyReviewedFilter) + entryQueryText += ` AND is_terminology_reviewed = $${entryQueryParams.length}` + } + if (isLanguageReviewedFilter !== null) { + entryQueryParams.push(isLanguageReviewedFilter) + entryQueryText += ` AND is_language_reviewed = $${entryQueryParams.length}` + } + if (statusFilter !== null) { + entryQueryParams.push(statusFilter) + entryQueryText += ` AND status = $${entryQueryParams.length}` + } + entryQueryText += '\nORDER BY term' + + const exportFilesPath = getExportFilesPath(dictionaryId) + await mkdir(exportFilesPath, { recursive: true }) + exportFilePath = `${exportFilesPath}/${exportId}` + exportFile = await open(exportFilePath, 'ax') + + const isXmlFileFormat = exportFileFormat === 'xml' + const isDsvFileFormat = ['csv', 'tsv'].includes(exportFileFormat) + const isTbxFileFormat = exportFileFormat === 'tbx' + let dsvConfig + if (isXmlFileFormat) { + const openingMarkup = + '\n\n' + await exportFile.write(openingMarkup) + } else if (isDsvFileFormat) { + if (exportFileFormat === 'csv') dsvConfig = { delimiter: ';' } + else if (exportFileFormat === 'tsv') dsvConfig = { delimiter: '\t' } + + if (exportFields.foreignTerms) { + const { rows: languages } = await dbClient.query( + ` + SELECT l.code + FROM dictionary d + LEFT JOIN dictionary_language dl ON dl.dictionary_id = d.id + LEFT JOIN language l ON l.id = dl.language_id + WHERE d.id = $1 + ORDER BY dl.selection_order + `, + [dictionaryId] + ) + + dsvConfig.languageCodes = languages.map(language => language.code) + } + + const fieldNamesArr = ['term'] + if (exportFields.domainLabels) fieldNamesArr.push('domainLabels') + if (exportFields.label) fieldNamesArr.push('label') + if (exportFields.definition) fieldNamesArr.push('def') + if (exportFields.synonyms) fieldNamesArr.push('syns') + if (exportFields.links) fieldNamesArr.push('links') + if (exportFields.other) fieldNamesArr.push('other') + dsvConfig.languageCodes?.forEach(languageCode => { + fieldNamesArr.push(`[${languageCode}]fTerms`) + if (exportFields.foreignDefinitions) { + fieldNamesArr.push(`[${languageCode}]fDef`) + } + if (exportFields.foreignSynonyms) { + fieldNamesArr.push(`[${languageCode}]fSyns`) + } + }) + if (exportFields.images) fieldNamesArr.push('images') + if (exportFields.audio) fieldNamesArr.push('audios') + if (exportFields.videos) fieldNamesArr.push('videos') + const headerLine = `${fieldNamesArr.join(dsvConfig.delimiter)}\n` + await exportFile.write(headerLine) + } else if (isTbxFileFormat) { + const { + rows: [tbxMetadata] + } = await dbClient.query( + ` + SELECT + name_sl, + name_en, + author, + to_char(time_modified, 'YYYY-MM-DD') modified_date_string, + status, + ( + SELECT to_char(time_created, 'YYYY-MM-DD') + FROM dictionary_export de + WHERE de.id = $1 + ) export_date_string + FROM dictionary + WHERE id = $2 + `, + [exportId, dictionaryId] + ) + const portalName = await getInstanceSetting('portal_name_sl') + const authorsString = tbxMetadata.author?.join(', ') + const urlPublished = + tbxMetadata.status === 'published' + ? new URL(`/slovarji/${dictionaryId}/o-slovarju`, origin).href + : null + + let openingMarkup = '\n' + openingMarkup += '\n' + openingMarkup += '\n\n' + openingMarkup += '\n\n' + openingMarkup += `${tbxMetadata.name_sl}\n` + openingMarkup += `${tbxMetadata.name_en}\n` + openingMarkup += '\n\n' + openingMarkup += `

Datum objave: ${tbxMetadata.export_date_string}

\n` + openingMarkup += + '

Avtorske pravice: Delo je dostopno pod pogoji licence CC BY 4.0.

\n' + openingMarkup += '
\n\n' + openingMarkup += `

Vir: ${portalName}

\n` + if (authorsString) openingMarkup += `

Avtorji: ${authorsString}

\n` + openingMarkup += `

Datum objave: ${tbxMetadata.modified_date_string}

\n` + if (urlPublished) { + openingMarkup += `

Mesto objave: ${urlPublished}

\n` + } + openingMarkup += '
\n
\n' + openingMarkup += '
\n\n\n' + + await exportFile.write(openingMarkup) + } + + cursor = dbClient.query(new Cursor(entryQueryText, entryQueryParams)) + + let entries = [] + let batchEntriesCount + let entriesWritten = 0 + do { + // Keep getting and writing entries to file in batches of 100. + entries = await cursor.read(100) + batchEntriesCount = entries.length + + if (batchEntriesCount) { + await transformAndAppend( + entries, + exportFields, + exportFile, + exportFileFormat, + dsvConfig + ) + entriesWritten += batchEntriesCount + } + } while (batchEntriesCount === 100) + + if (isXmlFileFormat) { + const closingMarkup = '
\n' + await exportFile.write(closingMarkup) + } else if (isTbxFileFormat) { + const closingMarkup = '\n\n\n' + await exportFile.write(closingMarkup) + } + + await dbClient.query( + "UPDATE dictionary_export SET status = 'finished', time_finished = NOW(), entry_count = $1 WHERE id = $2", + [entriesWritten, exportId] + ) + } catch (error) { + await cursor?.close() + await dbClient.query( + "UPDATE dictionary_export SET status = 'failed', time_finished = NOW() WHERE id = $1", + [exportId] + ) + await unlink(exportFilePath) + throw error + } finally { + dbClient.release() + await exportFile?.close() + } + } + + static async fetchExportDownloadMetadata(exportId) { + const { + rows: [fetchedMetadata] + } = await db.query( + ` + SELECT + e.status, + e.dictionary_id, + d.name_sl_short name_string, + to_char(e.time_created, 'YYYYMMDDHH24MISS') time_string, + e.export_file_format + FROM dictionary_export e + LEFT JOIN dictionary d ON d.id = e.dictionary_id + WHERE e.id = $1`, + [exportId] + ) + + const deserializedMetadata = + deserialize.exportDownloadMetadata(fetchedMetadata) + + return deserializedMetadata + } } module.exports = Dictionary diff --git a/express/models/email.js b/express/models/email.js index 3a70baa..2a881fd 100644 --- a/express/models/email.js +++ b/express/models/email.js @@ -3,15 +3,28 @@ const htmlToText = require('nodemailer-html-to-text').htmlToText() const { smtpHost, smtpPort, - smtpTlsRejectUnauthorized, + smtpUser, + smtpPassword, + smtpSecure, + smtpRequireTls, + smtpAllowInvalidCerts, smtpFrom } = require('../config/keys') const options = { host: smtpHost, port: smtpPort, - tls: { rejectUnauthorized: smtpTlsRejectUnauthorized } + secure: smtpSecure, + requireTLS: smtpRequireTls, + tls: { rejectUnauthorized: !smtpAllowInvalidCerts } } +if (smtpUser || smtpPassword) { + options.auth = { + user: smtpUser, + pass: smtpPassword + } +} + const defaults = { from: smtpFrom } const transporter = nodemailer.createTransport(options, defaults) diff --git a/express/models/entry.js b/express/models/entry.js index 45d977d..c20a283 100644 --- a/express/models/entry.js +++ b/express/models/entry.js @@ -184,7 +184,12 @@ Entry.fetchFull = async entryId => { SELECT jsonb_strip_nulls( jsonb_build_object( 'version', version, - 'version_time', version_time + 'version_time', version_time, + 'version_author', ( + SELECT username + FROM "user" + WHERE id = (version_snapshot['version_author'])::int + ) ) ) FROM entry_version_history @@ -232,6 +237,7 @@ Entry.fetchFullWithOrderedForeignLanguages = async entryId => { 'audio', e.audio, 'video', e.video, 'time_modified', e.time_modified, + 'external_url', e.external_url, 'domain_labels', ARRAY( SELECT name FROM entry_domain_label edl @@ -421,6 +427,8 @@ Entry.deleteAllLinks = async dictionaryId => { // (Re)index specific entry into entry search index. Entry.indexIntoSearchEngine = async (entryId, shouldWait) => { + // TODO If this method is ever used for linked portals/dictionaries, + // TODO rework the source object below (already done in Dictionary.indexIntoSearchEngine). const values = [entryId] const text = ` SELECT @@ -465,7 +473,7 @@ Entry.indexIntoSearchEngine = async (entryId, shouldWait) => { ) ) FROM entry_foreign ef - LEFT JOIN LANGUAGE l ON l.id = ef.language_id + LEFT JOIN language l ON l.id = ef.language_id WHERE entry_id = e.id ) ) @@ -495,10 +503,11 @@ Entry.indexIntoSearchEngine = async (entryId, shouldWait) => { const { dictionary, primary_domain: primaryDomain } = dataToIndex let { entry } = dataToIndex - // TODO Luka: I expect "source" needing a rework once linked portals and dictionaries start working. + + // TODO i18n Luka: index portal name for both languages? const source = { code: await getInstanceSetting('portal_code'), - name: await getInstanceSetting('portal_name') + name: await getInstanceSetting('portal_name_sl') } entry = prepareEntryForIndexing(entry) @@ -618,13 +627,17 @@ Entry.update = async (userId, entry) => { // Fetch a single version snapshot of a single entry from DB. Entry.fetchVersionSnapshot = async (entryId, version) => { const { - rows: [{ version_snapshot: historySnapshot }] + rows: [{ version_snapshot: historySnapshot, author }] } = await db.query( - 'SELECT version_snapshot FROM entry_version_history WHERE entry_id = $1 and version = $2', + `SELECT v.version_snapshot, ( + SELECT u.username + FROM "user" u + WHERE u.id = (v.version_snapshot['version_author'])::int) as author + FROM entry_version_history v WHERE v.entry_id = $1 and v.version = $2`, [entryId, version] ) - return historySnapshot + return { data: historySnapshot, author } } /* Fetch by language and entry Id. Note that this version includes the language name */ diff --git a/express/models/extraction.js b/express/models/extraction.js index 98258da..6954716 100644 --- a/express/models/extraction.js +++ b/express/models/extraction.js @@ -12,13 +12,14 @@ const { getFileNamesInFolder, getFileStatsInFolder } = require('./helpers/extraction') +const { extractionApiOrigin } = require('../config/keys') const Extraction = {} // Fetch all extractions for a specific user. Extraction.fetchAllForUser = async userId => { const { rows: fetchedExtractions } = await db.query( - 'SELECT id, name, status, corpus_id, oss_params, time_started, time_finished FROM extraction WHERE user_id = $1 ORDER BY id', + 'SELECT id, name, status, corpus_id, oss_params, time_started, time_finished FROM extraction WHERE user_id = $1 ORDER BY status ASC, id DESC, time_finished DESC', [userId] ) @@ -74,19 +75,19 @@ Extraction.fetch = async id => { return deserialize.extraction(fetchedExtraction) } -// Fetch author email of a specific extraction entry from DB. -Extraction.fetchAuthorEmail = async id => { +// Fetch data of the author of a specific extraction entry from DB. +Extraction.fetchAuthorData = async id => { const { - rows: [{ email }] + rows: [authorData] } = await db.query( - `SELECT u.email + `SELECT u.email, u.language FROM extraction e LEFT JOIN "user" u ON u.id = e.user_id WHERE e.id = $1`, [id] ) - return email + return authorData } // Update extraction entry in DB. @@ -160,6 +161,18 @@ Extraction.fetchTermCandidatesCount = async function (extractionId) { return termCandidates.length } +// Fetch term candidates slice for a specific extraction. +Extraction.fetchTermCandidatesSlice = async function ( + extractionId, + fromIndex, + toIndex +) { + const termCandidatesJson = await this.fetchTermCandidatesJson(extractionId) + const termCandidates = JSON.parse(termCandidatesJson).terminoloski_kandidati + const termCandidatesSlice = termCandidates.slice(fromIndex, toIndex) + return termCandidatesSlice +} + // Mark extraction from own documents as began. Extraction.beginOwn = async (extractionId, documentsNames) => { let timeStarted @@ -195,20 +208,20 @@ Extraction.beginOwn = async (extractionId, documentsNames) => { Extraction.beginOss = async extractionId => { let timeStarted await db.transaction(async dbClient => { - const insertExtractionJob = dbClient.query( - 'INSERT INTO extraction_job (extraction_id, job_type, filename) VALUES ($1, $2, $3)', - [extractionId, 'oss term candidates', ''] - ) - const updateExtraction = dbClient.query( - "UPDATE extraction SET status = 'in progress', time_started = NOW() WHERE id = $1 RETURNING time_started", - [extractionId] - ) - ;[ { rows: [{ time_started: timeStarted }] } - ] = await Promise.all([updateExtraction, insertExtractionJob]) + ] = await Promise.all([ + dbClient.query( + "UPDATE extraction SET status = 'in progress', time_started = NOW() WHERE id = $1 RETURNING time_started", + [extractionId] + ), + dbClient.query( + 'INSERT INTO extraction_job (extraction_id, job_type, filename) VALUES ($1, $2, $3)', + [extractionId, 'oss term candidates', ''] + ) + ]) }) return timeStarted @@ -235,6 +248,7 @@ Extraction.processOwn = async function (extractionId, extractionName) { const documentNames = await this.fetchAllDocumentsNames(extractionId) const conllusPath = getConllusPath(extractionId) const conllusPaths = [] + const MAX_BODY_LENGTH = 10 ** 9 // 1 GB // Using remote API, transform each document into conllu format. for (const documentName of documentNames) { const filePath = `${documentsPath}/${documentName}` @@ -242,17 +256,18 @@ Extraction.processOwn = async function (extractionId, extractionName) { form.append('file', createReadStream(filePath), documentName) try { const { data: data1 } = await axios.post( - 'http://rsdo.lhrs.feri.um.si:8080/datotekaVConlluAsync', + `${extractionApiOrigin}/datotekaVConlluAsync`, form, { headers: { ...form.getHeaders() - } + }, + maxBodyLength: MAX_BODY_LENGTH } ) const remotejobId = +data1.check_job_url.split('/').at(-1) await db.query( - "UPDATE extraction_job SET status = 'in progress', remote_job_id = $1 WHERE extraction_id = $2 AND job_type = $3 AND filename = $4", + "UPDATE extraction_job SET status = 'in progress', remote_job_id = $1, time_started = NOW() WHERE extraction_id = $2 AND job_type = $3 AND filename = $4", [remotejobId, extractionId, 'doc to conllu', documentName] ) @@ -262,10 +277,15 @@ Extraction.processOwn = async function (extractionId, extractionName) { while (true) { await sleep(5) const { data: data2 } = await axios.get( - `http://rsdo.lhrs.feri.um.si:8080/job/${remotejobId}` + `${extractionApiOrigin}/job/${remotejobId}` ) if (data2.finished_on) { - if (data2.job_status !== 'finished processing (OK)') throw Error() + if (data2.job_status !== 'finished processing (OK)') { + throw Error( + `Remote job with id ${remotejobId} failed with result:\n${data2.job_result}` + ) + } + // TODO Read the response as a stream and try to parse it's contents into a file (write stream)('stream-json' package?). const fileSavePath = `${conllusPath}/${documentName}.conllu` await writeFile(fileSavePath, data2.job_result) @@ -277,7 +297,8 @@ Extraction.processOwn = async function (extractionId, extractionName) { break } } - } catch { + } catch (error) { + logExtractionError(error, extractionId, 'doc to conllu', documentName) await failTheJob(extractionId, 'doc to conllu', documentName) } } @@ -309,15 +330,18 @@ Extraction.processOwn = async function (extractionId, extractionName) { try { const { data: data3 } = await axios.post( - 'http://rsdo.lhrs.feri.um.si:8080/izlusciAsync', + `${extractionApiOrigin}/izlusciAsync`, { conllus: conllusArr, - prepovedaneBesede: Array.from(stopTermsSet) - } + prepovedaneBesede: Array.from(stopTermsSet), + // TODO Enabled for all cases. Add a switch for users later. + definicije: true + }, + { maxBodyLength: MAX_BODY_LENGTH } ) const remotejobId = +data3.check_job_url.split('/').at(-1) await db.query( - "UPDATE extraction_job SET status = 'in progress', remote_job_id = $1 WHERE extraction_id = $2 AND job_type = $3 AND filename = $4", + "UPDATE extraction_job SET status = 'in progress', remote_job_id = $1, time_started = NOW() WHERE extraction_id = $2 AND job_type = $3 AND filename = $4", [remotejobId, extractionId, 'conllus to term candidates', ''] ) @@ -325,14 +349,21 @@ Extraction.processOwn = async function (extractionId, extractionName) { while (true) { await sleep(5) const { data: data4 } = await axios.get( - `http://rsdo.lhrs.feri.um.si:8080/job/${remotejobId}` + `${extractionApiOrigin}/job/${remotejobId}` ) if (data4.finished_on) { - if (data4.job_status !== 'finished processing (OK)') throw Error() + const { job_result: jobResult } = data4 + if ( + data4.job_status !== 'finished processing (OK)' || + !jobResult.terminoloski_kandidati + ) { + throw Error( + `Remote job with id ${remotejobId} failed with result:\n${jobResult}` + ) + } + // TODO Read the response as a stream and try to parse it's contents into a file (write stream)('stream-json' package?). - await writeFile(termCandidatesPath, JSON.stringify(data4.job_result)) - // TODO Once returned JSON is properly formed, use the bottom line instead. - // await writeFile(termCandidatesPath, data4.job_result.terminoloski_kandidati) + await writeFile(termCandidatesPath, JSON.stringify(jobResult)) await db.query( "UPDATE extraction_job SET status = 'finished', time_finished = NOW() WHERE extraction_id = $1 AND job_type = $2 AND filename = $3", [extractionId, 'conllus to term candidates', ''] @@ -340,8 +371,10 @@ Extraction.processOwn = async function (extractionId, extractionName) { break } } - } catch { + } catch (error) { + logExtractionError(error, extractionId, 'conllus to term candidates') await failTheJob(extractionId, 'conllus to term candidates', '') + await skipConcordancerJob(extractionId) await failExtraction(extractionId) return } @@ -350,7 +383,7 @@ Extraction.processOwn = async function (extractionId, extractionName) { // Start concondancer corpus processing. try { await db.query( - "UPDATE extraction_job SET status = 'in progress' WHERE extraction_id = $1 AND job_type = $2 AND filename = $3", + "UPDATE extraction_job SET status = 'in progress', time_started = NOW() WHERE extraction_id = $1 AND job_type = $2 AND filename = $3", [extractionId, 'concordancer', ''] ) console.log('CREATING CORPUS') @@ -361,32 +394,90 @@ Extraction.processOwn = async function (extractionId, extractionName) { } = await axios.post('http://concordancer:5000/dashboard/corpus', { title: extractionName }) + + // Wait for creation of corpus. + while (true) { + console.log('SLEEP FOR 5 SECS') + await sleep(5) + const { + data: { status } + } = await axios.get( + `http://concordancer:5000/dashboard/corpus/${corpusId}` + ) + + if (status === 'Creating') continue + if (status === 'Active') break + throw Error('Error creating concorcander corpus') + } console.log('CORPUS CREATED') - console.log('SLEEP FOR 10 SECS') - await sleep(10) + + const inProgressStatusList = [ + 'Waiting', + 'Importing', + 'ImportingCompleted', + 'Indexing', + 'IndexingCompleted' + ] for (const conlluPath of conllusPaths) { const textPathParts = conlluPath.split('/') textPathParts[0] = '/data' const textPath = textPathParts.join('/') console.log('ADDING TEXT') - await axios.post( + const { + data: { + entityInfo: { id: textId } + } + } = await axios.post( `http://concordancer:5000/dashboard/corpus/${corpusId}/text`, { sourceFile: textPath } ) + + // Wait for text ingestion. + while (true) { + console.log('SLEEP FOR 5 SECS') + await sleep(5) + const { + data: { status } + } = await axios.get( + `http://concordancer:5000/dashboard/corpus/${corpusId}/text/${textId}` + ) + + if (inProgressStatusList.includes(status)) continue + if (status === 'Active') break + throw Error('Error importing concorcander text') + } console.log('TEXT ADDED') - console.log('SLEEP FOR 10 SECS') - await sleep(10) } const termListPathParts = termCandidatesPath.split('/') termListPathParts[0] = '/data' const termListPath = termListPathParts.join('/') console.log('ADDING TERMS') - await axios.post( + const { + data: { + entityInfo: { id: termListId } + } + } = await axios.post( `http://concordancer:5000/dashboard/corpus/${corpusId}/termList`, { sourceFile: termListPath } ) + + // Wait for term list ingestion. + while (true) { + console.log('SLEEP FOR 5 SECS') + await sleep(5) + const { + data: { status } + } = await axios.get( + `http://concordancer:5000/dashboard/corpus/${corpusId}/termList/${termListId}` + ) + + if (inProgressStatusList.includes(status)) continue + if (status === 'Active') break + throw Error('Error importing concorcander text') + } console.log('TERMS ADDED') + await db.query( "UPDATE extraction_job SET status = 'finished', time_finished = NOW() WHERE extraction_id = $1 AND job_type = $2 AND filename = $3", [extractionId, 'concordancer', ''] @@ -397,9 +488,8 @@ Extraction.processOwn = async function (extractionId, extractionName) { [corpusId, extractionId] ) console.log('EXTRACTION SUCCESSFUL') - } catch (e) { - console.log('EXTRACTION ERROR') - console.log(e) + } catch (error) { + logExtractionError(error, extractionId, 'concordancer') await failTheJob(extractionId, 'concordancer', '') await failExtraction(extractionId) } @@ -435,15 +525,17 @@ Extraction.processOss = async function (extractionId, ossParams) { ...(ossParams.documentType && { vrste: ossParams.documentType }), ...(ossParams.keywords && { kljucneBesede: ossParams.keywords }), ...(ossParams.domainUdk && { udk: ossParams.domainUdk }), - ...(stopTerms.length && { prepovedaneBesede: stopTerms }) + ...(stopTerms.length && { prepovedaneBesede: stopTerms }), + // TODO Enabled for all cases. Add a switch for users later. + definicije: true }) - const extractApiUrl = `http://rsdo.lhrs.feri.um.si:8080/oss/izlusciPoIskanjuAsync?${searchParams}` + const extractApiUrl = `${extractionApiOrigin}/oss/izlusciPoIskanjuAsync?${searchParams}` try { const { data: data1 } = await axios.get(extractApiUrl) const remotejobId = +data1.check_job_url.split('/').at(-1) await db.query( - "UPDATE extraction_job SET status = 'in progress', remote_job_id = $1 WHERE extraction_id = $2 AND job_type = $3 AND filename = $4", + "UPDATE extraction_job SET status = 'in progress', remote_job_id = $1, time_started = NOW() WHERE extraction_id = $2 AND job_type = $3 AND filename = $4", [remotejobId, extractionId, 'oss term candidates', ''] ) @@ -451,15 +543,21 @@ Extraction.processOss = async function (extractionId, ossParams) { while (true) { await sleep(5) const { data: data2 } = await axios.get( - `http://rsdo.lhrs.feri.um.si:8080/job/${remotejobId}` + `${extractionApiOrigin}/job/${remotejobId}` ) if (data2.finished_on) { - if (data2.job_status !== 'finished processing (OK)') throw Error() + if ( + data2.job_status !== 'finished processing (OK)' || + !Array.isArray(data2.job_result?.terminoloski_kandidati) + ) { + throw Error( + `Remote job with id ${remotejobId} failed with result:\n${data2.job_result}` + ) + } + // TODO Read the response as a stream and try to parse it's contents into a file (write stream)('stream-json' package?). const termCandidatesPath = getTermCandidatesPath(extractionId) await writeFile(termCandidatesPath, JSON.stringify(data2.job_result)) - // TODO Once returned JSON is properly formed, use the bottom line instead. - // await writeFile(termCandidatesPath, data4.job_result.terminoloski_kandidati) await db.query( "UPDATE extraction_job SET status = 'finished', time_finished = NOW() WHERE extraction_id = $1 AND job_type = $2 AND filename = $3", [extractionId, 'oss term candidates', ''] @@ -474,7 +572,8 @@ Extraction.processOss = async function (extractionId, ossParams) { "UPDATE extraction SET status = 'finished', time_finished = NOW() WHERE id = $1", [extractionId] ) - } catch { + } catch (error) { + logExtractionError(error, extractionId, 'oss term candidates') await failTheJob(extractionId, 'oss term candidates', '') await failExtraction(extractionId) } @@ -487,6 +586,13 @@ async function failTheJob(extractionId, jobType, documentName) { ) } +async function skipConcordancerJob(extractionId) { + await db.query( + "UPDATE extraction_job SET status = 'skipped' WHERE extraction_id = $1 AND job_type = $2", + [extractionId, 'concordancer'] + ) +} + async function failExtraction(extractionId) { await db.query( "UPDATE extraction SET status = 'failed', time_finished = NOW() WHERE id = $1", @@ -498,4 +604,18 @@ function sleep(seconds) { return new Promise(resolve => setTimeout(resolve, seconds * 1000)) } +function logExtractionError(error, extractionId, jobType, filename) { + // eslint-disable-next-line no-console + console.error( + Error(`Failed extraction job: + extractionId: ${extractionId}, + jobType: ${jobType}, + filename: ${filename}`) + ) + + if (error.isAxiosError) error = Error(`Axios message: ${error.message}`) + // eslint-disable-next-line no-console + console.error(error) +} + module.exports = Extraction diff --git a/express/models/helpers/dictionary/export-file.js b/express/models/helpers/dictionary/export-file.js new file mode 100644 index 0000000..e038c97 --- /dev/null +++ b/express/models/helpers/dictionary/export-file.js @@ -0,0 +1,486 @@ +const xmlFlow = require('xml-flow') +const xss = require('xss') + +exports.transformAndAppend = async ( + entries, + exportFields, + exportFile, + exportFileFormat, + dsvConfig +) => { + let transformEntry + if (exportFileFormat === 'xml') transformEntry = intoXml + else if (dsvConfig) transformEntry = intoDsv(dsvConfig) + else if (exportFileFormat === 'tbx') transformEntry = intoTbx + else throw Error('Specified export format not supported yet') + + for (const entry of entries) { + const transformedEntry = transformEntry(entry, exportFields) + await exportFile.write(`${transformedEntry}\n`) + } +} + +function intoXml(entry, exportFields) { + const entryObj = { $name: 'entry', $markup: [] } + + if (entry.term) { + entryObj.$markup.push({ term: entry.term }) + } + + if (exportFields.domainLabels && entry.domain_labels.length) { + const domainLabelsObj = { + $name: 'domainLabels', + $markup: entry.domain_labels.map(domainLabel => { + return { domainLabel } + }) + } + entryObj.$markup.push(domainLabelsObj) + } + + if (exportFields.label && entry.label) { + entryObj.$markup.push({ label: entry.label }) + } + + if (exportFields.definition && entry.definition) { + entryObj.$markup.push({ def: entry.definition }) + } + + if (exportFields.synonyms && entry.synonyms?.length) { + const SynonymsObj = { + $name: 'syns', + $markup: entry.synonyms.map(synonym => { + return { syn: synonym } + }) + } + entryObj.$markup.push(SynonymsObj) + } + + if (exportFields.links && entry.links.length) { + const LinksObj = { + $name: 'links', + $markup: entry.links.map(linkObj => { + return { + $name: 'link', + $attrs: { type: linkObj.type }, + $text: linkObj.link + } + }) + } + entryObj.$markup.push(LinksObj) + } + + if (exportFields.other && entry.other) { + entryObj.$markup.push({ other: entry.other }) + } + + if (exportFields.foreignTerms && entry.foreign_entries.length) { + const fLangsObj = { + $name: 'fLangs', + $markup: entry.foreign_entries.map(fEntryObj => { + const fLangObj = { + $name: 'fLang', + $attrs: { lang: fEntryObj.lang_code }, + $markup: [] + } + + if (fEntryObj.terms) { + const fTermsObj = { + $name: 'fTerms', + $markup: fEntryObj.terms.map(term => { + return { fTerm: term } + }) + } + fLangObj.$markup.push(fTermsObj) + } + + if (exportFields.foreignDefinitions && fEntryObj.definition) { + fLangObj.$markup.push({ fDef: fEntryObj.definition }) + } + + if (exportFields.foreignSynonyms && fEntryObj.synonyms?.length) { + const fSynsObj = { + $name: 'fSyns', + $markup: fEntryObj.synonyms.map(synonym => { + return { fSyn: synonym } + }) + } + fLangObj.$markup.push(fSynsObj) + } + + return fLangObj + }) + } + entryObj.$markup.push(fLangsObj) + } + + const shouldExportImages = exportFields.images && entry.image?.length + const shouldExportAudio = exportFields.audio && entry.audio?.length + const shouldExportVideos = exportFields.videos && entry.video?.length + const shouldCreateMm = + shouldExportImages || shouldExportAudio || shouldExportVideos + + if (shouldCreateMm) { + const MmObj = { + $name: 'mm', + $markup: [] + } + + if (shouldExportImages) { + entry.image.forEach(el => { + MmObj.$markup.push({ image: el }) + }) + } + + if (shouldExportAudio) { + entry.audio.forEach(el => { + MmObj.$markup.push({ audio: el }) + }) + } + + if (shouldExportVideos) { + entry.video.forEach(el => { + MmObj.$markup.push({ video: el }) + }) + } + + entryObj.$markup.push(MmObj) + } + + return xmlFlow.toXml(entryObj, { escape: str => str }) +} + +function intoDsv({ delimiter, languageCodes }) { + return function (entry, exportFields) { + const fieldsArr = [intoDsvField(entry.term)] + + if (exportFields.domainLabels) { + fieldsArr.push( + intoDsvField( + entry.domain_labels.reduce((agg, domainLabel, index) => { + agg += `${index ? '\n' : ''}${domainLabel}` + return agg + }, '') + ) + ) + } + + if (exportFields.label) fieldsArr.push(intoDsvField(entry.label)) + + if (exportFields.definition) fieldsArr.push(intoDsvField(entry.definition)) + + if (exportFields.synonyms) { + fieldsArr.push( + intoDsvField( + entry.synonyms?.reduce((agg, synonym, index) => { + agg += `${index ? '\n' : ''}${synonym}` + return agg + }, '') + ) + ) + } + + if (exportFields.links) { + fieldsArr.push( + intoDsvField( + entry.links.reduce((agg, linkObj, index) => { + agg += `${index ? '\n' : ''}[${linkObj.type[0]}t]${linkObj.link}` + return agg + }, '') + ) + ) + } + + if (exportFields.other) fieldsArr.push(intoDsvField(entry.other)) + + languageCodes?.forEach(languageCode => { + const fEntryObj = entry.foreign_entries.find( + entryObj => entryObj.lang_code === languageCode + ) + + fieldsArr.push( + intoDsvField( + fEntryObj?.terms?.reduce((agg, term, index) => { + agg += `${index ? '\n' : ''}${term}` + return agg + }, '') + ) + ) + if (exportFields.foreignDefinitions) { + fieldsArr.push(intoDsvField(fEntryObj?.definition)) + } + if (exportFields.foreignSynonyms) { + fieldsArr.push( + intoDsvField( + fEntryObj?.synonyms?.reduce((agg, synonym, index) => { + agg += `${index ? '\n' : ''}${synonym}` + return agg + }, '') + ) + ) + } + }) + + if (exportFields.images) { + fieldsArr.push( + intoDsvField( + entry.image?.reduce((agg, el, index) => { + agg += `${index ? '\n' : ''}${el}` + return agg + }, '') + ) + ) + } + + if (exportFields.audio) { + fieldsArr.push( + intoDsvField( + entry.audio?.reduce((agg, el, index) => { + agg += `${index ? '\n' : ''}${el}` + return agg + }, '') + ) + ) + } + + if (exportFields.videos) { + fieldsArr.push( + intoDsvField( + entry.video?.reduce((agg, el, index) => { + agg += `${index ? '\n' : ''}${el}` + return agg + }, '') + ) + ) + } + + return fieldsArr.join(delimiter) + } +} + +function intoDsvField(fieldString) { + if (!fieldString) return '' + return `"${fieldString.replaceAll('"', '""')}"` +} + +const tbxLinkTypeMap = { + related: 'relatedConcept', + broader: 'relatedConceptBroader', + narrow: 'relatedConceptNarrower' +} +const brTagPattern = /]*>/ +function intoTbx(entry, exportFields) { + const entryObj = { $name: 'termEntry', $markup: [] } + + if (exportFields.images) { + entry.image?.forEach(el => { + entryObj.$markup.push({ + $name: 'xref', + $attrs: { type: 'xGraphic' }, + $text: el + }) + }) + } + + if (exportFields.audio) { + entry.audio?.forEach(el => { + entryObj.$markup.push({ + $name: 'xref', + $attrs: { type: 'xAudio' }, + $text: el + }) + }) + } + + if (exportFields.videos) { + entry.video?.forEach(el => { + entryObj.$markup.push({ + $name: 'xref', + $attrs: { type: 'xVideo' }, + $text: el + }) + }) + } + + const slLangObj = { + $name: 'langSet', + $attrs: { 'xml:lang': 'sl' }, + $markup: [] + } + + if (exportFields.label && entry.label) { + slLangObj.$markup.push({ + $name: 'descrip', + $attrs: { type: 'explanation' }, + $text: intoTbxMixed(entry.label) + }) + } + + if (exportFields.definition && entry.definition) { + slLangObj.$markup.push({ + $name: 'descrip', + $attrs: { type: 'definition' }, + $text: intoTbxMixed(entry.definition) + }) + } + + if (exportFields.links) { + entry.links.forEach(linkObj => { + slLangObj.$markup.push({ + $name: 'descrip', + $attrs: { type: tbxLinkTypeMap[linkObj.type] }, + $text: intoTbxMixed(linkObj.link) + }) + }) + } + + if (exportFields.other && entry.other) { + const otherLines = entry.other.split(brTagPattern) + otherLines.forEach(line => { + slLangObj.$markup.push({ + $name: 'descrip', + $attrs: { type: 'other' }, + $text: intoTbxMixed(line) + }) + }) + } + + const shouldExportTerm = entry.term + const shouldExportDomainLabels = + exportFields.domainLabels && entry.domain_labels.length + const shouldCreateTermNtig = shouldExportTerm || shouldExportDomainLabels + + if (shouldCreateTermNtig) { + const termNtigObj = { + $name: 'ntig', + $markup: [{ $name: 'termGrp', $markup: [] }] + } + const termGrpMarkup = termNtigObj.$markup[0].$markup + + if (shouldExportTerm) { + termGrpMarkup.push({ term: intoTbxMixed(entry.term) }) + termGrpMarkup.push({ + $name: 'termNote', + $attrs: { type: 'termType' }, + $text: 'entryTerm' + }) + } + + if (shouldExportDomainLabels) { + entry.domain_labels.forEach(domainLabel => { + termGrpMarkup.push({ + $name: 'termNote', + $attrs: { type: 'domain' }, + $text: domainLabel + }) + }) + } + + slLangObj.$markup.push(termNtigObj) + } + + if (exportFields.synonyms) { + entry.synonyms?.forEach(synonym => { + slLangObj.$markup.push({ + $name: 'ntig', + $markup: [ + { + $name: 'termGrp', + $markup: [ + { term: intoTbxMixed(synonym) }, + { + $name: 'termNote', + $attrs: { type: 'termType' }, + $text: 'synonym' + } + ] + } + ] + }) + }) + } + + entryObj.$markup.push(slLangObj) + + if (exportFields.foreignTerms) { + entry.foreign_entries.forEach(fEntryObj => { + const langSetObj = { + $name: 'langSet', + $attrs: { 'xml:lang': fEntryObj.lang_code }, + $markup: [] + } + + if (exportFields.foreignDefinitions && fEntryObj.definition) { + langSetObj.$markup.push({ + $name: 'descrip', + $attrs: { type: 'definition' }, + $text: intoTbxMixed(fEntryObj.definition) + }) + } + + fEntryObj.terms?.forEach(term => { + langSetObj.$markup.push({ + $name: 'ntig', + $markup: [ + { + $name: 'termGrp', + $markup: [{ term: intoTbxMixed(term) }] + } + ] + }) + }) + + if (exportFields.foreignSynonyms) { + fEntryObj.synonyms?.forEach(synonym => { + langSetObj.$markup.push({ + $name: 'ntig', + $markup: [ + { + $name: 'termGrp', + $markup: [ + { term: intoTbxMixed(synonym) }, + { + $name: 'termNote', + $attrs: { type: 'termType' }, + $text: 'synonym' + } + ] + } + ] + }) + }) + } + + entryObj.$markup.push(langSetObj) + }) + } + + return xmlFlow.toXml(entryObj, { escape: str => str }) +} + +const tbxRichFilter = new xss.FilterXSS({ + whiteList: { + sup: [], + sub: [], + b: [], + i: [] + }, + stripIgnoreTag: true, + stripIgnoreTagBody: ['script', 'style'] +}) +const tagPattern = /<\s*(\/)?\s*([^\s/>]+)\s*>/g +function intoTbxMixed(mixedContentStr) { + return tbxRichFilter + .process(mixedContentStr) + .replace(tagPattern, tbxMixedReplacer) +} + +const tbxMixedTypeMap = { + sup: 'superscript', + sub: 'subscript', + b: 'bold', + i: 'italics' +} +function tbxMixedReplacer(match, closingSlash, tagName) { + if (closingSlash) return '' + return `` +} diff --git a/express/models/helpers/dictionary/import-file.js b/express/models/helpers/dictionary/import-file.js index f9bf533..4635572 100644 --- a/express/models/helpers/dictionary/import-file.js +++ b/express/models/helpers/dictionary/import-file.js @@ -250,7 +250,7 @@ function customTagHandler(tag, html, { isWhite, isClosing }) { const matchUrl = html.match(/href="?(?https?:\/\/.*?)"?[\s>]/) const url = matchUrl ? xss.escapeAttrValue(matchUrl.groups.url) : undefined - return `` + return `` } function toText(markupObj) { diff --git a/express/models/helpers/dictionary/index.js b/express/models/helpers/dictionary/index.js index 26d8a77..1526d41 100644 --- a/express/models/helpers/dictionary/index.js +++ b/express/models/helpers/dictionary/index.js @@ -1,7 +1,22 @@ const { removeHtmlTags } = require('../../helpers') const { searchEngineClient, ENTRY_INDEX } = require('../../search-engine') +const { DATA_FILES_PATH } = require('../../../config/settings') exports.deserialize = { + dictionary(dictionary) { + const deserializedDictionary = { + id: dictionary.id, + nameSl: dictionary.name_sl, + timeModified: dictionary.time_modified, + status: dictionary.status, + countEntries: dictionary.count_entries, + countComments: dictionary.count_comments, + isAdmin: dictionary.is_admin + } + + return deserializedDictionary + }, + primaryDomain(domain) { const deserializedDomain = { id: domain.id, @@ -102,16 +117,78 @@ exports.deserialize = { return deserializedDomainLabel }, - imports(oneImport) { - const deserializedImports = { - timeStarted: oneImport.time_started, - status: oneImport.status, - deleteExisting: oneImport.delete_existing_entries, - fileFormat: oneImport.file_format, - countValidEntries: oneImport.count_valid_entries + // imports(oneImport) { + // const deserializedImports = { + // timeStarted: oneImport.time_started, + // status: oneImport.status, + // deleteExisting: oneImport.delete_existing_entries, + // fileFormat: oneImport.file_format, + // countValidEntries: oneImport.count_valid_entries + // } + + // return deserializedImports + // }, + + exports(oneExport) { + const deserializedExports = { + id: oneExport.id, + status: oneExport.status, + dateCreated: oneExport.date_created, + entryCount: oneExport.entry_count ?? '', + typeString: `${ + oneExport.is_valid_filter === true + ? 2 + : oneExport.is_valid_filter === false + ? 3 + : 1 + }${ + oneExport.is_published_filter === true + ? 2 + : oneExport.is_published_filter === false + ? 3 + : 1 + }${ + oneExport.status_filter === 'complete' + ? 2 + : oneExport.status_filter === 'in_edit' + ? 3 + : 1 + }${ + oneExport.is_terminology_reviewed_filter === true + ? 2 + : oneExport.is_terminology_reviewed_filter === false + ? 3 + : 1 + }${ + oneExport.is_language_reviewed_filter === true + ? 2 + : oneExport.is_language_reviewed_filter === false + ? 3 + : 1 + }-${ + oneExport.export_file_format === 'xml' + ? 1 + : oneExport.export_file_format === 'csv' + ? 2 + : oneExport.export_file_format === 'tsv' + ? 3 + : 0 + }` } - return deserializedImports + return deserializedExports + }, + + exportDownloadMetadata(metadata) { + const deserializedMetadata = { + exportStatus: metadata.status, + dictionaryId: metadata.dictionary_id, + nameString: metadata.name_string, + timeString: metadata.time_string, + fileFormat: metadata.export_file_format + } + + return deserializedMetadata } } @@ -194,3 +271,7 @@ function prepareEntryForIndexing(entry) { } exports.prepareEntryForIndexing = prepareEntryForIndexing + +exports.getExportFilesPath = dictId => { + return `${DATA_FILES_PATH}/dict_export/${dictId}` +} diff --git a/express/models/helpers/extraction.js b/express/models/helpers/extraction.js index 75c7ca6..90c6574 100644 --- a/express/models/helpers/extraction.js +++ b/express/models/helpers/extraction.js @@ -1,4 +1,5 @@ const { readdir, stat } = require('fs/promises') +const path = require('path') const { partial } = require('filesize') const { DATA_FILES_PATH } = require('../../config/settings') @@ -43,17 +44,21 @@ exports.getFileNamesInFolder = async folderPath => { return filenames } +exports.getFileStats = getFileStats + exports.getFileStatsInFolder = async folderPath => { const filenames = await readdir(folderPath) - const fileStats = await Promise.all( - filenames.map(async filename => { - const filePath = `${folderPath}/${filename}` - const { mtimeMs: timeModified, size } = await stat(filePath) - const sizeHumanReadable = formatFileSize(size) - const fileStats = { filename, size: sizeHumanReadable, timeModified } - return fileStats - }) - ) + const filePaths = filenames.map(filename => `${folderPath}/${filename}`) + const fileStats = await Promise.all(filePaths.map(getFileStats)) + fileStats.sort((a, b) => a.timeModified - b.timeModified) + return fileStats +} + +async function getFileStats(filePath) { + const filename = path.basename(filePath) + const { mtimeMs: timeModified, size } = await stat(filePath) + const sizeHumanReadable = formatFileSize(size) + const fileStats = { filename, size: sizeHumanReadable, timeModified } return fileStats } diff --git a/express/models/helpers/portal/index.js b/express/models/helpers/portal/index.js index 8af4333..080ee95 100644 --- a/express/models/helpers/portal/index.js +++ b/express/models/helpers/portal/index.js @@ -9,8 +9,10 @@ exports.aggregateSettings = settings => { exports.deserialize = { settings(settings) { const deserializedSettings = { - name: settings.portal_name, - description: settings.portal_description, + nameSl: settings.portal_name_sl, + nameEn: settings.portal_name_en, + descriptionSl: settings.portal_description_sl, + descriptionEn: settings.portal_description_en, code: settings.portal_code, isExtractionEnabled: settings.is_extraction_enabled, isDictionariesEnabled: settings.is_dictionaries_enabled, diff --git a/express/models/helpers/search/generate-query/consultancy/all.js b/express/models/helpers/search/generate-query/consultancy/all.js index 3d87ae8..7663e31 100644 --- a/express/models/helpers/search/generate-query/consultancy/all.js +++ b/express/models/helpers/search/generate-query/consultancy/all.js @@ -9,7 +9,7 @@ module.exports = function (filters, hitsPerPage, page) { filter: [] } }, - sort: ['_score', 'timeCreated'] + sort: ['_score', { timeCreated: 'desc' }] } if (filters.status) { diff --git a/express/models/helpers/search/generate-query/consultancy/phrase.js b/express/models/helpers/search/generate-query/consultancy/phrase.js index 8a291c0..9cf2a10 100644 --- a/express/models/helpers/search/generate-query/consultancy/phrase.js +++ b/express/models/helpers/search/generate-query/consultancy/phrase.js @@ -45,7 +45,7 @@ module.exports = function (searchString, filters, hitsPerPage, page) { filter: [] } }, - sort: ['_score', 'timeCreated'] + sort: ['_score', { timeCreated: 'desc' }] } if (filters.status) { diff --git a/express/models/helpers/search/generate-query/consultancy/wildcard.js b/express/models/helpers/search/generate-query/consultancy/wildcard.js index 138565c..1d34fd8 100644 --- a/express/models/helpers/search/generate-query/consultancy/wildcard.js +++ b/express/models/helpers/search/generate-query/consultancy/wildcard.js @@ -39,7 +39,7 @@ module.exports = function (searchString, filters, hitsPerPage, page) { filter: [] } }, - sort: ['_score', 'timeCreated'] + sort: ['_score', { timeCreated: 'desc' }] } if (filters.status) { diff --git a/express/models/helpers/search/generate-query/consultancy/words.js b/express/models/helpers/search/generate-query/consultancy/words.js index 4f27ecf..5d7c5b3 100644 --- a/express/models/helpers/search/generate-query/consultancy/words.js +++ b/express/models/helpers/search/generate-query/consultancy/words.js @@ -54,7 +54,7 @@ module.exports = function (searchString, filters, hitsPerPage, page) { filter: [] } }, - sort: ['_score', 'timeCreated'] + sort: ['_score', { timeCreated: 'desc' }] } if (filters.status) { diff --git a/express/models/helpers/search/generate-query/editor/all.js b/express/models/helpers/search/generate-query/editor/all.js index 2af3598..3ed95c2 100644 --- a/express/models/helpers/search/generate-query/editor/all.js +++ b/express/models/helpers/search/generate-query/editor/all.js @@ -2,7 +2,7 @@ const { EDITOR_MAX_HITS } = require('../../../../../config/settings') module.exports = function (dictionaryId, filters, searchFieldFilters) { const queryDsl = { - _source: ['id', 'isValid', 'isPublished', 'term'], + _source: ['id', 'isValid', 'isPublished', 'term', 'homonymSort'], fields: ['foreignEntries.terms'], script_fields: { commentActivityIndicator: { diff --git a/express/models/helpers/user/index.js b/express/models/helpers/user/index.js index c0db29e..49d6825 100644 --- a/express/models/helpers/user/index.js +++ b/express/models/helpers/user/index.js @@ -7,6 +7,7 @@ exports.deserialize = { lastName: user.last_name, email: user.email, hitsPerPage: user.hits_per_page, + language: user.language, userRoles: user.user_roles, assignedConsultancyEntries: user.assigned_consultancy_entries } @@ -21,7 +22,8 @@ exports.deserialize = { firstName: userData.first_name, lastName: userData.last_name, email: userData.email, - password: userData.password + status: userData.status, + language: userData.language } return deserializedData diff --git a/express/models/inter_instance_sync.js b/express/models/inter_instance_sync.js index 14d9dba..1bd80c7 100644 --- a/express/models/inter_instance_sync.js +++ b/express/models/inter_instance_sync.js @@ -73,6 +73,9 @@ class InterInstanceSync { 'SELECT l.code AS language_code, ef.entry_id, ef.term, ef.definition, ef.synonym FROM entry_foreign AS ef' + ' INNER JOIN language AS l ON l.id = ef.language_id' + ' WHERE entry_id = ANY($1::int[]) ORDER BY ef.entry_id, l.code' + const sqlLinks = + 'SELECT entry_id, link, type FROM entry_link WHERE entry_id = ANY($1::int[]) ORDER BY entry_id, type, link' + const { rows: entryList } = await db.query(sqlEntries, [ dictionaryId, since @@ -83,6 +86,7 @@ class InterInstanceSync { const { rows: translationList } = await db.query(sqlTranslations, [ entryIds ]) + const { rows: linkList } = await db.query(sqlLinks, [entryIds]) let currentEntryId = 0 let lastEntryId = 0 translationList.forEach(t => { @@ -102,6 +106,23 @@ class InterInstanceSync { } entry.translations.push(t) }) + linkList.forEach(l => { + currentEntryId = l.entry_id + const entry = entryList.find(e => { + return e.id === currentEntryId + }) + if (!entry) return + if (lastEntryId === 0) { + entry.links = [] + lastEntryId = l.entry_id + } else if (currentEntryId !== lastEntryId) { + // new entry translations : save previous + entry.links = [] + lastEntryId = currentEntryId + currentEntryId = l.entry_id + } + entry.links.push({ type: l.type, link: l.link }) + }) return entryList } } diff --git a/express/models/portal.js b/express/models/portal.js index c74714a..aa91db8 100644 --- a/express/models/portal.js +++ b/express/models/portal.js @@ -12,9 +12,11 @@ Portal.fetchInstanceSettings = async () => { WHERE name IN ( - 'portal_name', + 'portal_name_sl', + 'portal_name_en', 'portal_code', - 'portal_description', + 'portal_description_sl', + 'portal_description_en', 'is_consultancy_enabled', 'is_dictionaries_enabled', 'is_extraction_enabled')` @@ -32,9 +34,11 @@ Portal.updateInstaceSettings = async payload => { const isConsultancyEnabled = payload.isConsultancyEnabled ? 'T' : 'F' const values = [ - payload.portalName, + payload.portalNameSl, + payload.portalNameEn, payload.portalCode, - payload.portalDescription, + payload.portalDescriptionSl, + payload.portalDescriptionEn, isExtractionEnabled, isDictionariesEnabled, isConsultancyEnabled @@ -47,17 +51,21 @@ Portal.updateInstaceSettings = async payload => { value = CASE name WHEN - 'portal_name' THEN $1 + 'portal_name_sl' THEN $1 WHEN - 'portal_code' THEN $2 + 'portal_name_en' THEN $2 WHEN - 'portal_description' THEN $3 + 'portal_code' THEN $3 WHEN - 'is_extraction_enabled' THEN $4 + 'portal_description_sl' THEN $4 WHEN - 'is_dictionaries_enabled' THEN $5 + 'portal_description_en' THEN $5 WHEN - 'is_consultancy_enabled' THEN $6 + 'is_extraction_enabled' THEN $6 + WHEN + 'is_dictionaries_enabled' THEN $7 + WHEN + 'is_consultancy_enabled' THEN $8 ELSE value END` diff --git a/express/models/system/eurotermbank.js b/express/models/system/eurotermbank.js index db089f8..cce8821 100644 --- a/express/models/system/eurotermbank.js +++ b/express/models/system/eurotermbank.js @@ -47,7 +47,7 @@ Eurotermbank.push = async () => { 'definition', ef.definition, 'synonyms', ef.synonym) FROM entry_foreign ef - LEFT JOIN LANGUAGE l ON l.id = ef.language_id + LEFT JOIN language l ON l.id = ef.language_id WHERE entry_id = e.id ) foreign_entries FROM entry e diff --git a/express/models/user.js b/express/models/user.js index d81ba03..3615f77 100644 --- a/express/models/user.js +++ b/express/models/user.js @@ -17,6 +17,7 @@ User.create = async user => { user.email || null, bcryptHash || null ] + if (user.language) values.push(user.language) const text = `INSERT INTO "user" ( username, @@ -24,6 +25,7 @@ User.create = async user => { last_name, email, bcrypt_hash + ${user.language ? ', language' : ''} ) VALUES (${db.genParamStr(values)}) RETURNING id` @@ -67,7 +69,10 @@ User.fetchByActivationToken = async activationToken => { // Activate user account. User.activateAccount = async user => { - await db.query(`UPDATE "user" SET status = 'active' WHERE id = $1`, [user.id]) + await db.query( + `UPDATE "user" SET status = 'active', time_activated = NOW() WHERE id = $1`, + [user.id] + ) } // Generate a user remember me token. @@ -101,6 +106,7 @@ User.fetchDeserializedDataById = async userId => { u.last_name, u.email, u.hits_per_page, + u.language, ARRAY( SELECT jsonb_build_object( 'roleName', r.role_name, @@ -258,9 +264,9 @@ User.updatePortalRoles = async rolesPerUser => { User.fetchUser = async userId => { const text = ` - SELECT id, username, first_name, last_name, email + SELECT id, username, first_name, last_name, email, status, language FROM "user" - WHERE id=$1` + WHERE id = $1` const value = [userId] const { rows } = await db.query(text, value) @@ -271,17 +277,33 @@ User.fetchUser = async userId => { } User.updateUser = async (userId, payload) => { - const text = ` + const previousStatusText = 'SELECT status FROM "user" WHERE id = $1' + const { rows } = await db.query(previousStatusText, [userId]) + const previousStatus = rows[0].status + + let statusValue + if (previousStatus === 'registered') { + statusValue = !payload.status ? 'registered' : 'active' + } else statusValue = !payload.status ? 'inactive' : 'active' + + const updateText = ` UPDATE "user" SET username = $2, first_name = $3, - last_name = $4 + last_name = $4, + status = $5 WHERE id = $1` - const values = [userId, payload.username, payload.firstName, payload.lastName] + const values = [ + userId, + payload.username, + payload.firstName, + payload.lastName, + statusValue + ] - await db.query(text, values) + await db.query(updateText, values) } User.fetchUserRoles = async userId => { @@ -467,4 +489,12 @@ User.updateHitsPerPage = async (username, hitsPerPageAmount) => { ) } +// Update user's language. +User.updateLanguage = async (userId, languageCode) => { + await db.query('UPDATE "user" SET language = $1 WHERE id = $2', [ + languageCode, + userId + ]) +} + module.exports = User diff --git a/express/package-lock.json b/express/package-lock.json index 67dc4ce..87f3a87 100644 --- a/express/package-lock.json +++ b/express/package-lock.json @@ -22,6 +22,9 @@ "form-data": "^4.0.0", "helmet": "^5.0.2", "http-errors": "^2.0.0", + "i18next": "^22.4.10", + "i18next-fs-backend": "^2.1.1", + "i18next-http-middleware": "^3.2.2", "ioredis": "^4.27.8", "morgan": "^1.10.0", "multer": "^1.4.3", @@ -64,6 +67,17 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/types": { "version": "7.17.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", @@ -1678,6 +1692,38 @@ "node": ">= 6" } }, + "node_modules/i18next": { + "version": "22.4.10", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.10.tgz", + "integrity": "sha512-3EqgGK6fAJRjnGgfkNSStl4mYLCjUoJID338yVyLMj5APT67HUtWoqSayZewiiC5elzMUB1VEUwcmSCoeQcNEA==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.20.6" + } + }, + "node_modules/i18next-fs-backend": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-2.1.1.tgz", + "integrity": "sha512-FTnj+UmNgT3YRml5ruRv0jMZDG7odOL/OP5PF5mOqvXud2vHrPOOs68Zdk6iqzL47cnnM0ZVkK2BAvpFeDJToA==" + }, + "node_modules/i18next-http-middleware": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.2.2.tgz", + "integrity": "sha512-OW2sWnbns+PuLi77T+/ni4Mi+TNJ6Q6XNGdZicMv9FD+QfZrFrynBVqryHB/bfflszx2Zswg/kxtCildVLdhSA==" + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3039,6 +3085,11 @@ "node": ">=4" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "node_modules/registry-auth-token": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", @@ -3789,6 +3840,14 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==" }, + "@babel/runtime": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, "@babel/types": { "version": "7.17.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", @@ -5032,6 +5091,24 @@ "debug": "4" } }, + "i18next": { + "version": "22.4.10", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.10.tgz", + "integrity": "sha512-3EqgGK6fAJRjnGgfkNSStl4mYLCjUoJID338yVyLMj5APT67HUtWoqSayZewiiC5elzMUB1VEUwcmSCoeQcNEA==", + "requires": { + "@babel/runtime": "^7.20.6" + } + }, + "i18next-fs-backend": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-2.1.1.tgz", + "integrity": "sha512-FTnj+UmNgT3YRml5ruRv0jMZDG7odOL/OP5PF5mOqvXud2vHrPOOs68Zdk6iqzL47cnnM0ZVkK2BAvpFeDJToA==" + }, + "i18next-http-middleware": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.2.2.tgz", + "integrity": "sha512-OW2sWnbns+PuLi77T+/ni4Mi+TNJ6Q6XNGdZicMv9FD+QfZrFrynBVqryHB/bfflszx2Zswg/kxtCildVLdhSA==" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -6100,6 +6177,11 @@ "redis-errors": "^1.0.0" } }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "registry-auth-token": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", diff --git a/express/package.json b/express/package.json index bcddb87..5b67ece 100644 --- a/express/package.json +++ b/express/package.json @@ -4,8 +4,8 @@ "private": true, "scripts": { "start": "node ./bin/www", - "devstart": "nodemon --ignore data_files/ --inspect=0.0.0.0:9229 ./bin/www", - "devstart-wait": "nodemon --ignore data_files/ --inspect-brk=0.0.0.0:9229 ./bin/www" + "devstart": "nodemon --ignore data_files/ --ignore public/locales/dev --inspect=0.0.0.0:9229 ./bin/www", + "devstart-wait": "nodemon --ignore data_files/ --ignore public/locales/dev --inspect-brk=0.0.0.0:9229 ./bin/www" }, "dependencies": { "@opensearch-project/opensearch": "^2.1.0", @@ -22,6 +22,9 @@ "form-data": "^4.0.0", "helmet": "^5.0.2", "http-errors": "^2.0.0", + "i18next": "^22.4.10", + "i18next-fs-backend": "^2.1.1", + "i18next-http-middleware": "^3.2.2", "ioredis": "^4.27.8", "morgan": "^1.10.0", "multer": "^1.4.3", diff --git a/express/public/documents/RSDO_smernice.pdf b/express/public/documents/RSDO_smernice.pdf index 9ab1cf6..15feafe 100644 Binary files a/express/public/documents/RSDO_smernice.pdf and b/express/public/documents/RSDO_smernice.pdf differ diff --git a/express/public/documents/Podrocja_TP_2022-11-28.pdf b/express/public/documents/Tabela.pdf similarity index 79% rename from express/public/documents/Podrocja_TP_2022-11-28.pdf rename to express/public/documents/Tabela.pdf index 3a20f29..c544d2e 100644 Binary files a/express/public/documents/Podrocja_TP_2022-11-28.pdf and b/express/public/documents/Tabela.pdf differ diff --git a/express/public/documents/dictionary_schema.xsd b/express/public/documents/dictionary_schema.xsd new file mode 100644 index 0000000..5b414ca --- /dev/null +++ b/express/public/documents/dictionary_schema.xsd @@ -0,0 +1,285 @@ + + + + + Dictionary (root element) + + + + + + Entry + + + + + + Slovenian Term + + + + + Headword Group + + + + + Wordforms + + + + + Accent + + + + + Pronunciation + + + + + + + Domain Labels + + + + + + Domain Label + + + + + + + + Label + + + + + Definition + + + + + Synonyms + + + + + + Synonym + + + + + + + + Links + + + + + + Link + + + + + + + + + + + + + + + Other + + + + + Foreign Languages + + + + + + Foreign Language + + + + + + Foreign Terms + + + + + + Foreign Term + + + + + + + + Foreign Definition + + + + + Foreign Synonims + + + + + + Foreign Synonim + + + + + + + + + + + + + + + Multimedia + + + + + + Image + + + + + Audio + + + + + Video + + + + + + + + + + + + + + Superscript + + + + + Subscript + + + + + Bold + + + + + + + + + + + + + Italic + + + + + + + + + + + + + Link + + + + + + + + + + + + + + New Line + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/express/public/images/Filtri.png b/express/public/images/Filtri.png index ea53506..9400994 100644 Binary files a/express/public/images/Filtri.png and b/express/public/images/Filtri.png differ diff --git a/express/public/images/Iskalni_zadetki.png b/express/public/images/Iskalni_zadetki.png index 9cdea41..bbd02b2 100644 Binary files a/express/public/images/Iskalni_zadetki.png and b/express/public/images/Iskalni_zadetki.png differ diff --git a/express/public/images/Napredno_iskanje.png b/express/public/images/Napredno_iskanje.png index 7d7be3a..0a9974e 100644 Binary files a/express/public/images/Napredno_iskanje.png and b/express/public/images/Napredno_iskanje.png differ diff --git a/express/public/images/exclamation-triangle-white.svg b/express/public/images/exclamation-triangle-white.svg new file mode 100644 index 0000000..c213e5e --- /dev/null +++ b/express/public/images/exclamation-triangle-white.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/express/public/images/user-x.svg b/express/public/images/user-x.svg new file mode 100644 index 0000000..7048bbb --- /dev/null +++ b/express/public/images/user-x.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/express/public/javascripts/admin.js b/express/public/javascripts/admin.js index e9b3182..608e660 100644 --- a/express/public/javascripts/admin.js +++ b/express/public/javascripts/admin.js @@ -1,7 +1,9 @@ -/* global $, axios, bootstrap, currentPagePath, initPagination, removeAllChildNodes, unsavedData */ +/* global $, axios, bootstrap, currentPagePath, initPagination, removeAllChildNodes, unsavedData, replaceContainer, i18next */ // const currentPagePath = location.pathname +let queryBattery = '' + window.addEventListener('load', () => { initAdmin() }) @@ -62,11 +64,11 @@ function initAdmin() { renderResults(results) updatePager(page, numberOfAllPages) } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response?.data) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } alert(message) updatePager() @@ -130,11 +132,11 @@ function initAdmin() { ifUserExists(data, type) event.target.reset() } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } console.log(message) } @@ -155,11 +157,11 @@ function initAdmin() { renderResults(results) updatePager(page, numberOfAllPages) } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response?.data) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } alert(message) updatePager() @@ -189,9 +191,9 @@ function initAdmin() { aEl.type = 'link' aEl.href = `/admin/uporabniki/${result.id}/urejanje` imgEl.src = '/images/u_edit-alt.svg' - imgEl.alt = 'Uredi' + imgEl.alt = i18next.t('Uredi') spanEl.className = 'normal-gray ms-1' - spanEl.textContent = 'Uredi' + spanEl.textContent = i18next.t('Uredi') td4.append(aEl) aEl.append(imgEl, spanEl) rowEl.append(td1, td2, td3, td4) @@ -214,11 +216,11 @@ function initAdmin() { renderResults(results) updatePager(page, numberOfAllPages) } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response?.data) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } alert(message) updatePager() @@ -267,9 +269,9 @@ function initAdmin() { aEl.type = 'link' aEl.href = `/admin/slovarji/${result.id}/podatki` imgEl.src = '/images/u_edit-alt.svg' - imgEl.alt = 'Uredi' + imgEl.alt = i18next.t('Uredi') spanEl.className = 'normal-gray ms-1' - spanEl.textContent = 'Uredi' + spanEl.textContent = i18next.t('Uredi') td6.append(aEl) aEl.append(imgEl, spanEl) rowEl.append(td1, td2, td3, td4, td5, td6) @@ -343,18 +345,20 @@ function initAdmin() { async function onPageChange(newPage) { try { - const { page, numberOfAllPages, results } = await getDataForPage( - newPage - ) + const results = await getDataForPage(newPage) + + const numberOfAllPages = +results.headers['number-of-all-pages'] + const page = +results.headers.page + removeAllChildNodes(resultsListEl) - renderResults(results) + renderResults(results.data) updatePager(page, numberOfAllPages) } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response?.data) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } alert(message) updatePager() @@ -362,56 +366,46 @@ function initAdmin() { } async function getDataForPage(page) { - const url = `/api/v1/dictionaries/listSecondaryDomains?p=${page}` - const { data } = await axios.get(url) - return data + const url = `/api/v1/dictionaries/secondaryDomains?q=${queryBattery}&p=${page}` + return await axios.get(url) } function renderResults(results) { - results.forEach(result => { - const rowEl = document.createElement('tr') - const input = document.createElement('input') - const th = document.createElement('th') - const input2 = document.createElement('input') - const td1 = document.createElement('td') - const tdTrans = document.createElement('td') - const td2 = document.createElement('td') - const div = document.createElement('div') - const editBtn = document.createElement('button') - const deleteBtn = document.createElement('button') - const imgEditEl = document.createElement('img') - const imgDelEl = document.createElement('img') - input.value = result.id - input.type = 'hidden' - input.name = 'domainLabelId' - th.scope = 'row' - input2.className = 'form-check checkbox-table' - input2.type = 'checkbox' - input2.name = 'isVisible' - input2.disabled = true - input2.checked = !!result.isApproved - th.append(input2) - td1.className = 'tdata-area' - td1.textContent = result.nameSl - tdTrans.className = 'tdata-translation' - tdTrans.textContent = result.nameEn - td2.classList.add('buttons-group') - div.classList.add('table-buttons') - editBtn.className = 'p-0 table-button-grp me-3 edit-row-btn' - editBtn.type = 'button' - imgEditEl.src = '/images/u_edit-alt.svg' - deleteBtn.className = 'p-0 table-button-grp delete-row-btn' - deleteBtn.dataset.bsTarget = '#alert-modal' - deleteBtn.dataset.bsToggle = 'modal' - deleteBtn.type = 'button' - imgDelEl.src = '/images/red-trash-icon.svg' - td2.append(div) - div.append(editBtn, deleteBtn) - editBtn.append(imgEditEl) - deleteBtn.append(imgDelEl) + replaceContainer('page-results', results) + } - rowEl.append(input, th, td1, tdTrans, td2) - resultsListEl.appendChild(rowEl) + const updatePaginationOnFilter = axiosResult => { + const numberOfAllPages = +axiosResult.headers['number-of-all-pages'] + const page = +axiosResult.headers.page + // removeAllChildNodes(resultsListEl) + // renderResults(results.data) + // console.log(numberOfAllPages) + // console.log(page) + // console.log(updatePager) + updatePager(page, numberOfAllPages) + } + + /// / Due to unsual design, the function was moved inside + function searchController() { + // if (window.location.pathname.includes('podrocne-oznake')) { // No if required since already checkek above + queryBattery = document.getElementById('input-search').value + // console.log(queryBattery) + axios + .get(`/api/v1/dictionaries/secondaryDomains?q=${queryBattery}`) + .then(result => { + updatePaginationOnFilter(result) + replaceContainer('page-results', result.data) + }) + // } + } + const inlineSearchButton = document.getElementById('inline-search-btn') + + if (inlineSearchButton) { + inlineSearchButton.addEventListener('click', searchController) + $('#input-search').on('keyup', function (e) { + if (e.code === 'Enter' || e.code === 'NumpadEnter') { + searchController() + } }) } } @@ -450,7 +444,7 @@ function initAdmin() { ifUserExists(data, type) event.target.reset() } catch (error) { - const message = 'Prišlo je do napake.' + const message = i18next.t('Prišlo je do napake.') } } } @@ -460,17 +454,6 @@ function initAdmin() { formEditUser.addEventListener('input', enableButton) } - if (/\/slovarji\/\d+\/struktura/.test(currentPagePath)) { - changePreview() - const switchForm = document.querySelector('.switch-forms') - switchForm.addEventListener('click', () => changePreview()) - const wholeForm = document.getElementById('form-dictionary-structure') - wholeForm.addEventListener('input', enableButton) - const dictSideMenu = document.querySelector('.admin-nav-content') - unsavedData(wholeForm, dictSideMenu) - $('#languages-input').on('change', enableButton) - } - if (currentPagePath === '/admin/nastavitve/portal') { const formAdminPortal = document.getElementById('admin-portal-settings') formAdminPortal.addEventListener('input', enableButton) @@ -537,7 +520,7 @@ function createNewAuthorInput(pageForm) { divAuthor.className = 'author mt-sm-4 added-field' divSubjectName.className = 'subject-name' spanName.className = 'smaller-gray-uppercase' - spanName.textContent = 'AVTOR' + spanName.textContent = i18next.t('AVTOR') divRow.className = 'row align-items-center' divColSm5.className = 'col-sm-6' inputGroup.className = 'input-group' @@ -553,7 +536,7 @@ function createNewAuthorInput(pageForm) { divColSm.className = 'col-sm' spanNameInfoTxt.className = 'd-md-inline d-block name-info-txt ms-xxl-3 ms-md-3 mt-3 mt-ms-0' - spanNameInfoTxt.textContent = 'Dodaten avtor.' + spanNameInfoTxt.textContent = i18next.t('Dodaten avtor.') divAuthor.appendChild(divSubjectName) divSubjectName.appendChild(spanName) @@ -594,9 +577,9 @@ function createNewAreaInput(pageForm) { divSmallNameArea.className = 'author mt-4 added-field' divSubjectName.className = 'subject-name' spanInputNameTxtSlo.className = 'smaller-gray-uppercase' - spanInputNameTxtSlo.textContent = 'NOVO PODPODROČJE (slovensko)' + spanInputNameTxtSlo.textContent = i18next.t('NOVO PODPODROČJE (slovensko)') spanInputNameTxtEng.className = 'smaller-gray-uppercase mt-4' - spanInputNameTxtEng.textContent = 'NOVO PODPODROČJE (angleško)' + spanInputNameTxtEng.textContent = i18next.t('NOVO PODPODROČJE (angleško)') divRow.className = 'row align-items-center' divRow2.className = 'row align-items-center' divEnglishInput.className = 'mt-4' @@ -617,11 +600,12 @@ function createNewAreaInput(pageForm) { divColSm.className = 'col-sm mt-3 mt-sm-0' divColSm2.className = 'col-sm mt-3 mt-sm-0' spanNameInfoTxt.className = 'd-sm-inline name-info-txt ms-xxl-3 ms-md-3' - spanNameInfoTxt.textContent = + spanNameInfoTxt.textContent = i18next.t( 'Vpišite novo podpodročje. Na seznamu podpodročij bo vidno takoj po potrditvi administratorja portala.' + ) spanNameInfoTxtEng.className = 'd-sm-inline name-info-txt ms-xxl-3 ms-md-3 mt-4' - spanNameInfoTxtEng.textContent = 'Novo podpodročje (angleško).' + spanNameInfoTxtEng.textContent = i18next.t('Novo podpodročje (angleško).') divSmallNameArea.appendChild(divSubjectName) divSubjectName.appendChild(spanInputNameTxtSlo) @@ -782,7 +766,7 @@ function handleAreasClick({ target }) { const newButtonGroup = document.createElement('div') const cancelButton = document.createElement('button') const saveButton = document.createElement('button') - cancelButton.textContent = 'Prekliči' + cancelButton.textContent = i18next.t('Prekliči') cancelButton.type = 'button' cancelButton.className = 'btn btn-secondary me-2' cancelButton.style.height = '33px' @@ -790,7 +774,7 @@ function handleAreasClick({ target }) { cancelButton.addEventListener('click', () => abortEditing(newButtonGroup, tableButtons, tDataArea, tDataTranslation) ) - saveButton.textContent = 'POTRDI' + saveButton.textContent = i18next.t('POTRDI') saveButton.type = 'button' saveButton.className = 'btn btn-primary' saveButton.style.height = '33px' @@ -986,76 +970,12 @@ function mobileMoveContent() { currentPagePath.includes('slovarji') && !currentPagePath.includes('admin') ) - navTitle.textContent = 'Urejanje' - else navTitle.textContent = 'Administrator' + navTitle.textContent = i18next.t('Urejanje') + else navTitle.textContent = i18next.t('Administracija') siteHeading.style.display = 'block' } } -function changePreview() { - const switchDomainLabel = document.getElementById('domain-labels') - const switchLabel = document.getElementById('label-checkbox') - const switchDefinition = document.getElementById('definition-check-box') - const switchSynonym = document.getElementById('synonyms') - const switchLink = document.getElementById('links') - const switchForeignLang = document.getElementById('language-group') - const switchForeignTerm = document.getElementById('termin-language-subgroup') - const switchForeignDef = document.getElementById( - 'definition-language-subgroup' - ) - const switchForeignSyn = document.getElementById('synonym-language-subgroup') - const switchImage = document.getElementById('images') - const switchAudio = document.getElementById('audio') - const switchVideo = document.getElementById('video') - - const previewDomainSecondary = document.querySelector( - '.preview-domain-secondary' - ) - const selectedLabel = document.querySelector('.preview-label') - const selectedDef = document.querySelector('.preview-definition') - const selectedSynonyms = document.querySelector('.preview-synonym') - const linkedTerms = document.getElementById('linked-terms') - const foreignLanguages = document.querySelector( - '.preview-languages-container' - ) - const listForeignTerm = document.querySelector('.preview-f-term') - const listForeignDefinitions = document.querySelector( - '.preview-foreign-definition' - ) - const foreignSynonyms = document.querySelector('.preview-foreign-synonyms') - const selectedImages = document.querySelector('.preview-images') - const selectedAudio = document.querySelector('.preview-audio') - const selectedVideo = document.querySelector('.preview-video') - - if (!switchDomainLabel.checked) - previewDomainSecondary.classList.add('hide-preview') - else previewDomainSecondary.classList.remove('hide-preview') - if (!switchLabel.checked) selectedLabel.classList.add('hide-preview') - else selectedLabel.classList.remove('hide-preview') - if (!switchDefinition.checked) selectedDef.classList.add('hide-preview') - else selectedDef.classList.remove('hide-preview') - if (!switchSynonym.checked) selectedSynonyms.classList.add('hide-preview') - else selectedSynonyms.classList.remove('hide-preview') - if (!switchLink.checked) linkedTerms.classList.add('hide-preview') - else linkedTerms.classList.remove('hide-preview') - if (!switchForeignLang.checked) foreignLanguages.classList.add('hide-preview') - else foreignLanguages.classList.remove('hide-preview') - if (!switchForeignSyn.checked) foreignSynonyms.classList.add('hide-preview') - else foreignSynonyms.classList.remove('hide-preview') - if (!switchForeignTerm.checked) listForeignTerm.classList.add('hide-preview') - else listForeignTerm.classList.remove('hide-preview') - if (!switchForeignDef.checked) - listForeignDefinitions.classList.add('hide-preview') - else listForeignDefinitions.classList.remove('hide-preview') - - if (!switchImage.checked) selectedImages.classList.add('hide-preview') - else selectedImages.classList.remove('hide-preview') - if (!switchAudio.checked) selectedAudio.classList.add('hide-preview') - else selectedAudio.classList.remove('hide-preview') - if (!switchVideo.checked) selectedVideo.classList.add('hide-preview') - else selectedVideo.classList.remove('hide-preview') -} - function checkUserRightsCb(el) { const { terminologyReviewCb, languageReviewCb } = window.adminElements const termRevCbs = document.querySelectorAll('.terminology-review-cb') @@ -1109,12 +1029,18 @@ function ifUserExists(data, type) { } } if (bool === true && data[0] !== undefined) { - modalText.textContent = `Uporabnik ${data[0].username} je že v tabeli.` + // modalText.textContent = `Uporabnik ${data[0].username} je že v tabeli.` + modalText.textContent = + i18next.t('Uporabnik') + + `${data[0].username}` + + i18next.t('je že v tabeli.') modalEl.toggle() } else createNewUserArea(data, type) } else createNewUserArea(data, type) } else { - modalText.textContent = `Preverite, če ste pravilno vpisali uporabniško ime. Bodite pozorni na velike in male črke.` + modalText.textContent = i18next.t( + 'Preverite, če ste pravilno vpisali uporabniško ime. Bodite pozorni na velike in male črke.' + ) modalEl.toggle() } } @@ -1172,24 +1098,24 @@ function createNewUserArea(data, type) { inputConsultAdmin.type = 'checkbox' if (type === 'portal') { - tdPortAdminOrAdmin.dataset.label = 'Skrbnik portala' + tdPortAdminOrAdmin.dataset.label = i18next.t('Skrbnik portala') inputPortAdminOrAdmin.name = `rolesPerUser['${data[0].id}'][isPortalAdmin]` - tdDictAdmin.dataset.label = 'Skrbnik slovarjev' + tdDictAdmin.dataset.label = i18next.t('Skrbnik slovarjev') inputDictAdmin.name = `rolesPerUser['${data[0].id}'][isDictionariesAdmin]` - tdConsultAdmin.dataset.label = 'Skrbnik svetovalnice' + tdConsultAdmin.dataset.label = i18next.t('Skrbnik svetovalnice') inputConsultAdmin.name = `rolesPerUser['${data[0].id}'][isConsultancyAdmin]` } else { - tdPortAdminOrAdmin.dataset.label = 'Administrator' + tdPortAdminOrAdmin.dataset.label = i18next.t('Administrator') inputPortAdminOrAdmin.classList.add('administration-cb') inputPortAdminOrAdmin.name = `rightsPerUser['${data[0].id}'][isAdministration]` - tdDictAdmin.dataset.label = 'Urejanje' + tdDictAdmin.dataset.label = i18next.t('Urejanje') inputDictAdmin.name = `rightsPerUser['${data[0].id}'][isEditing]` inputDictAdmin.classList.add('edit-cb') - tdConsultAdmin.dataset.label = 'Strokovni pregled' + tdConsultAdmin.dataset.label = i18next.t('Strokovni pregled') inputConsultAdmin.classList.add('terminology-review-cb') inputConsultAdmin.name = `rightsPerUser['${data[0].id}'][isTerminologyReview]` tdLanguageRev.className = 'pt-1 pb-1' - tdLanguageRev.dataset.label = 'Jezikovni pregled' + tdLanguageRev.dataset.label = i18next.t('Jezikovni pregled') divLanguageRev.className = 'form-check d-flex justify-content-left justify-content-xl-center' inputLanguageRev.className = 'language-review-cb form-check-input' @@ -1235,7 +1161,7 @@ function createNewUserArea(data, type) { // Summernote $('.summernote').summernote({ - placeholder: 'Na kratko opišite zasnovo in namen slovarja.', + placeholder: i18next.t('Na kratko opišite zasnovo in namen slovarja.'), height: 300, minheight: 150, toolbar: [ diff --git a/express/public/javascripts/collapsable-comments.js b/express/public/javascripts/collapsable-comments.js index 11fc04d..f83db49 100644 --- a/express/public/javascripts/collapsable-comments.js +++ b/express/public/javascripts/collapsable-comments.js @@ -4,21 +4,28 @@ const commentsContainer = document.querySelector('#comments-container') const dropImage = document.querySelector('#dropImage') const commentCount = document.querySelector('#comments-count') + const collapsableHR = document.querySelector('.collapable-hr') const hide = () => { - pager.className += ' d-none' + pager.className += ' invisible' commentsContainer.className += ' d-none' - commentCount.className += ' d-none' + commentCount.className += ' invisible' + collapsableHR.className += ' d-none' + } + + const show = () => { + pager.className = 'pager d-flex' + commentsContainer.className = 'comments-container pe-1' + dropImage.src = '/images/arrow_drop_up.svg' + commentCount.className = 'comment-count d-flex ms-auto me-4 text-p875rem' + collapsableHR.className += 'comments-top-hr mt-3' } document.getElementById('collapseComments').addEventListener('click', e => { isVisible = !isVisible if (isVisible) { - pager.className = 'pager' - commentsContainer.className = 'comments-container pe-1' - dropImage.src = '/images/arrow_drop_up.svg' - commentCount.className = 'comment-count d-flex ms-auto me-4 text-p875rem' + show() } else { dropImage.src = '/images/arrow_drop_down.svg' hide() diff --git a/express/public/javascripts/comments.js b/express/public/javascripts/comments.js index 1e46558..8c8c33c 100644 --- a/express/public/javascripts/comments.js +++ b/express/public/javascripts/comments.js @@ -1,6 +1,6 @@ // TODO Remove no-console ignore rule once things are out of rapid dev phase. /* eslint no-console: 0 */ -/* global axios, initPagination */ +/* global axios, initPagination, i18next */ const pageURL = location.pathname window.addEventListener('load', () => { @@ -127,20 +127,21 @@ function renderCommentCount(commentCount) { let displayText = `${commentCount} ` // TODO Let a i18n library handle the following logic. + // TODO I18n switch (commentCount % 100) { case 1: - displayText += 'komentar' + displayText += i18next.t('komentar') break case 2: - displayText += 'komentarja' + displayText += i18next.t('komentarja') break case 3: case 4: - displayText += 'komentarji' + displayText += i18next.t('komentarji') break default: - displayText += 'komentarjev' + displayText += i18next.t('komentarjev') break } @@ -309,7 +310,7 @@ function submitComment() { let ctxId = null if (ctxData.ctxId !== undefined) ctxId = ctxData.ctxId if (!message) { - alert('Vaš komentar je brez vsebine.') + alert(i18next.t('Vaš komentar je brez vsebine.')) } else { const payload = { message, ctxType, ctxId, quoteId: null } createComment(payload) @@ -326,7 +327,7 @@ function submitCommentReply() { let ctxId = null if (ctxData.ctxId !== undefined) ctxId = ctxData.ctxId if (!message) { - alert('Vaš komentar je brez vsebine.') + alert(i18next.t('Vaš komentar je brez vsebine.')) } else { const payload = { message, ctxType, ctxId, quoteId } createComment(payload) @@ -577,11 +578,11 @@ async function onPageChange(newPage) { const { page, numberOfAllPages } = await displayComments(newPage) updatePager(page, numberOfAllPages) } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response?.data) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } alert(message) updatePager() diff --git a/express/public/javascripts/consultancy-search-mechanism.js b/express/public/javascripts/consultancy-search-mechanism.js index 9eb59d5..6709da1 100644 --- a/express/public/javascripts/consultancy-search-mechanism.js +++ b/express/public/javascripts/consultancy-search-mechanism.js @@ -11,6 +11,7 @@ function routeConsultancy(searchString) { } else { url = new URL(location) + url.searchParams.delete('p') const sq = document.getElementById('search-query') if (sq) { url.searchParams.set('q', sq.value) @@ -65,3 +66,14 @@ if (inputMainSearch) { propagateFunctionalityToASearchButton(searchButton) propagateFunctionalityToASearchButton(advancedSearchButton) + +$(document).ready(() => { + const focused = $('#description') + if (focused) { + focused.focus() + } + + if (window.location.pathname === '/svetovanje/iskanje') { + // todo + } +}) diff --git a/express/public/javascripts/consultancy.js b/express/public/javascripts/consultancy.js index 915b7e1..143e9e0 100644 --- a/express/public/javascripts/consultancy.js +++ b/express/public/javascripts/consultancy.js @@ -1,4 +1,5 @@ -/* global $, handleDomainsClickForConsultancy, inputFieldsChecker, currentPagePath, axios, nthParent, tooltipListWOL, tooltipTriggerList */ +/* global $, bootstrap, handleDomainsClickForConsultancy, initPagination, inputFieldsChecker, currentPagePath, +axios, nthParent, tooltipListWOL, tooltipTriggerList, removeAllChildNodes, i18next */ /* author: Miha Stele, 2022 @@ -9,6 +10,10 @@ let selectedID = -1 // consultancy FORM let offsetMain +let OFFSET_PADDING_MASK = 16 +if (window.location.pathname === '/svetovanje/vprasanje/admin/svetovalci') { + OFFSET_PADDING_MASK = 0 +} function adjustOffsetBy() { offsetMain = document.querySelector('#offset-main') const fixedTopSection = document.querySelector('#fixed-top-section') @@ -22,15 +27,21 @@ function adjustOffsetBy() { if (offsetHeaderPadding !== null) offsetMain.style.paddingTop = `0px` } else { for (let i = 0; i < offsetHeader.length; i++) { - offsetHeader[i].style.paddingTop = `${referenceHeight}px` + offsetHeader[i].style.paddingTop = `${ + referenceHeight - OFFSET_PADDING_MASK + }px` } if (headerPadding !== null) { const headerPaddingHeight = headerPadding.offsetHeight - offsetMain.style.paddingTop = `${headerPaddingHeight}px` + offsetMain.style.paddingTop = `${ + headerPaddingHeight - OFFSET_PADDING_MASK + }px` } if (offsetHeaderPadding !== null) { const offsetHeaderHeight = offsetHeaderPadding.offsetHeight - offsetMain.style.paddingTop = `${referenceHeight + offsetHeaderHeight}px` + offsetMain.style.paddingTop = `${ + referenceHeight + offsetHeaderHeight - OFFSET_PADDING_MASK + }px` } } } @@ -46,7 +57,7 @@ window.addEventListener('load', () => { window.adminElements = ce /* copy source admin.js in case of refactoring */ - if (currentPagePath === '/svetovanje/vprasanje/admin/uporabniki') { + if (currentPagePath === '/svetovanje/vprasanje/admin/svetovalci') { offsetMain.addEventListener('click', handleDomainsClickForConsultancy) ce.name = document.getElementById('name-input') if (ce.name) { @@ -108,7 +119,7 @@ try { const summernote = $('.summernote') if (summernote) { summernote.summernote({ - placeholder: 'Na kratko opišite zasnovo in namen slovarja.', + placeholder: i18next.t('Na kratko opišite zasnovo in namen slovarja.'), height: 300, minheight: 150, toolbar: [ @@ -383,7 +394,17 @@ if (createAnswerForm) { // console.log(data) if (res.status === 201) { - window.location.href = '/svetovanje' + const infoModal = document.getElementById('begin-response') + if (infoModal) { + const responseModal = new bootstrap.Modal(infoModal) + responseModal.toggle() + } + + document + .getElementById('understand-btn') + .addEventListener('click', () => { + window.location.href = '/svetovanje' + }) } } @@ -407,7 +428,7 @@ if (insertConsultantForm) { }) .then(result => { console.log(result) - window.location.href = '/svetovanje/vprasanje/admin/uporabniki' + window.location.href = '/svetovanje/vprasanje/admin/svetovalci' }) }) } @@ -421,6 +442,243 @@ try { console.log(e) } +/// Frontend Pagination (as in other pages in this project) + +function onResultClick(e) { + const entryEl = e.target.closest(`#${this.id} a.rl`) + if (!entryEl) return + + const anchorId = entryEl.querySelector('.anchor').id + const anchoredUrl = new URL(location) + anchoredUrl.hash = anchorId + history.replaceState(null, '', anchoredUrl) +} + +const resultsListEl = document.getElementById('results-data') +if (resultsListEl) { + resultsListEl.addEventListener('click', onResultClick) +} + +const initialPage = +new URL(location).searchParams.get('p') || 1 + +let updatePager +try { + updatePager = initPagination('pagination', onPageChange, initialPage) +} catch (e) {} + +async function onPageChange(newPage) { + const receivedPageNumber = await changePage(newPage) + updateUrlAndHistory(receivedPageNumber) +} + +async function changePage(newPage) { + try { + const res = await getDataForPage(newPage) + + const page = +res.headers.page + const numberOfAllPages = +res.headers['number-of-all-pages'] + const resultsMarkup = res.data + + removeAllChildNodes(resultsListEl) + renderResults(resultsMarkup) + updatePager(page, numberOfAllPages) + + return page + } catch (error) { + // console.log(error) + let message = i18next.t('Prišlo je do napake.') + if (error.response?.data) { + message = error.response.data + } else if (error.request) { + return // return to bypass error caused (assumed) by popstate on iOS/MacOS + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') + } + + alert(message) + updatePager() + } +} + +async function getDataForPage(page) { + const qParams = new URL(location).searchParams + qParams.set('p', page) + + // const url = `/api/v1/search/main?${qParams}` + + // let url = `/api/v1/consultancy/entry-pagination?${qParams}` + // URL PATTERN PARSING + const PATH = window.location.pathname + if (PATH.includes('/admin/')) { + // todo ADMIN URL handling + qParams.set('isAdmin', 'true') + // url = `/api/v1/consultancy/entry-pagination?${qParams}` // required due to updated queryParams + + if (PATH.includes('/novo')) { + qParams.set('type', 'new') + } else if (PATH.includes('/pripravljeno')) { + qParams.set('type', 'review') + } else if (PATH.includes('/zavrnjeno')) { + qParams.set('type', 'rejected') + } else if (PATH.includes('/v-delu')) { + qParams.set('type', 'in progress') + } else if (PATH.includes('/objavljeno')) { + qParams.set('type', 'published') + } else { + // Invalid case + console.log('INVALID CASE, CHECK URL') + return + } + } + // const url = `/api/v1/search/main?${qParams}` + + const url = `/api/v1/consultancy/entry-pagination?${qParams}` + console.log(url) + + return await axios.get(url) +} + +function renderResults(resultsMarkup) { + const wrapper = document.createElement('div') + wrapper.innerHTML = resultsMarkup + resultsListEl.appendChild(wrapper) +} + +function updateUrlAndHistory(page) { + const newUrl = new URL(location) + newUrl.searchParams.set('p', page) + history.pushState(null, '', newUrl) +} + +window.addEventListener('popstate', () => { + const page = +new URL(location).searchParams.get('p') || 1 + changePage(page) +}) + +/// // PAGINATION (backend only) ///// + +/* const updatePager = initPaginationBE('pagination', onPageChangeBE) + +function onPageChangeBE(newPage) { + const newUrl = new URL(location) + newUrl.searchParams.set('p', newPage) + history.pushState(null, '', newUrl) + window.location.href = newUrl +} + +function initPaginationBE(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 + } + } + + updatePagerUi( + +new URL(location).searchParams.get('p') || 1, + +document.querySelector('.pages-total').innerHTML + ) + + return updatePagerUi +} + +window.addEventListener('popstate', () => { + const page = +new URL(location).searchParams.get('p') || 1 + onPageChangeBE(page) +}) + */ +/// /////////////////// + /* if (tooltipList) { console.log(tooltipList) @@ -438,3 +696,10 @@ $(document).ready(() => { focused.focus() } }) + +const askBackButton = document.querySelector('#cancel-cons-btn') +if (askBackButton) { + askBackButton.addEventListener('click', () => { + history.back() + }) +} diff --git a/express/public/javascripts/demo-paginacija.js b/express/public/javascripts/demo-paginacija.js index b4078fe..3bf44e4 100644 --- a/express/public/javascripts/demo-paginacija.js +++ b/express/public/javascripts/demo-paginacija.js @@ -7,9 +7,15 @@ // 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', onPageChange) + 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. @@ -62,27 +68,49 @@ * Na vsaki strani, kjer bo paginacija, jo inicializiraj in uporabljaj po zgledu zgornje kode. * \** ****************************************************************************************************************************************** **/ -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') - +function initPagination(paginationRootElIds, onPageChange, currentPage = 1) { let reqLock = false + let numOfAllPages + const backwardBtnEls = [] + const forwardBtnEls = [] + const pageInputEls = [] + const pagesCountDisplayEls = [] - rootEl.addEventListener('click', handleButtonClick) - formEl.addEventListener('submit', handleFormSubmit) + if (Array.isArray(paginationRootElIds)) { + paginationRootElIds.forEach(id => initControls(id)) + } else { + initControls(paginationRootElIds) + } + const allControlEls = [...backwardBtnEls, ...forwardBtnEls, ...pageInputEls] - function handleButtonClick({ target }) { + 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 - const numOfAllPages = +pagesCountDisplayEl.textContent if (buttonEl.classList.contains('first-page')) { if (currentPage === 1) return @@ -103,12 +131,12 @@ function initPagination(paginationRootElId, onPageChange, currentPage = 1) { } } - function handleFormSubmit(e) { + function handleFormSubmit(e, pageInputEl) { e.preventDefault() if (reqLock) return const inputValue = +pageInputEl.value - if (!(inputValue > 0 && inputValue <= pagesCountDisplayEl.textContent)) { + if (!(inputValue > 0 && inputValue <= numOfAllPages)) { alert('Nepravilna vrednost strani') pageInputEl.value = currentPage return @@ -120,20 +148,12 @@ function initPagination(paginationRootElId, onPageChange, currentPage = 1) { function enableLock() { reqLock = true - btnFirstPage.disabled = true - btnPreviousPage.disabled = true - btnNextPage.disabled = true - btnLastPage.disabled = true - pageInputEl.disabled = true + allControlEls.forEach(el => (el.disabled = true)) } function disableLock() { reqLock = false - btnFirstPage.disabled = false - btnPreviousPage.disabled = false - btnNextPage.disabled = false - btnLastPage.disabled = false - pageInputEl.disabled = false + allControlEls.forEach(el => (el.disabled = false)) } function updatePagerUi(newCurrentPage, newNumOfAllPages) { @@ -141,23 +161,19 @@ function initPagination(paginationRootElId, onPageChange, currentPage = 1) { if (!newCurrentPage) return currentPage = newCurrentPage - pageInputEl.value = newCurrentPage - pagesCountDisplayEl.textContent = newNumOfAllPages + pageInputEls.forEach(el => (el.value = newCurrentPage)) + pagesCountDisplayEls.forEach(el => (el.textContent = newNumOfAllPages)) if (newCurrentPage === 1) { - btnFirstPage.disabled = true - btnPreviousPage.disabled = true + backwardBtnEls.forEach(el => (el.disabled = true)) } else { - btnFirstPage.disabled = false - btnPreviousPage.disabled = false + backwardBtnEls.forEach(el => (el.disabled = false)) } if (newCurrentPage === newNumOfAllPages) { - btnNextPage.disabled = true - btnLastPage.disabled = true + forwardBtnEls.forEach(el => (el.disabled = true)) } else { - btnNextPage.disabled = false - btnLastPage.disabled = false + forwardBtnEls.forEach(el => (el.disabled = false)) } } diff --git a/express/public/javascripts/dictionaries.js b/express/public/javascripts/dictionaries.js index b39920a..e91e116 100644 --- a/express/public/javascripts/dictionaries.js +++ b/express/public/javascripts/dictionaries.js @@ -1,4 +1,4 @@ -/* global $, axios, bootstrap, currentPagePath, initPagination, removeAllChildNodes, unsavedData */ +/* global $, axios, bootstrap, currentPagePath, initPagination, removeAllChildNodes, unsavedData, replaceContainer, i18next */ // Temporary workaround (use of currentPagePath). @@ -6,6 +6,8 @@ window.addEventListener('load', () => { initDictionaries() }) +let queryBattery = '' + function initDictionaries() { const ce = {} window.dictionaryElements = ce @@ -166,7 +168,7 @@ function initDictionaries() { if (ce.fileUploadInput.files.item(0) !== null) chosenFile.textContent = ce.fileUploadInput.files.item(0).name else { - chosenFile.textContent = 'Izberi datoteko' + chosenFile.textContent = i18next.t('Izberi datoteko') } } }) @@ -185,12 +187,140 @@ function initDictionaries() { await axios.post(event.target.action, payload) - alert('SLOVAR UVOŽEN') + alert(i18next.t('SLOVAR UVOŽEN')) } catch (error) { - alert('NAPAKA V UVOZU') + alert(i18next.t('NAPAKA V UVOZU')) + } + } + + { + const resultsListEl = document.getElementById('page-results') + const dictionaryId = document.getElementById('dictionary-id').value + + 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.') + } + alert(message) + updatePager() + } + } + + async function getDataForPage(page) { + const url = `/api/v1/dictionaries/${dictionaryId}/showImportFromFileForm?p=${page}` + const { data } = await axios.get(url) + return data + } + + function renderResults(results) { + const localeOptions = { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit' + } + 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 date = new Date(result.time_started).toLocaleDateString( + 'sl-SL', + localeOptions + ) + td1.textContent = date + td2.textContent = result.file_format + td3.textContent = result.count_valid_entries + td4.textContent = result.status + rowEl.append(td1, td2, td3, td4) + resultsListEl.appendChild(rowEl) + }) } } } + + if (/\/slovarji\/\d+\/izvoz/.test(currentPagePath)) { + const resultsListEl = document.getElementById('page-results') + const dictionaryId = document.getElementById('dictionary-id').value + + 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.') + } + alert(message) + updatePager() + } + } + + async function getDataForPage(page) { + const url = `/api/v1/dictionaries/${dictionaryId}/showExportToFileForm?p=${page}` + const { data } = await axios.get(url) + return data + } + + function renderResults(results) { + const localeOptions = { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit' + } + 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 date = new Date(result.time_created).toLocaleDateString( + 'sl-SL', + localeOptions + ) + td1.textContent = date + td2.textContent = result.export_file_format + td3.textContent = result.entry_count + if (result.status === 'finished') { + const aEl = document.createElement('a') + aEl.href = `/slovarji/export-download/${result.id}` + aEl.textContent = i18next.t('Shrani') + aEl.className = 'btn btn-primary' + td4.append(aEl) + } else td4.textContent = '' + + rowEl.append(td1, td2, td3, td4) + resultsListEl.appendChild(rowEl) + }) + } + } + if (/\/slovarji\/\d+\/vsebina/.test(currentPagePath)) { ce.formEditContent = document.getElementById('form-edit-content') ce.formEditContent.addEventListener('submit', updateEntry) @@ -233,6 +363,7 @@ function initDictionaries() { ce.clearSearchInput = document.querySelector('.clear-search-input') ce.collapsablePart = document.querySelectorAll('.collapsable-entry-part') ce.scrollEl = document.querySelector('.content-nav-content') + $('.multiple').on('select2:select', enableButton) ce.collapsablePart.forEach(el => { el.addEventListener('hide.bs.collapse', () => changePageContent('collapse-hidden') @@ -257,7 +388,7 @@ function initDictionaries() { }) ce.statusEditEl = document.getElementById('status-edit') // ce.deleteEntryBtn.addEventListener('click', () => ajaxDeleteEntry()) - ce.newEntryBtn.addEventListener('click', () => { + ce.newEntryBtn.addEventListener('click', e => { ce.formEditContent.reset() changePageContent('new-entry-btn') }) @@ -307,7 +438,8 @@ function initDictionaries() { if (mcBtnsGrp.length) { mcBtnsGrp.forEach(e => e.addEventListener('click', enableButton)) } - ce.newHeadwordGroupBtn.addEventListener('click', createHeadword) + if (ce.newHeadwordGroupBtn) + ce.newHeadwordGroupBtn.addEventListener('click', createHeadword) if (ce.newConnectionBtn) ce.newConnectionBtn.addEventListener('click', () => addConnectionField(ce.formEditContent, ce.newConnectionBtn) @@ -408,7 +540,7 @@ function initDictionaries() { if (termListMenu.childElementCount > 1) { const termListLabels = termListMenu.querySelectorAll('label') - termListLabels[0].click() + if (termListLabels.length) termListLabels[0].click() } function handleClick({ target }) { @@ -423,10 +555,11 @@ function initDictionaries() { const okBtnText = modalUseBtn.textContent const cnclBtnTxt = modalCnclBtn.textContent const modalMainTxt = modalMain.textContent - modalUseBtn.textContent = 'Shrani' - modalCnclBtn.textContent = 'Ne shrani' - modalMain.textContent = + modalUseBtn.textContent = i18next.t('Shrani') + modalCnclBtn.textContent = i18next.t('Ne shrani') + modalMain.textContent = i18next.t( 'Imate neshranjene spremembe. Ali jih želite shraniti?' + ) const alertModal = new bootstrap.Modal( document.getElementById('alert-modal') ) @@ -478,8 +611,8 @@ function initDictionaries() { if (deleteEntryEl) { const modalUseBtn = document.getElementById('modal-del-btn') const modalCnclBtn = document.getElementById('cancel-btn') - modalUseBtn.textContent = 'Izbriši' - modalCnclBtn.textContent = 'Ne izbriši' + modalUseBtn.textContent = i18next.t('Izbriši') + modalCnclBtn.textContent = i18next.t('Ne izbriši') const alertModal = new bootstrap.Modal( document.getElementById('delete-modal') ) @@ -497,8 +630,8 @@ function initDictionaries() { const { data } = await axios.get( `/api/v1/entries/${entryId}/version-snapshots/${versionId}` ) - const payload = { entry: data } - insertTermData(entryId, payload) + const payload = { entry: data.data } + insertTermData(entryId, payload, data.author) const isPublishedEl = payload.entry.is_published const info = new FormData(formEditContent) info.append('isPublishedEl', isPublishedEl) @@ -531,6 +664,7 @@ function initDictionaries() { info.append('isPublishedEl', isPublishedEl) loadPreview(info) changeVersionList(data.entry.versions) + setLatestVersion(data.entry) ce.formEditContent.addEventListener('input', () => (unsaved = true)) formEditContent.dataset.entryId = entryId formEditContent.action = '/api/v1/entries/update' @@ -553,7 +687,7 @@ function initDictionaries() { } } - function insertTermData(entryId, data) { + function insertTermData(entryId, data, vAuthor) { const { newConnectionBtn } = window.dictionaryElements const localeOptions = { day: '2-digit', @@ -609,12 +743,20 @@ function initDictionaries() { } else for (let i = 0; i < termIdText.length; i++) { - termIdText[i].textContent = `ID: Ni idja` + termIdText[i].textContent = i18next.t('ID: Ni idja') } - ce.authorEl.textContent = data.entry.version_author - ce.versionEl.textContent = `Verzija ${data.entry.version}` - ce.previewVersion.textContent = `Verzija ${data.entry.version}` - ce.previewAuthor.textContent = data.entry.version_author + if (vAuthor) { + ce.authorEl.textContent = vAuthor + ce.previewAuthor.textContent = vAuthor + } else { + ce.authorEl.textContent = data.entry.version_author + ce.previewAuthor.textContent = data.entry.version_author + } + // ce.versionEl.textContent = `Verzija ${data.entry.version}` + ce.versionEl.textContent = i18next.t('Verzija') + `${data.entry.version}` + // ce.previewVersion.textContent = `Verzija ${data.entry.version}` + ce.previewVersion.textContent = + i18next.t('Verzija') + `${data.entry.version}` termName.value = data.entry.term ? data.entry.term.replace(/"/g, '"') : '' @@ -757,18 +899,19 @@ function initDictionaries() { const payload = new URLSearchParams(new FormData(ce.formEditContent)) payload.set('entryId', entryId) try { + saveBtn.textContent = i18next.t('Shranjujem ...') await axios.post(ce.formEditContent.action, payload) spinnerEl.classList.remove('d-none') saveBtn.classList.add('saved-entry-btn') - saveBtn.textContent = 'Shranjeno' + saveBtn.textContent = i18next.t('Shranjeno') if (id != null) entryId = id ajaxSideMenu(entryId) } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } messageContainer.textContent = message } @@ -776,19 +919,20 @@ function initDictionaries() { event.preventDefault() const payload = new URLSearchParams(new FormData(ce.formEditContent)) try { + saveBtn.textContent = i18next.t('Shranjujem ...') const res = await axios.post(ce.formEditContent.action, payload) spinnerEl.classList.remove('d-none') saveBtn.classList.add('saved-entry-btn') - saveBtn.textContent = 'Shranjeno' + saveBtn.textContent = i18next.t('Shranjeno') let entryId = res.data.entryId if (id != null) entryId = id ajaxSideMenu(entryId) } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } messageContainer.textContent = message } finally { @@ -806,11 +950,11 @@ function initDictionaries() { }) renderTerms(data, entryId) } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } messageContainer.textContent = message } @@ -829,7 +973,7 @@ function initDictionaries() { selectNextEntry(selectedIndex) } catch (error) { console.log(error) - const message = 'Prišlo je do napake.' + const message = i18next.t('Prišlo je do napake.') messageContainer.textContent = message } } @@ -870,21 +1014,27 @@ function initDictionaries() { /"/g, '"' )}` - } else labelElement.textContent = '[ni termina]' - } else labelElement.textContent = '[ni termina]' + } else labelElement.textContent = i18next.t('[ni termina]') + } else labelElement.textContent = i18next.t('[ni termina]') } else { if (el.isValid) { if (el.isPublished) { - labelElement.textContent = sloTerm + labelElement.textContent = `${sloTerm} ${ + el.homonymSort ? '(' + el.homonymSort + ')' : '' + }` labelElement.className = 'terms-label term-good-btn btn p-2 ms-2 me-3 text-truncate justify-content-start d-inline-block' } else { labelElement.className = 'terms-label btn p-2 ms-2 me-3 text-truncate justify-content-start d-inline-block' - labelElement.textContent = sloTerm + labelElement.textContent = `${sloTerm} ${ + el.homonymSort ? '(' + el.homonymSort + ')' : '' + }` } } else { - labelElement.textContent = sloTerm + labelElement.textContent = `${sloTerm} ${ + el.homonymSort ? '(' + el.homonymSort + ')' : '' + }` labelElement.className = 'terms-label not-valid-not-published btn p-2 ms-2 me-3 text-truncate justify-content-start d-inline-block' } @@ -949,84 +1099,83 @@ function initDictionaries() { } } - { - const resultsListEl = document.getElementById('page-results') - const dictionaryId = document.getElementById('subareas-dict-id').value + const resultsListEl = document.getElementById('page-results') + const dictionaryId = document.getElementById('subareas-dict-id').value - 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 = '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) - updatePager() + async function onPageChange(newPage) { + try { + const results = await getDataForPage(newPage) + + const numberOfAllPages = +results.headers['number-of-all-pages'] + const page = +results.headers.page + removeAllChildNodes(resultsListEl) + renderResults(results.data) + 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 getDataForPage(page) { - const url = `/api/v1/dictionaries/${dictionaryId}/listDomainLabels?p=${page}` - const { data } = await axios.get(url) - return data - } - - function renderResults(results) { - results.forEach(result => { - const rowEl = document.createElement('tr') - const input = document.createElement('input') - const th = document.createElement('th') - const input2 = document.createElement('input') - const td1 = document.createElement('td') - const td2 = document.createElement('td') - const div = document.createElement('div') - const editBtn = document.createElement('button') - const deleteBtn = document.createElement('button') - const imgEditEl = document.createElement('img') - const imgDelEl = document.createElement('img') - input.value = result.id - input.type = 'hidden' - input.name = 'domainLabelId' - th.scope = 'row' - input2.className = 'form-check checkbox-table' - input2.type = 'checkbox' - input2.name = 'isVisible' - input2.disabled = true - input2.checked = !!result.isVisible - th.append(input2) - td1.className = 'tdata-area' - td1.textContent = result.name - - td2.classList.add('buttons-group') - div.classList.add('table-buttons') - editBtn.className = 'p-0 table-button-grp me-3 edit-row-btn' - editBtn.type = 'button' - imgEditEl.src = '/images/u_edit-alt.svg' - deleteBtn.className = 'p-0 table-button-grp delete-row-btn' - deleteBtn.dataset.bsTarget = '#alert-modal' - deleteBtn.dataset.bsToggle = 'modal' - deleteBtn.type = 'button' - imgDelEl.src = '/images/red-trash-icon.svg' - td2.append(div) - div.append(editBtn, deleteBtn) - editBtn.append(imgEditEl) - deleteBtn.append(imgDelEl) - - rowEl.append(input, th, td1, td2) - resultsListEl.appendChild(rowEl) - }) + alert(message) + updatePager() } } + + async function getDataForPage(page) { + // const url = `/api/v1/dictionaries/${dictionaryId}/listDomainLabels?p=${page}` + const url = `/api/v1/dictionaries/${dictionaryId}/domainLabels?q=${queryBattery}&p=${page}` + // const { data } = await axios.get(url) + return await axios.get(url) + } + + function renderResults(results) { + replaceContainer('page-results', results) + } + + const updatePaginationOnFilter = axiosResult => { + const numberOfAllPages = +axiosResult.headers['number-of-all-pages'] + const page = +axiosResult.headers.page + // removeAllChildNodes(resultsListEl) + // renderResults(results.data) + // console.log(numberOfAllPages) + // console.log(page) + // console.log(updatePager) + updatePager(page, numberOfAllPages) + } + + /// / Due to unsual design, the function was moved inside + function searchController() { + if (window.location.pathname.includes('podrocne-oznake')) { + queryBattery = document.getElementById('input-search').value + // console.log(queryBattery) + axios + .get( + `/api/v1/dictionaries/${ + window.location.pathname.split('/')[2] + }/domainLabels?q=${queryBattery}&p=${1}` + ) + .then(result => { + updatePaginationOnFilter(result) + replaceContainer('page-results', result.data) + }) + } + } + + const inlineSearchButton = document.getElementById('inline-search-btn') + + if (inlineSearchButton) { + inlineSearchButton.addEventListener('click', searchController) + $('#input-search').on('keyup', function (e) { + if (e.code === 'Enter' || e.code === 'NumpadEnter') { + searchController() + } + }) + } + /// // } if (/\/slovarji\/\d+\/napredno/.test(currentPagePath)) { @@ -1217,8 +1366,8 @@ function mobileMoveContent() { currentPagePath.includes('slovarji') && !currentPagePath.includes('admin') ) - navTitle.textContent = 'Urejanje' - else navTitle.textContent = 'Administrator' + navTitle.textContent = i18next.t('Urejanje') + else navTitle.textContent = i18next.t('Administracija') siteHeading.style.display = 'block' } } @@ -1310,9 +1459,9 @@ function createNewAreaInput(pageForm) { divSmallNameArea.className = 'author mt-4 added-field' divSubjectName.className = 'subject-name' spanInputNameTxtSlo.className = 'input-name-txt' - spanInputNameTxtSlo.textContent = 'NOVO PODPODROČJE (slovensko)' + spanInputNameTxtSlo.textContent = i18next.t('NOVO PODPODROČJE (slovensko)') spanInputNameTxtEng.className = 'input-name-txt mt-4' - spanInputNameTxtEng.textContent = 'NOVO PODPODROČJE (angleško)' + spanInputNameTxtEng.textContent = i18next.t('NOVO PODPODROČJE (angleško)') divRow.className = 'row align-items-center' divRow2.className = 'row align-items-center' divEnglishInput.className = 'mt-4' @@ -1333,11 +1482,12 @@ function createNewAreaInput(pageForm) { divColSm.className = 'col-sm mt-3 mt-sm-0' divColSm2.className = 'col-sm mt-3 mt-sm-0' spanNameInfoTxt.className = 'd-sm-inline name-info-txt ms-xxl-3 ms-md-3' - spanNameInfoTxt.textContent = + spanNameInfoTxt.textContent = i18next.t( 'Vpišite novo podpodročje. Na seznamu podpodročij bo vidno takoj po potrditvi administratorja portala.' + ) spanNameInfoTxtEng.className = 'd-sm-inline name-info-txt ms-xxl-3 ms-md-3 mt-4' - spanNameInfoTxtEng.textContent = 'Novo podpodročje (angleško).' + spanNameInfoTxtEng.textContent = i18next.t('Novo podpodročje (angleško).') divSmallNameArea.appendChild(divSubjectName) divSubjectName.appendChild(spanInputNameTxtSlo) @@ -1468,6 +1618,10 @@ function changePageContent(el) { const collapsableBtn = document.querySelectorAll('.collapsable-entry-btn') const addedFields = document.querySelectorAll('.added-field') const collapsibleData = document.querySelectorAll('.collapsible-data') + const responseModal = new bootstrap.Modal( + document.getElementById('duplicate-modal'), + { keyboard: false } + ) switch (el) { case 'content-preview': loadPreview(info) @@ -1530,6 +1684,7 @@ function changePageContent(el) { break case 'new-entry-btn': { // formEditContent.reset() + formEditContent.removeAttribute('data-entry-id') formEditContent.action = '/api/v1/entries/create' if (editSection.classList.contains('d-none')) @@ -1546,6 +1701,16 @@ function changePageContent(el) { previewBtnEl.disabled = true delete newEntryBtn.dataset.term } + // editBtnEl.click() + editBtnEl.classList.add('active-site-link') + previewBtnEl.classList.remove('active-site-link') + commentsBtnEl.classList.remove('active-site-link') + previewButtonsGroup.classList.remove('d-none') + commentsButtonsGroup.classList.add('d-none') + previewSection.classList.add('d-none') + editSection.classList.remove('d-none') + commentsSection.classList.add('d-none') + resizeFields.forEach(el => autoResize(el)) authorEl.textContent = '' versionEl.textContent = 'Verzija 1' previewAuthor.textContent = '' @@ -1558,15 +1723,19 @@ function changePageContent(el) { deleteEntry.disabled = true changeCollapsedContent() if (addedFields.length) addedFields.forEach(el => el.remove()) - editBtnEl.click() - termInputField.focus() const messageContainer = document.querySelectorAll('.message-container') messageContainer.forEach(el => el.classList.add('d-none')) window.scrollTo({ top: 0, behavior: 'smooth' }) selectedEntry.classList.remove('selected-term-btn') $('.multiple').val(null).trigger('change') $('.without-dropdown').val(null).trigger('change') - editBtnEl.click() + // editBtnEl.click() + // termInputField.addEventListener('focus', () => + // showMCButtons(termInputField, 'link') + // ) + termInputField.focus() + termInputField.click() + break } case 'first': @@ -1610,7 +1779,6 @@ function changePageContent(el) { commentsBtnEl.disabled = true commentsBtnEl.classList.add('disabled') showDates.disabled = true - duplicateEntry.disabled = true deleteEntry.disabled = true changeCollapsedContent() editBtnEl.click() @@ -1618,6 +1786,8 @@ function changePageContent(el) { window.scrollTo({ top: 0, behavior: 'smooth' }) selectedEntry.classList.remove('selected-term-btn') editBtnEl.click() + responseModal.toggle() + duplicateEntry.disabled = true break case 'true': spanFilterText.classList.remove('normal-gray-label') @@ -1798,8 +1968,9 @@ function loadPreview(info) { : linksArr[i][key] === 'broader' ? 'BT:' : '' - - linkedTerms.innerHTML += keyTxt + ' ' + key + ' ' + linkedTerms.innerHTML += keyTxt + ' ' + key + if (parseInt(i) !== linksArr.length - 1) linkedTerms.innerHTML += ', ' + else linkedTerms.innerHTML += ' ' } } } else changeClasses(previewLinkedTerms, 'hide') @@ -1924,6 +2095,7 @@ function changeVersionList(versions) { dateLabel.dataset.bsCustomClass = 'dark-gray-tooltip' dateLabel.dataset.bsPlacement = 'bottom' dateLabel.dataset.bsToggle = 'tooltip' + dateLabel.dataset.bsHtml = 'true' const date = new Date(el.version_time).toLocaleDateString( 'sl-SL', localeOptions @@ -1932,9 +2104,16 @@ function changeVersionList(versions) { dateRadio.id = `date${el.version}` dateRadio.value = `${el.version}` dateLabel.htmlFor = `date${el.version}` + // new bootstrap.Tooltip(dateLabel, { + // title: `Verzija ${el.version}
Avtor: ${el.version_author}` + // }) // eslint-disable-next-line new bootstrap.Tooltip(dateLabel, { - title: `Verzija ${el.version}` + title: + i18next.t('Verzija') + + `${el.version}
` + + i18next.t('Avtor:') + + `${el.version_author}` }) allDatesEl.append(dateRadio) allDatesEl.appendChild(dateLabel) @@ -1942,6 +2121,30 @@ function changeVersionList(versions) { } } +function setLatestVersion(data) { + const latestVersionLabel = document.getElementById('latest-version-label') + // const tooltip = new bootstrap.Tooltip(latestVersionLabel, { + // title: `Verzija: ${data.version}
Avtor: ${data.version_author}`, + // customClass: 'dark-gray-tooltip', + // html: true, + // placement: 'bottom' + // }) + // eslint-disable-next-line + const tooltip = new bootstrap.Tooltip(latestVersionLabel, { + title: + i18next.t('Verzija:') + + `${data.version}
` + + i18next.t('Avtor:') + + `${data.version_author}`, + customClass: 'dark-gray-tooltip', + html: true, + placement: 'bottom' + }) + // Can't think of any better solution for the not disapearing label bug + const dateArea = document.querySelector('.date-scroller') + dateArea.addEventListener('click', () => tooltip.hide()) +} + function removeOldMedia(images, audio, video) { if (images !== null || audio !== null || video !== null) { const media = [images, audio, video] @@ -1998,7 +2201,7 @@ function showSelectedDateData(date) { // classicOverview.classList.add('d-none') // selectedOverview.classList.remove('d-none') btnSaveIcon.firstChild.src = '/images/u_redo.svg' - btnSaveIcon.children[1].textContent = 'Obnovi' + btnSaveIcon.children[1].textContent = i18next.t('Obnovi') btnSaveIcon.id = 'redo-action' } @@ -2038,7 +2241,7 @@ function changeCollapsedContent() { function createHeadword() { const { termInputField, headerwordTable } = window.dictionaryElements - if (!termInputField.value.length) alert('Vnesti morate termin') + if (!termInputField.value.length) alert(i18next.t('Vnesti morate termin')) else { const createHeadwordGroup = document.getElementById('create-headword-group') const createdHeadwordGroup = document.getElementById( @@ -2181,21 +2384,21 @@ function addField(form, element, content) { if (formEditContent) { switch (element) { case newImageBtn: - spanName.textContent = 'SLIKA' + spanName.textContent = i18next.t('SLIKA') inputField.name = 'image' - spanNameInfoTxt.textContent = 'Nova slika.' + spanNameInfoTxt.textContent = i18next.t('Nova slika.') if (content) inputField.value = content break case newAudioBtn: - spanName.textContent = 'ZVOK' + spanName.textContent = i18next.t('ZVOK') inputField.name = 'audio' - spanNameInfoTxt.textContent = 'Nov zvok.' + spanNameInfoTxt.textContent = i18next.t('Nov zvok.') if (content) inputField.value = content break case newVideoBtn: - spanName.textContent = 'VIDEO' + spanName.textContent = i18next.t('VIDEO') inputField.name = 'video' - spanNameInfoTxt.textContent = 'Nov video.' + spanNameInfoTxt.textContent = i18next.t('Nov video.') if (content) inputField.value = content break } @@ -2204,10 +2407,11 @@ function addField(form, element, content) { element.parentElement.parentElement.parentElement ) } else { - spanName.textContent = 'AVTOR' + spanName.textContent = i18next.t('AVTOR') inputField.name = 'author' - spanNameInfoTxt.textContent = - 'Dodajte ime in priimek naslednjega avtorja slovarja..' + spanNameInfoTxt.textContent = i18next.t( + 'Dodajte ime in priimek naslednjega avtorja slovarja.' + ) form.insertBefore( divMarginTop, element.parentElement.parentElement.parentElement @@ -2224,7 +2428,6 @@ function addConnectionField(form, element, data) { const divRow = document.createElement('div') const divColLg2 = document.createElement('div') const divColLg4 = document.createElement('div') - const divColLg = document.createElement('div') const colLg2Input = document.createElement('select') const inputGroup = document.createElement('div') const inputField = document.createElement('input') @@ -2243,7 +2446,6 @@ function addConnectionField(form, element, data) { divColLg2.appendChild(colLg2Input) colLg2Input.appendChild(opt) divRow.appendChild(divColLg4) - divRow.appendChild(divColLg) divColLg4.appendChild(inputGroup) inputGroup.appendChild(inputField) inputGroup.appendChild(spanInputGroup) @@ -2252,32 +2454,34 @@ function addConnectionField(form, element, data) { divColSm.appendChild(spanNameInfoTxt) divMarginTop.className = 'mt-4 added-field' - divSubjectName.className = 'subject-name' + divSubjectName.className = + 'subject-name col-sm-6 d-flex justify-content-between' spanName.className = 'input-name-txt' divRow.className = 'row align-items-center' - divColLg2.className = 'col-lg-2 col-6' - divColLg4.className = 'col-lg-4 mt-2 mt-lg-0' - divColLg.className = 'col-lg align-items-center' + divColLg2.className = 'col-xl-2 col-4' + divColLg4.className = 'col-8 col-xl-6 col-xxl-4' colLg2Input.className = 'name-input form-select d-inline' colLg2Input.name = 'type' inputGroup.className = 'input-group' - inputField.className = 'name-input d-inline form-control icon-trash mc-field' + inputField.className = + 'name-input d-inline form-control icon-trash mc-field dispatch-tab' spanInputGroup.className = 'input-group-text delete-author-btn' spanInputGroup.id = 'trash-icon-btn' spanInputGroup.addEventListener('click', deleteField) imgTrashIcon.className = 'delete-author p-0' imgTrashIcon.src = '/images/red-trash-icon.svg' imgTrashIcon.alt = 'Delete' - divColSm.className = 'col-sm' - spanNameInfoTxt.className = 'name-info-txt mt-3' - spanName.textContent = 'POVEZAVA' + divColSm.className = 'col d-none d-xl-flex align-items-center' + spanNameInfoTxt.className = 'd-md-inline d-block name-info-txt mt-3 mt-sm-0' + spanNameInfoTxt.textContent = i18next.t('Nov povezan termin.') + spanName.textContent = i18next.t('POVEZANI TERMIN') inputField.name = 'links' opt.value = 'broader' - opt.text = 'Širši' + opt.text = i18next.t('Širši') opt2.value = 'related' opt3.value = 'narrow' - opt2.text = 'Sorodni' - opt3.text = 'Ožji' + opt2.text = i18next.t('Sorodni') + opt3.text = i18next.t('Ožji') colLg2Input.add(opt2) colLg2Input.add(opt) colLg2Input.add(opt3) @@ -2286,6 +2490,32 @@ function addConnectionField(form, element, data) { divMarginTop, element.parentElement.parentElement.parentElement ) + + // Create mixed content buttons above input field + const mcDiv = document.createElement('div') + const supscriptBtn = document.createElement('button') + const subscriptBtn = document.createElement('button') + const supImg = document.createElement('img') + const subImg = document.createElement('img') + + mcDiv.appendChild(supscriptBtn) + mcDiv.appendChild(subscriptBtn) + supscriptBtn.appendChild(supImg) + subscriptBtn.appendChild(subImg) + + mcDiv.className = 'mc-buttons-group d-none' + supscriptBtn.className = 'mc-button mc-supscript' + supscriptBtn.type = 'button' + subscriptBtn.className = 'mc-button mc-subscript' + subscriptBtn.type = 'button' + supImg.src = '/images/superscript.svg' + supImg.className = 'mc-center-img' + subImg.className = 'mc-center-img' + subImg.src = '/images/subscript.svg' + + divSubjectName.appendChild(mcDiv) + // eslint-disable-next-line + inputField.addEventListener('focus', () => showMCButtons(inputField, 'link')) if (data) { inputField.value = data.link colLg2Input.value = data.type @@ -2336,14 +2566,14 @@ function handleAreasClick({ target }) { const saveButton = document.createElement('button') saveButton.type = 'button' cancelButton.type = 'button' - cancelButton.textContent = 'Prekliči' + cancelButton.textContent = i18next.t('Prekliči') cancelButton.className = 'btn btn-secondary me-2' cancelButton.style.height = '33px' cancelButton.style.width = '105px' cancelButton.addEventListener('click', () => abortEditing(newButtonGroup, tableButtons, tDataArea) ) - saveButton.textContent = 'POTRDI' + saveButton.textContent = i18next.t('POTRDI') saveButton.type = 'button' saveButton.className = 'btn btn-primary' saveButton.style.height = '33px' @@ -2666,7 +2896,7 @@ function checkLanguages() { } $('.summernote').summernote({ - placeholder: 'Na kratko opišite zasnovo in namen slovarja.', + placeholder: i18next.t('Na kratko opišite zasnovo in namen slovarja.'), height: 300, minheight: 150, toolbar: [ diff --git a/express/public/javascripts/dictionary-export.js b/express/public/javascripts/dictionary-export.js new file mode 100644 index 0000000..9f16590 --- /dev/null +++ b/express/public/javascripts/dictionary-export.js @@ -0,0 +1,24 @@ +/* globals axios, i18next */ + +const exportForm = document.forms['dictionary-export'] + +exportForm.addEventListener('submit', async event => { + event.preventDefault() + + const payload = Object.fromEntries(new FormData(event.target)) + + try { + await axios.post(event.target.action, payload) + // TODO This (page refresh) is a temporary hack. Replace with proper UI updating logic. + location.reload() + } catch (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.') + } + + alert(message) + } +}) diff --git a/express/public/javascripts/dictionary-import-extraction.js b/express/public/javascripts/dictionary-import-extraction.js index e6a683f..2af6c4e 100644 --- a/express/public/javascripts/dictionary-import-extraction.js +++ b/express/public/javascripts/dictionary-import-extraction.js @@ -1,11 +1,16 @@ -/* global axios, initPagination, removeAllChildNodes */ +/* global axios, initPagination, removeAllChildNodes, i18next, dictionaryId */ { const selectorEl = document.getElementById('select-extraction-name') selectorEl.addEventListener('change', () => loadCandidates(selectorEl.value)) const resultsListEl = document.getElementById('page-results') + const importFormEl = document.getElementById('import-form') + const importButtonEl = document.getElementById('import-btn') let termCandidates let hitsPerPage let numberOfAllPages + + importFormEl.addEventListener('submit', submitForm) + async function loadCandidates(id) { try { const { data } = await axios.get( @@ -18,6 +23,8 @@ const results = getDataForFirstPage(data) renderResults(results) updateDemoPager(1, numberOfAllPages) + importFormEl.action = `/api/v1/dictionaries/${dictionaryId}/import-extraction/${id}` + importButtonEl.disabled = false } catch (error) { console.log(error) } @@ -64,9 +71,27 @@ tdId.textContent = sequentialCount tdName.textContent = candidate.kanonicnaoblika tdSize.textContent = candidate.ranking - tdDate.textContent = candidate.pogostostpojavljanja + tdDate.textContent = candidate.pogostostpojavljanja[0] rowEl.append(tdId, tdName, tdSize, tdDate) resultsListEl.appendChild(rowEl) }) } + + async function submitForm(event) { + event.preventDefault() + const payload = Object.fromEntries(new FormData(event.target)) + + try { + await axios.post(event.target.action, payload) + alert(i18next.t('SLOVAR UVOŽEN')) + } catch (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.') + } + alert(message) + } + } } diff --git a/express/public/javascripts/extraction-edit-own.js b/express/public/javascripts/extraction-edit-own.js new file mode 100644 index 0000000..aad3c8c --- /dev/null +++ b/express/public/javascripts/extraction-edit-own.js @@ -0,0 +1,7 @@ +const nameEl = document.getElementById('name') +nameEl.addEventListener('input', enableButton) + +function enableButton() { + const disabledBtn = document.getElementById('mpbtn') + disabledBtn.classList.remove('disabled') +} diff --git a/express/public/javascripts/extraction-files-edit.js b/express/public/javascripts/extraction-files-edit.js index 9e0490a..ae9a7fb 100644 --- a/express/public/javascripts/extraction-files-edit.js +++ b/express/public/javascripts/extraction-files-edit.js @@ -1,16 +1,20 @@ -/* global axios, removeAllChildNodes, bootstrap */ +/* global axios, removeAllChildNodes, bootstrap, i18next */ +const fileUploadContainerEl = document.getElementById('upload-files-container') const fileUploadForm = document.forms['upload-files'] const fileInputEl = fileUploadForm.querySelector('input[type="file"]') const filesListEl = document.getElementById('files-list') const dropArea = document.querySelector('.drag-area') const dragButton = dropArea.querySelector('button') const dragInput = dropArea.querySelector('input') -const modalSpinner = new bootstrap.Modal( - document.getElementById('modal-spinner') -) const modalAlert = new bootstrap.Modal(document.getElementById('alert-modal')) let file +let fileListElCount = filesListEl.childElementCount +let isUploadInProgress = false +let isDeletionInProgress = false +const existingFileNames = [...filesListEl.children].map( + fileListEl => fileListEl.querySelector('.file-name').textContent +) dragButton.onclick = () => { dragInput.click() @@ -68,96 +72,216 @@ fileInputEl.addEventListener('change', submitFiles) filesListEl.addEventListener('click', handleFileClick) async function submitFiles() { - // TODO Lock additional submits for the duration of this function execution? + if (isUploadInProgress) return + isUploadInProgress = true + fileUploadContainerEl.hidden = true const MAX_FILE_SIZE = 10 ** 9 // 1 GB + const filesToUpload = [] const failedUploads = [] - - modalSpinner.toggle() + const succeededFileListEls = [] + let didRemoveAnyFileListEls = false for (const file of fileInputEl.files) { + const filename = file.name + + const indexOfSameNamed = existingFileNames.indexOf(filename) + if (indexOfSameNamed !== -1) { + filesListEl.children[indexOfSameNamed].remove() + existingFileNames.splice(indexOfSameNamed, 1) + didRemoveAnyFileListEls = true + } + + const fileListEl = createFileListEl(filename) + filesListEl.appendChild(fileListEl) + existingFileNames.push(filename) + if (file.size > MAX_FILE_SIZE) { const failedUpload = { - filename: file.name, + filename, message: 'File too large. Must not be over 1 GB.' } failedUploads.push(failedUpload) + updateFileListEl(fileListEl, { status: 'failed' }) continue } - const payload = new FormData() - payload.set(fileInputEl.name, file) + filesToUpload.push([file, fileListEl]) + } + + if (didRemoveAnyFileListEls) reindexFileListEls() + + for (const [file, fileListEl] of filesToUpload) { try { - await axios.put(apiEndpointBase, payload) + const payload = new FormData() + payload.set(fileInputEl.name, file) + const { data: fileStats } = await axios.put(apiEndpointBase, payload, { + onUploadProgress: displayUploadProgress(fileListEl) + }) + succeededFileListEls.push(fileListEl) + updateFileListEl(fileListEl, { status: 'success', fileStats }) } catch (error) { const failedUpload = { filename: file.name, message: error.response.data } failedUploads.push(failedUpload) + updateFileListEl(fileListEl, { status: 'failed' }) } } - 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) - modalSpinner.toggle() + succeededFileListEls.forEach(el => updateFileListEl(el, { status: 'done' })) + if (failedUploads.length) displayFailedUploads(failedUploads) + isUploadInProgress = false + fileUploadContainerEl.hidden = false } -function updateFilesList(files) { - removeAllChildNodes(filesListEl) - files.forEach(({ filename, size, timeModified }, index) => { - const rowEl = document.createElement('tr') - const tdId = document.createElement('td') - const tdName = document.createElement('td') - const tdSize = document.createElement('td') - const tdDate = document.createElement('td') - const tdDelete = document.createElement('td') - tdId.textContent = index + 1 - tdName.textContent = filename - tdName.className = 'filename' - tdSize.textContent = size - const formattedDate = new Date(timeModified).toLocaleDateString('sl-SL') - tdDate.textContent = formattedDate - const delBtn = document.createElement('button') - delBtn.className = 'p-0 delete-file delete-btn-table' - delBtn.type = 'button' - const deleteImg = document.createElement('img') - deleteImg.src = '/images/red-trash-icon.svg' - deleteImg.alt = 'Izbriši' - const delSpan = document.createElement('span') - delSpan.className = 'ms-2' - delSpan.textContent = 'Briši' - delBtn.appendChild(deleteImg) - delBtn.appendChild(delSpan) - tdDelete.appendChild(delBtn) - rowEl.append(tdId, tdName, tdSize, tdDate, tdDelete) - filesListEl.appendChild(rowEl) - }) +function createFileListEl(filename) { + const indexEl = document.createElement('td') + indexEl.className = 'file-index' + indexEl.textContent = ++fileListElCount + + const nameEl = document.createElement('td') + nameEl.className = 'file-name' + nameEl.textContent = filename + + const sizeEl = document.createElement('td') + sizeEl.className = 'file-size' + + const dateEl = document.createElement('td') + dateEl.className = 'file-date-modified' + + const progressBarId = `progress-bar-${fileListElCount}` + const progressLabelEl = document.createElement('label') + progressLabelEl.className = 'me-1' + progressLabelEl.for = progressBarId + progressLabelEl.textContent = '0%' + const progressBarEl = document.createElement('progress') + progressBarEl.id = progressBarId + progressBarEl.className = 'upload-progress' + progressBarEl.value = 0 + progressBarEl.max = 100 + const lastEl = document.createElement('td') + lastEl.className = 'file-last-cell' + lastEl.append(progressLabelEl, progressBarEl) + + const fileListEl = document.createElement('tr') + fileListEl.append(indexEl, nameEl, sizeEl, dateEl, lastEl) + + return fileListEl +} + +function updateFileListEl(fileListEl, { status, fileStats, index }) { + if (status === 'success') { + const nameEl = fileListEl.querySelector('.file-name') + nameEl.textContent = fileStats.filename + + const sizeEl = fileListEl.querySelector('.file-size') + sizeEl.textContent = fileStats.size + + const dateEl = fileListEl.querySelector('.file-date-modified') + const formattedDate = new Date(fileStats.timeModified).toLocaleDateString( + 'sl-SL' + ) + dateEl.textContent = formattedDate + + const successImgEl = document.createElement('img') + successImgEl.src = '/images/valid.svg' + successImgEl.alt = i18next.t('Datoteka uspešno naložena') + const lastEl = fileListEl.querySelector('.file-last-cell') + removeAllChildNodes(lastEl) + lastEl.append(successImgEl) + } else if (status === 'done') { + const deleteImgEl = document.createElement('img') + deleteImgEl.src = '/images/red-trash-icon.svg' + deleteImgEl.alt = '' + const deleteSpanEl = document.createElement('span') + deleteSpanEl.className = 'ms-2' + deleteSpanEl.textContent = 'Briši' + const deleteButtonEl = document.createElement('button') + deleteButtonEl.className = 'p-0 delete-file delete-btn-table' + deleteButtonEl.append(deleteImgEl, deleteSpanEl) + const lastEl = fileListEl.querySelector('.file-last-cell') + removeAllChildNodes(lastEl) + lastEl.append(deleteButtonEl) + } else if (status === 'reindex') { + const indexEl = fileListEl.querySelector('.file-index') + indexEl.textContent = index + 1 + } else if (status === 'failed') { + const failImgEl = document.createElement('img') + failImgEl.src = '/images/x_red.svg' + failImgEl.alt = i18next.t('Napaka pri nalaganju datoteke') + const lastEl = fileListEl.querySelector('.file-last-cell') + removeAllChildNodes(lastEl) + lastEl.append(failImgEl) + } } function displayFailedUploads(failedUploads) { + const modalContentEl = modalAlert._element.querySelector( + '.modal-alert-content' + ) + modalContentEl.classList.remove('d-flex') + + const descriptionEl = modalAlert._element.querySelector('#alert-text') + descriptionEl.textContent = i18next.t( + 'Naslednje datoteke niso bile naložene zaradi navedenih razlogov:' + ) + + modalContentEl.querySelector('ul')?.remove() + + const listEl = document.createElement('ul') + modalContentEl.append(listEl) + failedUploads.forEach(({ filename, message }) => { - const alertText = modalAlert.querySelector('#alert-text') - alertText.textContent = `NAPAKA - Ime datoteke: ${filename}, razlog: ${message}` - modalAlert.toggle() + const bulletEl = document.createElement('li') + bulletEl.textContent = `${filename} - ${message}` + listEl.appendChild(bulletEl) }) + + modalAlert.toggle() } async function handleFileClick(e) { if (e.target.closest('.delete-file')) { + if (isUploadInProgress) { + alert( + i18next.t('Brisanje je onemogočeno, doker se nalagajo nove datoteke.') + ) + return + } + if (isDeletionInProgress) { + alert(i18next.t('Brisanje je onemogočeno, saj je eno še v procesu.')) + return + } + isDeletionInProgress = true const fileEl = e.target.closest('tr') - const filename = fileEl.querySelector('.filename').textContent + const filename = fileEl.querySelector('.file-name').textContent try { await axios.delete(`${apiEndpointBase}/${filename}`) fileEl.remove() + existingFileNames.splice(existingFileNames.indexOf(filename), 1) + reindexFileListEls() + isDeletionInProgress = false } catch { - alert('Pri brisanju datoteke je prišlo do napake.') + alert(i18next.t('Pri brisanju datoteke je prišlo do napake.')) } } } + +function displayUploadProgress(fileListEl) { + const progressEl = fileListEl.querySelector('.upload-progress') + return function ({ loaded, total }) { + const percentCompleted = Math.round((loaded / total) * 100) + progressEl.setAttribute('value', percentCompleted) + progressEl.previousElementSibling.textContent = `${percentCompleted}%` + } +} + +function reindexFileListEls() { + fileListElCount = filesListEl.childElementCount + for (let i = 0; i < fileListElCount; i++) { + const fileListEl = filesListEl.children[i] + updateFileListEl(fileListEl, { status: 'reindex', index: i }) + } +} diff --git a/express/public/javascripts/extraction-list.js b/express/public/javascripts/extraction-list.js index a51bce5..9bfd66b 100644 --- a/express/public/javascripts/extraction-list.js +++ b/express/public/javascripts/extraction-list.js @@ -16,15 +16,20 @@ async function onListClick({ target }) { modalUseBtn.addEventListener('click', async () => { await axios.delete(`/api/v1/extraction/${extractionId}`) extractionEl.remove() + // TODO This (page refresh) is an unneeded workaround for an otherwise simple user friendlier solution. + location.reload() }) - } else if (target.classList.contains('btn-begin')) { + } else if (target.closest('.btn-begin')) { const extractionEl = target.closest('.task') const extractionId = extractionEl.dataset.id await axios.put(`/api/v1/extraction/${extractionId}/begin`) - const responseModal = new bootstrap.Modal( - document.getElementById('begin-response') - ) + const beginExtractionModalEl = document.getElementById('begin-response') + const responseModal = new bootstrap.Modal(beginExtractionModalEl) responseModal.toggle() + // TODO This (page refresh) is a temporary hack. Replace with proper UI updating logic. + beginExtractionModalEl.addEventListener('hide.bs.modal', () => + location.reload() + ) } else if (target.classList.contains('btn-duplicate')) { const extractionEl = target.closest('.task') const extractionId = extractionEl.dataset.id diff --git a/express/public/javascripts/extraction-oss-edit.js b/express/public/javascripts/extraction-oss-edit.js index a0511a1..af8d63e 100644 --- a/express/public/javascripts/extraction-oss-edit.js +++ b/express/public/javascripts/extraction-oss-edit.js @@ -1,4 +1,4 @@ -/* global $, axios, bootstrap */ +/* global $, axios, bootstrap, i18next */ $('.pick-multiple').select2() $('.enter-multiple').select2({ @@ -41,8 +41,8 @@ async function handleSearch() { } function handleSearchError() { - const alertText = modalAlert.querySelector('#alert-text') - alertText.textContent = `NAPAKA pri iskanju` + const alertText = modalAlert._element.querySelector('#alert-text') + alertText.textContent = i18next.t('NAPAKA pri iskanju') modalAlert.toggle() } diff --git a/express/public/javascripts/extraction-term-candidates.js b/express/public/javascripts/extraction-term-candidates.js index a95a8a3..a8064a4 100644 --- a/express/public/javascripts/extraction-term-candidates.js +++ b/express/public/javascripts/extraction-term-candidates.js @@ -32,7 +32,7 @@ tdId.textContent = sequentialCount tdName.textContent = candidate.kanonicnaoblika tdSize.textContent = candidate.ranking - tdDate.textContent = candidate.pogostostpojavljanja + tdDate.textContent = candidate.pogostostpojavljanja[0] rowEl.append(tdId, tdName, tdSize, tdDate) resultsListEl.appendChild(rowEl) }) diff --git a/express/public/javascripts/extraction.js b/express/public/javascripts/extraction.js index 079d73c..1f2610b 100644 --- a/express/public/javascripts/extraction.js +++ b/express/public/javascripts/extraction.js @@ -1,4 +1,4 @@ -/* global currentPagePath */ +/* global currentPagePath, i18next */ // const currentPagePath = location.pathname @@ -103,22 +103,22 @@ function initExtraction() { btnStart.type = 'button' btnImg.src = '/images/fi_arrow-right-circle.svg' btnSpan.className = 'ms-1' - btnSpan.textContent = 'Začni' + btnSpan.textContent = i18next.t('Začni') hr.className = 'mt-2 mb-3' divSmFlex2.className = 'd-flex justify-content-between' divSmFlexACMb.className = 'd-sm-flex align-items-center mb-0' imgAlertCircle.src = '/images/alert-circle.svg' spanNew.className = 'normal-gray ms-1' - spanNew.textContent = 'Nov' + spanNew.textContent = i18next.t('Nov') divSmFlexACMbMe.className = 'd-sm-flex align-content-center mb-0 me-3' divEditTask.className = 'align-items-center me-3 edit-task' imgEditAlt.src = '/images/u_edit-alt.svg' spanEdit.className = 'ms-1 normal-gray' - spanEdit.textContent = 'Uredi' + spanEdit.textContent = i18next.t('Uredi') divDeleteTask.className = 'ms-3 align-items-center delete-task' imgDeleteAlt.src = '/images/red-trash-icon.svg' spanDelete.className = 'ms-1 normal-gray' - spanDelete.textContent = 'Briši' + spanDelete.textContent = i18next.t('Briši') taskNewContainer.appendChild(divSmFlex) divSmFlex.appendChild(divSmGrid) @@ -233,7 +233,7 @@ function mobileMoveContent() { primaryButton.style.whiteSpace = '' } - navTitle.textContent = 'Urejanje' + navTitle.textContent = i18next.t('Urejanje') siteHeading.style.display = 'block' } } diff --git a/express/public/javascripts/login-and-register.js b/express/public/javascripts/login-and-register.js index 8c1f311..5e083a2 100644 --- a/express/public/javascripts/login-and-register.js +++ b/express/public/javascripts/login-and-register.js @@ -1,4 +1,4 @@ -/* global axios, validator, removeJumpLogic */ +/* global axios, validator, $, i18next */ /* @@ -27,6 +27,7 @@ const listenRegisterBtn = event => { loginHeader.className = loginHeader.className + ' d-none' regDescription.className = regDescription.className.replace('d-none', '') loginDescription.className = loginDescription.className + ' d-none' + clearErrorView() } const listenLoginBtn = event => { @@ -38,6 +39,7 @@ const listenLoginBtn = event => { regHeader.className = regHeader.className + ' d-none' loginDescription.className = loginDescription.className.replace('d-none', '') regDescription.className = regDescription.className + ' d-none' + clearErrorView() } regBtn.addEventListener('click', listenRegisterBtn) @@ -106,6 +108,23 @@ function updateLoginRegisterErrorView( } } +function clearErrorView() { + Object.keys(registerErrorIndicatorpairs).forEach(key => { + registerErrorIndicatorpairs[key][0].style.visibility = 'hidden' + registerErrorIndicatorpairs[key][1].style.visibility = 'hidden' + }) + + document.querySelectorAll('.error-login-icon').forEach(e => { + e.style.visibility = 'hidden' + }) + + document.querySelector('.error-text').style.visibility = 'hidden' + + document.getElementById('login-remember').checked = false + document.getElementById('terms-of-use').checked = false + document.getElementById('privacy-policy').checked = false +} + /* Show error message from the label that represents the error returned from the server Includes error X icons for login @@ -152,14 +171,23 @@ function updateRegisterWindowOnSuccess( footer.appendChild(document.createElement('button')) const btn = footer.firstElementChild btn.classList = 'btn btn-secondary text-secondary' - btn.textContent = 'ZAPRI' + btn.textContent = i18next.t('ZAPRI') btn.ariaLabel = 'Close' btn.dataset.bsDismiss = 'modal' const successDescriptionPararaph = body.children[1] - successDescriptionPararaph.textContent = `Pozdravljeni ${name} ${surname}, - na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili svoj uporabniški račun na Terminološkem portalu. - Povezava za potrditev je veljavna ${days} dni.` + // successDescriptionPararaph.textContent = `Pozdravljeni ${name} ${surname}, + // na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili svoj uporabniški račun na Terminološkem portalu. + // Povezava za potrditev je veljavna ${days} dni.` + + successDescriptionPararaph.textContent = + i18next.t('Pozdravljeni ') + + `${name} ${surname}, ` + + i18next.t( + 'na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili svoj uporabniški račun na Terminološkem portalu. Povezava za potrditev je veljavna ' + ) + + `${days} ` + + i18next.t('dni.') } // login and register @@ -199,7 +227,7 @@ function updateRegisterWindowOnSuccess( updateLoginRegisterErrorView( registerErrorLabels[iterator], true, - 'Prazno obvezno polje' + i18next.t('Prazno obvezno polje') ) failFrontendValidation = true } else { @@ -215,7 +243,7 @@ function updateRegisterWindowOnSuccess( updateLoginRegisterErrorView( 'error-email', true, - 'Neveljavna e-pošta' + i18next.t('Neveljavna e-pošta') ) } else { updateLoginRegisterErrorView('error-email', false) @@ -226,7 +254,7 @@ function updateRegisterWindowOnSuccess( updateLoginRegisterErrorView( 'error-password', true, - 'Geslo je prekratko' + i18next.t('Geslo je prekratko') ) } else { updateLoginRegisterErrorView('error-password', false) @@ -237,7 +265,7 @@ function updateRegisterWindowOnSuccess( updateLoginRegisterErrorView( 'error-password-repeat', true, - 'Geslo se ne ujema' + i18next.t('Geslo se ne ujema') ) } } @@ -263,11 +291,11 @@ function updateRegisterWindowOnSuccess( // event.target.messageBind.textContent = data } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } showErrorReturnedFromServer( @@ -279,3 +307,28 @@ function updateRegisterWindowOnSuccess( } } } + +function registerConditions() { + // Requirement 1: Terms of Use Checkbox checked + const requirement1 = document.getElementById('terms-of-use') + // Requirement 2: Privacy Policy checked + const requirement2 = document.getElementById('privacy-policy') + + const registerButton = document.getElementById('regbtnmain') + if (requirement1.checked && requirement2.checked) { + registerButton.disabled = false + } else { + registerButton.disabled = true + } +} + +document + .getElementById('terms-of-use') + .addEventListener('change', registerConditions) +document + .getElementById('privacy-policy') + .addEventListener('change', registerConditions) + +$('#staticBackdrop').on('hidden.bs.modal', function () { + clearErrorView() +}) diff --git a/express/public/javascripts/main-sreach-utils.js b/express/public/javascripts/main-sreach-utils.js index 3ab76ed..ef2c987 100644 --- a/express/public/javascripts/main-sreach-utils.js +++ b/express/public/javascripts/main-sreach-utils.js @@ -154,6 +154,21 @@ function searchQuery(searchString, searchFilterDOM = {}) { document.location.href = url } +function searchQueryWithExistingFilters(searchString) { + const stringBuilder = `/iskanje?q=${searchString}` + const thisUrl = new URL(location) + const url = new URL(stringBuilder, location.protocol + '//' + location.host) + + thisUrl.searchParams.forEach((value, key, parent) => { + // console.log(`VALUE: ${value}`) + if (!(key === 'q' || key === 'p')) { + url.searchParams.append(key, value) + } + }) + + document.location.href = url +} + // end search implementation functions // search filter functions @@ -173,7 +188,11 @@ function sbmFn(sbm) { inputString = '*' } - searchQuery(inputString, searchFilterDOM) + if (sbm === document.querySelector('.search-btn-a')) { + searchQuery(inputString, searchFilterDOM) + } else { + searchQueryWithExistingFilters(inputString) + } }) } } diff --git a/express/public/javascripts/mixed-content.js b/express/public/javascripts/mixed-content.js index fabb16e..474693d 100644 --- a/express/public/javascripts/mixed-content.js +++ b/express/public/javascripts/mixed-content.js @@ -1,21 +1,42 @@ const allMixedContentFields = document.querySelectorAll('.mc-field') allMixedContentFields.forEach(el => - el.addEventListener('focusin', () => showMCButtons(el)) + el.addEventListener('focus', () => showMCButtons(el)) ) document.addEventListener('keypress', e => { if (e.key === 'Enter' && e.target.classList.contains('dispatch-tab')) { - const form = event.target.form - const index = Array.prototype.indexOf.call(form, e.target) - form.elements[index + 1]?.focus() e.preventDefault() + const currentNode = e.target + const allElements = document.querySelectorAll('input, select, textarea') + const currentIndex = [...allElements].findIndex(el => + currentNode.isEqualNode(el) + ) + const targetIndex = (currentIndex + 1) % allElements.length + allElements[targetIndex].focus() } }) -function showMCButtons(element) { - const parent = element.parentElement.parentElement.parentElement +function showMCButtons(element, linkTerm) { + let parent + if (linkTerm) { + parent = element.parentElement.parentElement.parentElement.parentElement + } else { + parent = element.parentElement.parentElement.parentElement + } + const mainContent = document.getElementById('offset-main') + const sideMenu = document.querySelector('.content-nav-content') const btnGrp = parent.querySelector('.mc-buttons-group') + // Warning, do not touch *_* + document.addEventListener('click', function (event) { + if (mainContent.contains(event.target) || sideMenu.contains(event.target)) { + if (element !== event.target && document.activeElement !== element) { + if (!parent.contains(event.target)) { + btnGrp.classList.add('d-none') + } + } + } + }) btnGrp.classList.remove('d-none') const btnGrpChildren = btnGrp.children const childrenBtns = Array.from(btnGrpChildren) @@ -29,10 +50,10 @@ function showMCButtons(element) { { once: true } ) ) - document.addEventListener('click', function (event) { - if (parent !== event.target && !parent.contains(event.target)) { - btnGrp.classList.add('d-none') - } + + document.addEventListener('keyup', function (e) { + const selectedEl = document.activeElement + if (!parent.contains(selectedEl)) btnGrp.classList.add('d-none') }) } @@ -50,8 +71,9 @@ function addMixedContentInput(el, selectedEl) { inputText.substring(0, selectedStart) + '' + inputText.substring(selectedStart, selectedEnd) + - ' ' + + '' + inputText.substring(selectedEnd) + placeCaret(selectedEl, selectedEnd + 7) } if (el.classList.contains('mc-italic') && selectedText.length) { @@ -59,8 +81,9 @@ function addMixedContentInput(el, selectedEl) { inputText.substring(0, selectedStart) + '' + inputText.substring(selectedStart, selectedEnd) + - ' ' + + '' + inputText.substring(selectedEnd) + placeCaret(selectedEl, selectedEnd + 7) } if (el.classList.contains('mc-supscript') && selectedText.length) { @@ -68,8 +91,9 @@ function addMixedContentInput(el, selectedEl) { inputText.substring(0, selectedStart) + '' + inputText.substring(selectedStart, selectedEnd) + - ' ' + + '' + inputText.substring(selectedEnd) + placeCaret(selectedEl, selectedEnd + 11) } if (el.classList.contains('mc-subscript') && selectedText.length) { @@ -77,27 +101,41 @@ function addMixedContentInput(el, selectedEl) { inputText.substring(0, selectedStart) + '' + inputText.substring(selectedStart, selectedEnd) + - ' ' + + '' + inputText.substring(selectedEnd) + placeCaret(selectedEl, selectedEnd + 11) } if (el.classList.contains('mc-hyperlink') && selectedText.length) { selectedEl.value = inputText.substring(0, selectedStart) + - '' + + '
' + inputText.substring(selectedStart, selectedEnd) + - ' ' + + '' + inputText.substring(selectedEnd) } if (el.classList.contains('mc-line-break')) { selectedEl.value = inputText.substring(0, selectedStart) + - '
' + + '
' + inputText.substring(selectedEnd) + placeCaret(selectedEl, selectedEnd + 5) el.removeEventListener('click', () => addMixedContentInput, false) } +} - // selectedEl.focus() - // selectedEl.setSelectionRange(selectedStart, selectedEnd) +function placeCaret(elem, caretPos) { + if (elem != null) { + if (elem.createTextRange) { + const range = elem.createTextRange() + range.move('character', caretPos) + range.select() + } else { + if (elem.selectionStart) { + elem.focus() + elem.setSelectionRange(caretPos, caretPos) + } else elem.focus() + } + } } diff --git a/express/public/javascripts/pagination-extensions.js b/express/public/javascripts/pagination-extensions.js index f6a829f..c22e55f 100644 --- a/express/public/javascripts/pagination-extensions.js +++ b/express/public/javascripts/pagination-extensions.js @@ -27,6 +27,8 @@ return await axios.get(url) } */ +// TODO MARK FOR REVIEW WHETHER THIS METHODS ARE STILL NEEDED + function prepareQueryArrayForArrayWithIDs(page, searchQuery, filters = {}) { const qParams = new URL(location).searchParams qParams.set('p', page) diff --git a/express/public/javascripts/profile-scripts.js b/express/public/javascripts/profile-scripts.js new file mode 100644 index 0000000..592df16 --- /dev/null +++ b/express/public/javascripts/profile-scripts.js @@ -0,0 +1,11 @@ +/* global isI18nReady */ + +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' + ) +}) + +const redConfirm = document.querySelector('#modal-use-btn') +redConfirm.style.backgroundColor = '#AC7171' diff --git a/express/public/javascripts/query-focus-handler.js b/express/public/javascripts/query-focus-handler.js new file mode 100644 index 0000000..5602956 --- /dev/null +++ b/express/public/javascripts/query-focus-handler.js @@ -0,0 +1,26 @@ +// Note to myself: please optimize and remove the redundacy of the code written in multiple classes to achieve the same result as this + +const focusOnCompletePromtBase = queryInput => { + queryInput.focus() + queryInput.select() +} + +// Tradeoff: Declare a variable for the element to reduce duplication? +const inputQuery = document.querySelector('#search-query') +if (inputQuery) { + focusOnCompletePromtBase(inputQuery) + + if (window.location.pathname === '/slovarji') { + document + .querySelector('#dicts-search-btn') + .addEventListener('click', () => { + focusOnCompletePromtBase(inputQuery) + }) + + document + .querySelector('#search-in-filter-modal') + .addEventListener('click', () => { + focusOnCompletePromtBase(inputQuery) + }) + } +} diff --git a/express/public/javascripts/reset-password.js b/express/public/javascripts/reset-password.js new file mode 100644 index 0000000..921424a --- /dev/null +++ b/express/public/javascripts/reset-password.js @@ -0,0 +1,59 @@ +/* global $, axios, 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') + return false + } + return true +} + +// Handle submit event +document + .querySelector('#reset-and-redirect') + .addEventListener('submit', event => { + event.preventDefault() + if (verifyPassword()) { + const password = document.getElementById('reset-password').value + const repeatPassword = 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', { + 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 + }) + } + }) + +// Handle cancel event +document.querySelector('#cancel-btn').addEventListener('click', event => { + event.preventDefault() + window.location = '/' +}) + +// Handle redirect on success +document.querySelector('#modal-fp-info-close').addEventListener('click', () => { + window.location = '/' +}) diff --git a/express/public/javascripts/scripts.js b/express/public/javascripts/scripts.js index c4c0d13..cf31356 100644 --- a/express/public/javascripts/scripts.js +++ b/express/public/javascripts/scripts.js @@ -1,4 +1,14 @@ -/* global $, bootstrap */ +/* global $, bootstrap, axios, i18next, i18nextHttpBackend */ + +// eslint-disable-next-line no-unused-vars +const isI18nReady = i18next.use(i18nextHttpBackend).init({ + lng: document.documentElement.lang ?? 'sl', + ns: 'core', + nsSeparator: false, + keySeparator: false, + ...(window.inDevEnv && { saveMissing: true }), + ...(!window.inDevEnv && { fallbackLng: false }) +}) const windows = ['#suggestions-root', '.advanced-search-root', '.kbd-root'] @@ -302,7 +312,7 @@ function renderSuggestions( ajustElementSettings(preElement, options) - preElement.textContent = 'Za to besedo še ni podatkov.' + preElement.textContent = i18next.t('Za to besedo še ni podatkov.') preElement.disabled = true preElement.classList = ['w-100'] preElement.classList += ` ${type}-sugg` @@ -646,31 +656,53 @@ document.querySelectorAll('.loginregisterlabel').forEach(label => { /** * Enables pagination logic for the specified pager UI. * - * @param {string} paginationRootElId - Pager element id. + * @param {(string|string[])} paginationRootElIds - Pager element id. * @param {function} onPageChange - Gets called with the number of the page the user requested. * @returns {function} - Call it with (newCurrentPage, newNumOfAllPages) to update the ui. Or without arguments, after handling a potential error. */ -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') - +function initPagination(paginationRootElIds, onPageChange, currentPage = 1) { let reqLock = false + let numOfAllPages + const backwardBtnEls = [] + const forwardBtnEls = [] + const pageInputEls = [] + const pagesCountDisplayEls = [] - rootEl.addEventListener('click', handleButtonClick) - formEl.addEventListener('submit', handleFormSubmit) + if (Array.isArray(paginationRootElIds)) { + paginationRootElIds.forEach(id => initControls(id)) + } else { + initControls(paginationRootElIds) + } + const allControlEls = [...backwardBtnEls, ...forwardBtnEls, ...pageInputEls] - function handleButtonClick({ target }) { + 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 - const numOfAllPages = +pagesCountDisplayEl.textContent if (buttonEl.classList.contains('first-page')) { if (currentPage === 1) return @@ -691,13 +723,13 @@ function initPagination(paginationRootElId, onPageChange, currentPage = 1) { } } - function handleFormSubmit(e) { + function handleFormSubmit(e, pageInputEl) { e.preventDefault() if (reqLock) return const inputValue = +pageInputEl.value - if (!(inputValue > 0 && inputValue <= pagesCountDisplayEl.textContent)) { - alert('Nepravilna vrednost strani') + if (!(inputValue > 0 && inputValue <= numOfAllPages)) { + alert(i18next.t('Nepravilna vrednost strani')) pageInputEl.value = currentPage return } @@ -708,20 +740,12 @@ function initPagination(paginationRootElId, onPageChange, currentPage = 1) { function enableLock() { reqLock = true - btnFirstPage.disabled = true - btnPreviousPage.disabled = true - btnNextPage.disabled = true - btnLastPage.disabled = true - pageInputEl.disabled = true + allControlEls.forEach(el => (el.disabled = true)) } function disableLock() { reqLock = false - btnFirstPage.disabled = false - btnPreviousPage.disabled = false - btnNextPage.disabled = false - btnLastPage.disabled = false - pageInputEl.disabled = false + allControlEls.forEach(el => (el.disabled = false)) } function updatePagerUi(newCurrentPage, newNumOfAllPages) { @@ -729,23 +753,20 @@ function initPagination(paginationRootElId, onPageChange, currentPage = 1) { if (!newCurrentPage) return currentPage = newCurrentPage - pageInputEl.value = newCurrentPage - pagesCountDisplayEl.textContent = newNumOfAllPages + numOfAllPages = newNumOfAllPages + pageInputEls.forEach(el => (el.value = newCurrentPage)) + pagesCountDisplayEls.forEach(el => (el.textContent = newNumOfAllPages)) if (newCurrentPage === 1) { - btnFirstPage.disabled = true - btnPreviousPage.disabled = true + backwardBtnEls.forEach(el => (el.disabled = true)) } else { - btnFirstPage.disabled = false - btnPreviousPage.disabled = false + backwardBtnEls.forEach(el => (el.disabled = false)) } if (newCurrentPage === newNumOfAllPages) { - btnNextPage.disabled = true - btnLastPage.disabled = true + forwardBtnEls.forEach(el => (el.disabled = true)) } else { - btnNextPage.disabled = false - btnLastPage.disabled = false + forwardBtnEls.forEach(el => (el.disabled = false)) } } @@ -923,11 +944,11 @@ if (registerSwithcButton) { function initSelect2(querySelector, placeholder) { $(querySelector).select2({ - allowClear: true, + // allowClear: true, placeholder: placeholder }) - $(querySelector).val(null).trigger('change') + // $(querySelector).val(null).trigger('change') } $(document).ready(function () { @@ -935,7 +956,7 @@ if (registerSwithcButton) { $('.select-search-field').select2({}) initSelect2('.select-domain-field', 'Področje') - initSelect2('.select-src-lang-field', 'Izvorni jezik') + initSelect2('.select-src-lang-field', 'Jezik iskanja') initSelect2('.select-dest-lang-field', 'Ciljni jezik') initSelect2('.select-dict-field', 'Slovar') initSelect2('.select-source-field', 'Vir') @@ -978,7 +999,7 @@ if (registerSwithcButton) { $('.select-search-field').select2({}) initSelect2('.select-domain-field', 'Področje') - initSelect2('.select-src-lang-field', 'Izvorni jezik') + initSelect2('.select-src-lang-field', 'Jezik iskanja') initSelect2('.select-dest-lang-field', 'Ciljni jezik') initSelect2('.select-dict-field', 'Slovar') initSelect2('.select-source-field', 'Vir') @@ -1097,7 +1118,7 @@ function transferText(sideMenuText, removeOptionalBreak = false) { const optionalBreak = document.getElementById('disposable-break') - if (document.body.clientWidth <= 1200) { + if (window.innerWidth < 1200) { navTitle.textContent = siteHeadingTextContent siteHeading.style.display = 'none' if (removeOptionalBreak) { @@ -1111,3 +1132,140 @@ function transferText(sideMenuText, removeOptionalBreak = false) { } } } + +function replaceContainer(id, HTMLContent) { + const el = document.getElementById(id) + if (el) { + el.innerHTML = HTMLContent + } +} + +// STATE MANAGER FOR WINDOWS IN FORGOT PASSWORD +class StateManager { + constructor() { + this.state = {} + } + + setState(newState) { + this.state = { ...this.state, ...newState } + } + + getState() { + return this.state + } +} + +class StateManagerInvoker { + constructor(stateManager, invokeFunction) { + this.stateManager = stateManager + this.invokeFn = invokeFunction + } + + setState(newState) { + this.stateManager.setState(newState) + this.invoke() + } + + invoke() { + this.invokeFn() + } +} + +const stateManager = new StateManager() +const forgottenPasswordInvoker = new StateManagerInvoker(stateManager, () => { + function closeAllFPRelatedModals() { + $('#staticBackdrop').modal('hide') + $('#reset-pass-modal').modal('hide') + $('#reset-pass-info').modal('hide') + } + + function showLoginModal() { + $('#staticBackdrop').modal('show') + } + + function showResetPasswordModal() { + $('#reset-pass-modal').modal('show') + } + + function showResetPasswordModalSuccessOrFail() { + $('#reset-pass-info').modal('show') + } + + function apllyDescriptionAccordingToState() { + $('#alert-text').text(stateManager.getState().fpassWindowDescription) + } + + closeAllFPRelatedModals() + switch (stateManager.getState().fpassWindowState) { + case ForgotPasswordState.LOGIN_SIGNUP: + showLoginModal() + break + case ForgotPasswordState.FORGOT_PASSWORD_MODAL: + showResetPasswordModal() + break + case ForgotPasswordState.FORGOT_PASSWORD_ERROR: + case ForgotPasswordState.FORGOT_PASSWORD_SUCCESS: + showResetPasswordModalSuccessOrFail() + apllyDescriptionAccordingToState() + break + default: + console.log('Unknown state') + } +}) + +const ForgotPasswordState = { + LOGIN_SIGNUP: 1, + FORGOT_PASSWORD_MODAL: 2, + FORGOT_PASSWORD_ERROR: 3, + FORGOT_PASSWORD_SUCCESS: 4 +} + +function onForgotPassword() { + forgottenPasswordInvoker.setState({ + fpassWindowState: ForgotPasswordState.FORGOT_PASSWORD_MODAL + }) +} + +function onCancelForgotPassword() { + forgottenPasswordInvoker.setState({ + fpassWindowState: ForgotPasswordState.LOGIN_SIGNUP + }) +} + +function onSendForgottenEmailRequest() { + axios + .post('/REPLACETHISDUMMYURL', { + usernameOrEmail: document.getElementById('forgot-pass-input').value + }) + .then(res => { + // const EMAIL = 'DUMMY_EMAIL' + + // State manager because we don't want to invoker twice + // it is required to update description first before updating state of the invoker + stateManager.setState({ + fpassWindowDescription: i18next.t( + 'Na vaš elektronski naslov smo vam posredovali povezavo za ponastavitev gesla. Prosimo preverite svoj elektronski predal.' + ) + }) + + forgottenPasswordInvoker.setState({ + fpassWindowState: ForgotPasswordState.FORGOT_PASSWORD_SUCCESS + }) + }) + .catch(err => { + 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 + }) + console.log(err) + }) +} + +$('#modal-fp-cancel-btn').on('click', onCancelForgotPassword) +$('#modal-fp-use-btn').on('click', onSendForgottenEmailRequest) +$('#modal-fp-info-close').on('click', () => $('#reset-pass-info').modal('hide')) +$('#forgotten-password').on('click', onForgotPassword) diff --git a/express/public/javascripts/search-dictionary.js b/express/public/javascripts/search-dictionary.js index a717be4..c74965a 100644 --- a/express/public/javascripts/search-dictionary.js +++ b/express/public/javascripts/search-dictionary.js @@ -1,4 +1,5 @@ -/* global $, axios, initPagination, removeAllChildNodes, closeUtilityContainers, prepareQueryArrayForArrayWithIDs, getDataFromURLSearchParams, prepareQueryParams, focusOnCompletePrompt */ +/* global $, axios, initPagination, removeAllChildNodes, closeUtilityContainers, prepareQueryArrayForArrayWithIDs, +getDataFromURLSearchParams, prepareQueryParams, focusOnCompletePrompt, i18next */ { /* @@ -117,11 +118,11 @@ return page } catch (error) { - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response?.data) { message = error.response.data } else if (error.request) { - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } alert(message) updatePager() diff --git a/express/public/javascripts/search-results.js b/express/public/javascripts/search-results.js index 14a7d72..c8c9346 100644 --- a/express/public/javascripts/search-results.js +++ b/express/public/javascripts/search-results.js @@ -1,10 +1,15 @@ -/* global $, axios, currentPagePath, initPagination, removeAllChildNodes, transferText, tooltipTriggerList, tooltipList, reinitalizeDefaultTooltipSet, createTooltip */ +/* global $, axios, currentPagePath, initPagination, + removeAllChildNodes, transferText, tooltipTriggerList, + tooltipList, createTooltip, resetTooltipTriggerList, + transferTextExtended, i18next */ // position correction functions function adjustOffsetBy() { // const { offsetMain, fixedTopSection } = window.dictionaryElements + const BROWSER_UNUSUAL_OFFSET = 17 // computer from chrome + const offsetMain = document.querySelector('#offset-main') const fixedTopSection = document.querySelector('#fixed-top-section') @@ -14,7 +19,7 @@ function adjustOffsetBy() { // const adminNavMobile = document.getElementsByClassName('admin-nav') const headerPadding = document.getElementById('header-padding') - if (document.body.clientWidth < 1200) { + if (document.body.clientWidth + BROWSER_UNUSUAL_OFFSET < 1200) { if (offsetHeaderPadding !== null) offsetMain.style.paddingTop = `0px` } else { for (let i = 0; i < offsetHeader.length; i++) { @@ -80,8 +85,14 @@ function mobileMoveContent() { '.header-container-divider-right' ) + // let actualInnerWidth = $("body").prop("clientWidth"); // El. width minus scrollbar width + // let actualInnerWidth = $("body").prop("scrollWidth"); // El. width minus scrollbar width + + // const BROWSER_UNUSUAL_OFFSET = 17 // computer from chrome + + // console.log($('body').innerWidth() + BROWSER_UNUSUAL_OFFSET) try { - if (document.body.clientWidth <= 1200) { + if (window.innerWidth < 1200) { if (secondaryButton) { mobileRightHolder.appendChild(secondaryButton) // secondaryButton.style.height = '28px' @@ -98,9 +109,11 @@ function mobileMoveContent() { primaryButton.style.whiteSpace = 'nowrap' } navTitle.textContent = siteHeadingTextContent - siteHeading.style.display = 'none' + // siteHeading.style.display = 'none' + + siteHeading.style.display = 'inline' // I know this is hacky, but sadly due to design this is mandatory in order to fix the bug } - if (document.body.clientWidth > 1200) { + if (window.innerWidth >= 1200) { if (secondaryButton) { headerContainerRight.appendChild(secondaryButton) secondaryButton.style.height = '' @@ -117,7 +130,7 @@ function mobileMoveContent() { primaryButton.style.whiteSpace = '' } - navTitle.textContent = 'Urejanje' + navTitle.textContent = i18next.t('Urejanje') siteHeading.style.display = 'block' } } catch (e) {} @@ -128,7 +141,11 @@ if (resultsListEl) { resultsListEl.addEventListener('click', onResultClick) } const initialPage = +new URL(location).searchParams.get('p') || 1 -const updatePager = initPagination('pagination', onPageChange, initialPage) +const updatePager = initPagination( + ['pagination-top', 'pagination-bottom'], + onPageChange, + initialPage +) async function onPageChange(newPage) { const receivedPageNumber = await changePage(newPage) @@ -146,7 +163,16 @@ async function changePage(newPage) { removeAllChildNodes(resultsListEl) renderResults(resultsMarkup) updatePager(page, numberOfAllPages) - reinitalizeDefaultTooltipSet() + // reinitalizeDefaultTooltipSet() + // console.log($('[data-toggle="tooltip"]')) + // $('[data-toggle="tooltip"]').tooltip() + + // toltip reinitialization after moving a page + resetTooltipTriggerList() + if (tooltipTriggerList.length > 0) { + // initialize tooltips + tooltipList(null, 'dark-gray-tooltip') // ADD for tooltip debug in the end -> [0].show() + } /* try { @@ -160,12 +186,12 @@ async function changePage(newPage) { return page } catch (error) { // console.log(error) - let message = 'Prišlo je do napake.' + let message = i18next.t('Prišlo je do napake.') if (error.response?.data) { message = error.response.data } else if (error.request) { return // return to bypass error caused (assumed) by popstate on iOS/MacOS - message = 'Strežnik ni dosegljiv. Poskusite kasneje.' + message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.') } alert(message) updatePager() @@ -210,119 +236,10 @@ function onResultClick(e) { try { if (tooltipTriggerList.length > 0) { // initialize tooltips - tooltipList(null, 'gray-tooltip') // ADD for tooltip debug in the end -> [0].show() + tooltipList(null, 'dark-gray-tooltip') // ADD for tooltip debug in the end -> [0].show() } } catch (e) {} -/* - // refresh: bool - function largeStringsOnSmallScreen(alwaysRefresh) { - let headwordTexts - let translationWords - let synonymWords - function init() { - headwordTexts = [...$('.rihw')] - translationWords = [...$('.term-h')].map(e => { - return e.children[0] - }) - synonymWords = [...$('.syn-h'), ...$('.risy')] - } - - function setupToolTips() { - setDynamicTooltipList([]) - if (headwordTexts) { - headwordTexts.map(ttElement => - createTooltip(ttElement, ttElement.innerHTML, '') - ) - // setDynamicTooltipList([...dynamicTooltipList, ...tt]) - } - if (translationWords) { - translationWords.map(ttElement => - createTooltip(ttElement, ttElement.innerHTML, '') - ) - // setDynamicTooltipList([...dynamicTooltipList, ...tt]) - } - if (synonymWords) { - synonymWords.map(ttElement => - createTooltip(ttElement, ttElement.innerHTML, '') - ) - // setDynamicTooltipList([...dynamicTooltipList, ...tt]) - } - // console.log(dynamicTooltipList) - } - - if (!headwordTexts || !translationWords || !synonymWords || alwaysRefresh) { - init() - } - - function initTooltips() { - try { - // if (dynamicTooltipList.length || alwaysRefresh) { - setupToolTips() - // } - } catch (e) {} - } - - function removeTooltips() { - try { - $('.rihw').tooltip('dispose') - $('.term-h').children().tooltip('dispose') - $('.syn-h').tooltip('dispose') - $('.risy').tooltip('dispose') - } catch (e) {} - } - - function manageResize() { - const tmpTerms = [...$('.term-h')].map(e => { - return e.children[0] - }) - const elements = [...tmpTerms, ...$('.syn-h')] - // console.log(document.documentElement.clientWidth) - - const dcw = document.documentElement.clientWidth - const checkBorders = (dcw > 991 && dcw < 1600) || dcw < 370 - if (checkBorders) { - const TEXT_LIMIT = 17 - elements.forEach(el => { - el.dataset.textStore = el.innerHTML.includes('...') - ? el.dataset.textStore - : el.innerHTML - el.innerHTML = `${el.innerHTML.slice(0, TEXT_LIMIT)}${ - el.innerHTML.length > TEXT_LIMIT ? '...' : '' - }` - }) - - initTooltips() - } else { - elements.forEach(el => { - if (el.dataset.textStore) { - el.innerHTML = el.dataset.textStore - } - }) - removeTooltips() - } - } - - return { - init: function () { - init() - }, - refresh: function () { - init() - try { - setupToolTips() - } catch (e) {} - }, - manageResize: manageResize - } - } - const textsScreenManager = largeStringsOnSmallScreen(true) - - textsScreenManager.manageResize() - - window.onresize = textsScreenManager.manageResize - */ - // refresh: bool function largeStringsOnSmallScreen(alwaysRefresh) { let headwordTexts @@ -336,30 +253,6 @@ function largeStringsOnSmallScreen(alwaysRefresh) { synonymWords = [...$('.syn-h'), ...$('.risy')] } - /* - function setupToolTips() { - setDynamicTooltipList([]) - if (headwordTexts) { - headwordTexts.map(ttElement => - createTooltip(ttElement, ttElement.innerHTML, '') - ) - // setDynamicTooltipList([...dynamicTooltipList, ...tt]) - } - if (translationWords) { - translationWords.map(ttElement => - createTooltip(ttElement, ttElement.innerHTML, '') - ) - // setDynamicTooltipList([...dynamicTooltipList, ...tt]) - } - if (synonymWords) { - synonymWords.map(ttElement => - createTooltip(ttElement, ttElement.innerHTML, '') - ) - // setDynamicTooltipList([...dynamicTooltipList, ...tt]) - } - // console.log(dynamicTooltipList) - } */ - if (!headwordTexts || !translationWords || !synonymWords || alwaysRefresh) { init() } @@ -379,169 +272,20 @@ function largeStringsOnSmallScreen(alwaysRefresh) { ttElement.tooltip('dispose') } catch (e) {} } - - /* - function initTooltips() { - try { - // if (dynamicTooltipList.length || alwaysRefresh) { - setupToolTips() - // } - } catch (e) {} - } - - function removeTooltips() { - try { - $('.rihw').tooltip('dispose') - $('.term-h').children().tooltip('dispose') - $('.syn-h').tooltip('dispose') - $('.risy').tooltip('dispose') - } catch (e) {} - } - */ - - /* let OLD_TEXT_LIMIT = 5000 // Guard to not prevent overdisposing - function manageResize() { - const tmpTerms = [...$('.term-h')].map(e => { - return e.children[0] - }) - const elements = [...tmpTerms, ...$('.syn-h')] - // console.log(document.documentElement.clientWidth) */ - - /* - $('.word-constraint').each((i, element) => { - // WRONG const wordBr = $(`.word-constraint:nth-child(${i}) > .word-breakable`) - const wordList = $(element).find('.word-breakable') - - wordList.each((i, wordElement) => { - // console.log(wordElement.innerText) - - if ( - wordElement.clientWidth > element.clientWidth && - wordElement.innerText.split(' ').length <= 1 - ) { - console.log('Lion') - createTooltip($(wordElement)) - wordElement.classList.push('text-ellipsis') - } else { - removeTooltip($(wordElement)) - // console.log(wordElement.classList) - wordElement.className = Array.from(wordElement.classList) - .filter(e => e !== 'text-ellipsis') - .join(' ') - // console.log(wordElement.classList) - } - }) - // console.log(wordBr) - }) - */ - - /* const dcw = document.documentElement.clientWidth - // const checkBorders = (dcw > 420 && dcw < 750) || dcw < 370 - - let borderIndex - - // check _common.scc long-text-strip and make sure the widths are aligned - if (dcw < 420) { - borderIndex = 0 // width = 8 chars (<420) - } else if (dcw < 750 || (dcw > 990 && dcw < 1750)) { - borderIndex = 1 // width = 12 chars (420 - 750, 990-1750) - } else if (dcw > 1750) { - borderIndex = 3 // width = 100% (>1759) - } else { - borderIndex = 2 // width = 25 chars (750-990) - } - - let TEXT_LIMIT - if (borderIndex === 0) { - TEXT_LIMIT = 8 - } else if (borderIndex === 1) { - TEXT_LIMIT = 12 - } else if (borderIndex === 2) { - TEXT_LIMIT = 25 - } else { - TEXT_LIMIT = null - } - - if (OLD_TEXT_LIMIT !== TEXT_LIMIT) { - elements.forEach(el => { - // console.log(el.innerHTML) - if (TEXT_LIMIT && el.innerHTML.length > TEXT_LIMIT) { - console.log(`DEBUG TEXT LIMIT: ${TEXT_LIMIT}`) - console.log(`DEBUG EL LEN: ${el.innerHTML.length}`) - console.log(`DEBUG EL TEXT: ${el.innerHTML}`) - console.log('TOOLTIP CREATED') - createTooltipEl(el, 'gray-tooltip') - } else { - removeTooltip($(el)) - } - }) - } - - OLD_TEXT_LIMIT = TEXT_LIMIT */ - - /* - if (checkBorders) { - console.log('HEYOO') - const TEXT_LIMIT = 17 - elements.forEach(el => { - el.dataset.textStore = el.innerHTML.includes('...') - ? el.dataset.textStore - : el.innerHTML - el.innerHTML = `${el.innerHTML.slice(0, TEXT_LIMIT)}${ - el.innerHTML.length > TEXT_LIMIT ? '...' : '' - }` - }) - - initTooltips() - } else { - elements.forEach(el => { - if (el.dataset.textStore) { - el.innerHTML = el.dataset.textStore - } - }) - removeTooltips() - } - - */ - /* } - - return { - init: function () { - init() - }, - refresh: function () { - init() - }, - manageResize: manageResize - } */ } -// const textsScreenManager = largeStringsOnSmallScreen(true) - -// textsScreenManager.manageResize() - -// window.onresize = textsScreenManager.manageResize - -/* -function transferText(sideMenuText) { - const siteHeading = document.getElementById('site-heading') - const siteHeadingTextContent = siteHeading.textContent - const navTitle = document.getElementById('nav-title') - - if (document.body.clientWidth <= 1200) { - navTitle.textContent = siteHeadingTextContent - siteHeading.style.display = 'none' - } else { - navTitle.textContent = sideMenuText - } -} -*/ +window.addEventListener('load', () => { + $('[data-toggle="tooltip"]').tooltip() +}) function handleProperTextDisplay() { + // const BROWSER_UNUSUAL_OFFSET = 17 if (/\/iskanje/.test(currentPagePath)) { - transferText('Iskanje po slovarjih', true) + transferText('Iskanje po slovarjih', true, 'site-heading') //, + // BROWSER_UNUSUAL_OFFSET + // ) } else if (/\/termin/.test(currentPagePath)) { - transferText('', true) + transferText('', true, 'site-heading') // , BROWSER_UNUSUAL_OFFSET) } } diff --git a/express/public/javascripts/select.js b/express/public/javascripts/select.js index ad131c6..e8f1108 100644 --- a/express/public/javascripts/select.js +++ b/express/public/javascripts/select.js @@ -1,4 +1,4 @@ -/* global $, currentPagePath */ +/* global $, currentPagePath, i18next */ // const currentPagePath = location.pathname @@ -60,13 +60,13 @@ function initSelect() { const userPassword2 = ce.userConfirmationPassEl.value if (userPassword1 === '') { - alert('Prosim, vnesite geslo') + alert(i18next.t('Prosim, vnesite geslo')) } else if (userPassword2 === '') { - alert('Prosim, ponovno vnesite geslo') + alert(i18next.t('Prosim, ponovno vnesite geslo')) } else if (userPassword1 !== userPassword2) { - alert('Gesli se ne ujemata') + alert(i18next.t('Gesli se ne ujemata')) } else { - alert('Gesli se ujemata!') + alert(i18next.t('Gesli se ujemata!')) } } } @@ -115,7 +115,7 @@ function deleteField(ele) { // Summernote $('.summernote').summernote({ - placeholder: 'Na kratko opišite zasnovo in namen slovarja.', + placeholder: i18next.t('Na kratko opišite zasnovo in namen slovarja.'), height: 300, minheight: 150, toolbar: [ diff --git a/express/public/javascripts/side-menu-filter-languages.js b/express/public/javascripts/side-menu-filter-languages.js index e01e756..650f96f 100644 --- a/express/public/javascripts/side-menu-filter-languages.js +++ b/express/public/javascripts/side-menu-filter-languages.js @@ -1,4 +1,4 @@ -/* global axios */ +/* global axios, i18next */ /* TODO @@ -8,6 +8,8 @@ not just language filter, but filter for many properties... /** BEGIN VARIABLE CONSTRUCTION AREA */ +let allAggregationDataForSF = null + // TODO insert all sections of ids const ids = [ 'src-lang-side', @@ -176,6 +178,10 @@ function cleanOldList() { modalListRoot.innerHTML = '' } +function setTitleOnModal(title) { + document.getElementById('modal-filter-label').innerHTML = title +} + async function selectMoreListener(event) { const idstring = `${event.currentTarget.id .replace('select-', '') @@ -183,12 +189,17 @@ async function selectMoreListener(event) { .concat('-side')}` // console.log(extractMap(idstring)) + const titleText = + event.currentTarget.parentElement.parentElement.children[0].textContent + + setTitleOnModal(titleText) + // mp is the array of the currently displayed filters (required to get the checked information) // const mp = initializedValues[idstring] // all filters for search // const allAggregationData = { id: idstring } - const allAggregationData = { id: idstring } + allAggregationDataForSF = { id: idstring } const currentURL = new URL(window.location.href) const searchParams = currentURL.searchParams @@ -200,10 +211,10 @@ async function selectMoreListener(event) { // const parsed = JSON.parse(event.currentTarget.dataset.aggregationInfo ?? '[]') - allAggregationData.items = data[idToSearchFiltersMapper(idstring)] + allAggregationDataForSF.items = data[idToSearchFiltersMapper(idstring)] cleanOldList() - initModalList(allAggregationData.id, allAggregationData.items) + initModalList(allAggregationDataForSF.id, allAggregationDataForSF.items) } function idToSearchFiltersMapper(inpt) { @@ -297,6 +308,18 @@ function finishSelectingFromModal(e) { window.location.href = url } +function finishSelectingFromModal2(e) { + const url = new URL(window.location.href) + const searchParams = url.searchParams + searchParams.delete(queryOrderedArr[sectionIDBattery]) + + selectedIds[sectionIDBattery].forEach(e => { + searchParams.append(queryOrderedArr[sectionIDBattery], e) + }) + + window.location.href = url +} + function reRenderSpecific(event) { const idstring = event.currentTarget.id const id = idstring.replace('show-all-', '') @@ -349,7 +372,7 @@ function rePlotAndCheck(sectionId, selectedIdsArray, plotInverse = false) { const img = document.createElement('img') img.setAttribute('src', '/images/chevron-left-blue.svg') const span = document.createElement('span') - span.innerHTML = 'PRIKAŽI VSE' + span.innerHTML = i18next.t('PRIKAŽI VSE') anchr.appendChild(img) anchr.appendChild(span) @@ -378,6 +401,7 @@ function clearModalFilters() { e.children[0].children[0].checked = false }) selectedIds[sectionIDBattery] = [] + clearSnapshots() } function clearModalFiltersWID(id) { @@ -402,7 +426,7 @@ function initializeClickables() { .addEventListener('click', selectFromModal) document .querySelector('#sf-modal-button') - .addEventListener('click', finishSelectingFromModal) + .addEventListener('click', finishSelectingFromModal2) document.querySelector('#ccf').addEventListener('click', clearModalFilters) @@ -428,6 +452,40 @@ function initializeClickables() { // initModalList('id1', langsMapModal) +function filterModalEntries(event) { + cleanOldList() + + // console.log(allAggregationDataForSF.items) + const prompt = document.getElementById('filterSfmText') + + let filtered + if (prompt) { + filtered = allAggregationDataForSF.items.filter(item => + item.name.toLowerCase().includes(prompt.value.toLowerCase()) + ) + } else { + return + } + + initModalList(allAggregationDataForSF.id, filtered) +} + +const sfmFilterButton = document.getElementById('filter-sfm') + +const sfmOnEnterListener = document.querySelector('.onEnterListener') + +if (sfmFilterButton) { + sfmFilterButton.addEventListener('click', filterModalEntries) +} + +if (sfmOnEnterListener) { + sfmOnEnterListener.addEventListener('keyup', event => { + if (event.key === 'Enter') { + filterModalEntries(event) + } + }) +} + /// global run initializeClickables() diff --git a/express/public/javascripts/tooltip-helper.js b/express/public/javascripts/tooltip-helper.js index f17f1cd..9873e34 100644 --- a/express/public/javascripts/tooltip-helper.js +++ b/express/public/javascripts/tooltip-helper.js @@ -70,20 +70,23 @@ function generateTooltipWithStyles( ) { let nonEmptyContent if (!content) { - nonEmptyContent = el => { - el.getAttribute('data-tooltip-content') - } + return generateTooltipBase(list, function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl, { + html: true, + title: tooltipTriggerEl.getAttribute('data-tooltip-content') || '', + customClass: styles + }) + }) } else { nonEmptyContent = content - } - - return generateTooltipBase(list, function (tooltipTriggerEl) { - return new bootstrap.Tooltip(tooltipTriggerEl, { - html: true, - title: nonEmptyContent, - customClass: styles + return generateTooltipBase(list, function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl, { + html: true, + title: nonEmptyContent || '', + customClass: styles + }) }) - }) + } } function generateTooltipBase(list, fn) { diff --git a/express/public/javascripts/utils.js b/express/public/javascripts/utils.js index d9b0e8f..434e288 100644 --- a/express/public/javascripts/utils.js +++ b/express/public/javascripts/utils.js @@ -1,4 +1,4 @@ -/* global axios */ +/* global axios, i18next */ function autoGrow(element) { element.style.height = '5px' @@ -156,14 +156,14 @@ function editRowBodyForConsultancy(fieldInfo) { fieldInfo.newButtonGroup = newButtonGroup fieldInfo.cancelButton = cancelButton fieldInfo.saveButton = saveButton - cancelButton.textContent = 'Prekliči' + cancelButton.textContent = i18next.t('Prekliči') cancelButton.className = 'btn btn-secondary me-2' cancelButton.style.height = '33px' cancelButton.style.width = '105px' cancelButton.addEventListener('click', () => abortConsultancyUserEditing(fieldInfo) ) - saveButton.textContent = 'POTRDI' + saveButton.textContent = i18next.t('POTRDI') saveButton.className = 'btn btn-primary' saveButton.style.height = '33px' saveButton.style.width = '105px' diff --git a/express/public/locales/dev/core.json b/express/public/locales/dev/core.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/express/public/locales/dev/core.json @@ -0,0 +1 @@ +{} diff --git a/express/public/locales/dev/extended.json b/express/public/locales/dev/extended.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/express/public/locales/dev/extended.json @@ -0,0 +1 @@ +{} diff --git a/express/public/locales/en/core.json b/express/public/locales/en/core.json new file mode 100644 index 0000000..4294fc4 --- /dev/null +++ b/express/public/locales/en/core.json @@ -0,0 +1,710 @@ +{ + " Terminološke odgovore pripravljajo sodelavci Terminološke sekcije Inštituta za slovenski jezik Frana Ramovša ZRC SAZU (in so objavljeni tudi na spletišču Terminologišče), ki pri delu upoštevajo osnovna terminološka načela.": " Answers to terminology questions are prepared by the employees of the Fran Ramovš Institute of the Slovenian Language at the Research Centre of the Slovenian Academy of Sciences and Arts (the answers are also published on their Terminology website). When addressing the questions, the consultants take into account the basic terminology principles.", + "(STOP) Termini": "(STOP) Terms", + "[ni termina]": "[no term]", + "#": "#", + "Odgovor:": "Answer:", + "Administracija": "Administration", + "Administrator": "Administrator", + "Administratorska konzola": "Admin console", + "Administratorski del portala je namenjen skupini oseb, ki vsebinsko in tehnično ureja portal, odpira slovarske vire, daje pooblastila posameznim uporabnikom in skrbi za vsebinsko in tehnično urejenost portala. Izberite module, ki jih boste ponudili na terminološkem portalu in na kratko opišite, kaj ponuja vaš portal.": "The administrative functions of the portal is intended for the group of people who edit the content and the technical aspects of the portal, publish dictionaries, give authorizations to other individual users and are responsible for the content and the technical aspects of the portal.", + "Aktivacija računa": "Account activation", + "Ali res želite izbrisati vnos? Dejanja ni mogoče razveljaviti.": "Are you sure you want to delete the entry? This action is cannot be undone.", + "Ali želite indeksirati?": "Do you want to index?", + "Ali želite izbrisati slovarski sestavek?": "Do you want to delete the dictionary entry?", + "Ali želite objaviti vsa gesla?": "Do you want to publish all entries?", + "Ali želite zbrisati ta profil. S tem bodo izbrisani vsi podatki, ki ste jih ustvarili": "Do you want to delete this profile? This action will also delete all data you created.", + "Ali želite zbrisati ta vnos?": "Do you want to delete this entry?", + "angleščina": "English", + "ANGLEŠKI NASLOV SLOVARJA *": "DICTIONARY TITLE IN ENGLISH *", + "ANGLEŠKI OPIS PORTALA": "PORTAL DESCRIPTION IN ENGLISH", + "ANGLEŠKO IME PORTALA": "PORTAL NAME IN ENGLISH", + "Angleško ime terminološkega portala.": "English name of the terminology portal.", + "Avtor": "Author", + "AVTOR": "AUTHOR", + "AVTOR SLOVARJA": "DICTIONARY AUTHOR", + "Avtor:": "Author:", + "Avtorja": "Authors", + "Avtorji": "Authors", + "Avtorji mnenja:": "Opinion authors:", + "Basic": "Basic", + "Besedila": "Texts", + "Besedilo gre tukaj": "The text goes here", + "Brisanje je onemogočeno, doker se nalagajo nove datoteke.": "Deletion is disabled while new files are being uploaded.", + "Brisanje je onemogočeno, saj je eno še v procesu.": "Deletion is disabled because previous deletion has not yet been completed.", + "Brisanje slovarja": "Delete the dictionary", + "Brisanje slovarskih sestavkov": "Delete dictionary entries", + "Briši": "Delete", + "BRIŠI": "DELETE", + "Briši uporabnika": "Delete user", + "Če imate pripravljen slovar v enem od formatov, ki so navedeni spodaj, lahko svoje podatke uvozite.": "If you have a dictionary in one of the formats listed below, you can import your data.", + "Če izberete to možnost, se bodo vsi doslejšnji slovarski sestavki ob uvozu nove datoteke izbrisali.": "If you select this option, all previous dictionary entries will be deleted when the new file is imported.", + "Če želite dodati novo luščenje, morate najprej pobrisati vsaj eno od obstoječih, saj je na posameznega uporabnika dovoljenih največ 5 luščenj.": "If you want to add a new extraction, you must first delete at least one of your current extraction, as a maximum of 5 extractions are allowed per user.", + "Celotni naslov slovarja v angleščini.": "Full title of the dictionary in English.", + "Celotni naslov slovarja, ki bo zabeležen v bibliografskih podatkih.": "The full dictionary title to be recorded in the bibliographic information.", + "Ciljni jeziki": "Target languages", + "Čim bolj natančno opišite terminološki problem, zlasti opišite vsebino pojma.": "Describe the terminological problem as precisely as you can, especially the concept the term should represent.", + "DATOTEKA": "FILE", + "Datoteka uspešno naložena": "File uploaded successfully", + "DATUM": "DATE", + "Datum objave/spremembe": "Date of publication/modification", + "Datum vprašanja:": "Question date:", + "Definicija": "Definition", + "DEFINICIJA": "DEFINITION", + "DEFINICIJA:": "DEFINITION:", + "Delete profile": "Delete profile", + "Deli": "Share", + "DELI": "SHARE", + "dni.": "days.", + "do številke": "and", + "Dodaj": "Add", + "DODAJ": "ADD", + "Dodaj besedilo za luščenje terminoloških kandidatov iz lastnega specializiranega korpusa. Ko boste dodali vsa besedila, ki ste jih izbrali, morate izbiro shraniti.": "Add text to extract term candidates from your own specialized corpus. After adding all selected texts, save your selection.", + "Dodaj datoteko": "Add file", + "Dodaj komentar...": "Add a comment...", + "Dodaj odgovor...": "Add a reply...", + "Dodaj opravilo": "Add a task", + "Dodaj povezani termin.": "Add a related term.", + "Dodaj povezavo": "Add link", + "Dodaj slovar": "Add a dictionary", + "Dodajte ime in priimek naslednjega avtorja slovarja.": "Add first and last name of the next dictionary author.", + "Dodajte ime in priimek naslednjega avtorja slovarja..": "Add first and last name of the next dictionary author..", + "Dodajte leto izida besedil, ki jih želite luščiti. Lahko dodate več posameznih let. Če boste polje pustili prazno, bodo vključena vsa leta. Če bo besedil preveč, boste morali omejiti izbiro.": "Add the year of publication of the texts to be used for extraction. You can add several years. If the field is left empty, all years will be included. If this results in too many results, you will need to limit your selection.", + "Dodajte podatek o zunajjezikovnih okoliščinah, ki niso povezane s pojmom, npr. letnico.": "Add information about extra-linguistic circumstances unrelated to the concept, e.g. ", + "Dodajte povezavo do slike.": "Link an image.", + "Dodajte povezavo do videa.": "Link a video.", + "Dodajte povezavo do zvočnega posnetka.": "Link an audio file.", + "Dodajte termin, ki je sicer definiran v samostojnem slovarskem sestavku, vendar je povezan s terminom, ki ga opisujete v tem slovarskem sestavku.": "Add a term that is defined in a separate dictionary entry but is related to the term you are describing in this dictionary entry.", + "Dodajte terminološki odgovor in ga utemeljite.": "Add a terminological answer and justify it.", + "Dodajte tujejezične ustreznike, ki se za opisani pojem tudi uporabljajo v tujem jeziku.": "Add foreign language equivalents that are also used in a foreign language for the described concept.", + "Dodajte tujejezični ustreznik.": "Add a foreign language equivalent.", + "Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Več...": "You can add a list of words that you do not want on the list of term candidates. The list should be saved in a .txt format. Read more...", + "Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Na koncu morate spremembe shraniti. Več ...": "You can add a list of words that you do not want on the list of term candidates. The list should be saved in a .txt format. Save all changes after you are done. Read more...", + "Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Vse spremembe morate shraniti. Več...": "You can add a list of words that you do not want on the list of term candidates. The list should be saved in a .txt format. Read more...", + "Dodaten avtor.": "Additional author.", + "DODATNI POVEZANI TERMIN": "ADDITIONAL RELATED TERM", + "Dodeli": "Assign", + "DODELI": "ASSIGN", + "Določite lastnosti terminoloških virov na portalu, zlasti minimalno število sestavkov, ki so pogoj za objavo, možnosti potrjevanja objave novih slovarjev, število različic slovarja, ki jih lahko hrani posamezni uporabnik. Te nastavitve veljajo za vse terminološke vire na portalu.": "Define the characteristics of the terminology resources on the portal, specifically the minimal number of entries required to publish a dictionary, the options for approving the publication of new terminology dictionaries, as well as the number of dictionary versions and exports that can be saved by individual users. These settings apply to all terminology resources on the portal.", + "Domači slovarji": "Local dictionaries", + "Drugo": "Other", + "DRUGO": "OTHER", + "DRUGO:": "OTHER:", + "Dvočrkovna oznaka terminološkega portala.": "Two-letter label of the terminology portal.", + "E - naslov": "E-mail address", + "E-naslov": "E-mail address", + "E-NASLOV": "E-MAIL ADDRESS", + "E-pošta": "E-mail", + "ELEKTRONSKI NASLOV": "E-MAIL ADDRESS", + "Elektronski naslov trenutno prijavljenega uporabnika.": "E-mail address of the currently logged-in user.", + "Elektronski naslov že obstaja": "The e-mail address already exists", + "EVROPSKI SKLAD ZA REGIONALNI RAZVOJ": "EUROPEAN REGIONAL DEVELOPMENT FUND", + "FAZA UREJANJA": "EDITING PHASE", + "Faze urejanja": "Editing phases", + "Filtri": "Filters", + "Filtriranje": "Filter", + "FORMAT ZAPISA": "FILE FORMAT", + "Gesli se ne ujemata": "Passwords do not match", + "Gesli se ujemata!": "Passwords match!", + "Geslo": "Password", + "GESLO": "PASSWORD", + "Geslo je prekratko": "The password is too short", + "Geslo je prekratko.": "The password is too short.", + "Geslo se ne ujema": "Incorrect password", + "Geslo se ne ujema.": "Incorrect password.", + "Hrvaščina": "Croatian", + "Hvala za poslano vprašanje, ki je bilo posredovano svetovalcem terminološkega portala. Odgovor boste prejeli na e-naslov, ki ste ga navedli ob registraciji.": "Thank you for submitting the question, which was forwarded to the terminology portal consultants. The answer will be sent to the e-mail address you used for registration.", + "Hvala za poslano vprašanje, ki je bilo posredovano v Terminološko svetovalnico ZRC SAZU. Odgovor boste prejeli na e-naslov, ki ste ga navedli ob registraciji.": "Thank you for submitting the question, which was forwarded to the Terminological Counselling of the ZRC SAZU. The answer will be sent to the e-mail address you used for registration.", + "ID Slovarja": "Dictionary ID", + "ID: Ni idja": "ID: No ID", + "IDEKSIRAJ": "INDEX", + "Imate neshranjene spremebe. Ali jih želite shraniti?": "Your changes have not been saved. Do you want to save them?", + "Imate neshranjene spremembe. Ali jih želite shraniti?": "Your changes have not been saved. Do you want to save them?", + "Ime": "First name", + "IME": "FIRST NAME", + "IME DATOTEKE": "FILE NAME", + "Ime in opis": "Name and description", + "IME IN PRIIMEK": "FIST AND LAST NAME", + "Ime in priimek trenutno prijavljenega uporabnika.": "First and last name of the currently logged-in user.", + "IME LUŠČENJA": "EXTRACTION NAME", + "IME PORTALA": "PORTAL NAME", + "IME PORTALA *": "PORTAL NAME *", + "Ime Priimek": "First Name Last Name", + "Ime registriranega uporabnika.": "Name of rhe registered user.", + "Ime slovarja": "Dictionary name", + "Ime, s katerim se uporabnik predstavlja na terminološkem portalu.": "The name with which the user presents himself on the terminology portal.", + "Indeksiranje slovarja": "Index dictionary", + "Institucija": "Institution", + "INSTITUCIJA": "INSTITUTION", + "Institucija, kjer trenutno prijavljeni uporabnik deluje.": "The institution where the currently logged-in user works.", + "Interni": "Internal", + "Išči": "Search", + "Išči po slovarju": "Search the dictionary", + "ISKANI NIZ JE BIL NAJDEN": "THE SEARCH STRING WAS FOUND", + "Iskanje": "Search", + "Iskanje po slovarjih": "Search the dictionaries", + "Iskanje po vseh": "Search all", + "ISSN": "ISSN", + "ISSN OZNAKA": "ISSN CODE", + "ISSN oznaka.": "ISSN code.", + "Italijanščina": "Italian", + "IZBERI": "SELECT", + "Izberi datoteko": "Select a file", + "IZBERI VEČ": "SELECT MORE", + "Izberite enega od rezultatov luščenja s seznama.": "Select one of the extraction results from the list.", + "Izberite format datoteke, v kateri je slovar shranjen na vašem računalniku.": "Select the file format of the dictionary saved on your computer.", + "Izberite format izpisa terminološkega slovarja.": "Select the file format to export the terminology dictionary.", + "Izberite luščenje": "Select extraction", + "Izberite področje": "Select an domain", + "Izberite področje iz seznama področij, da zmanjšate obseg besedil, iz katerih bo potekalo luščenje.": "Select a domain from the the list of domains to limit the scope of texts used for extraction.", + "Izberite področje svojega terminološkega slovarja na seznamu področij.": "Select the domain of your dictionary from the list of domains.", + "Izberite slovarske podatke, ki ste jih shranili na svojem računalniku.": "Select the dictionary data you saved on your computer.", + "Izberite takšno ime, ki vam bo pomagalo slediti rezultatom, če boste luščenje besedil opravili večkrat.": "Choose a name that will help you keep track of the results if you decide to run several extractions.", + "Izberite vrsto dokumenta iz seznama, npr. članek, diplomsko delo.": "Select the document type from the list, e.g. article, graduate thesis.", + "Izberite vse tuje jezike, ki jih bo terminološki slovar vseboval.": "Select all foreign languages ​​you want to include into the terminology dictionary.", + "Izberite, ali želite izpisati samo jezikovno pregledane slovarske sestavke.": "Choose whether you want to list only linguistically reviewed dictionary entries.", + "Izberite, ali želite izpisati samo objavljene ali tudi neobjavljene slovarske sestavke.": "Choose whether you want to list only published or unpublished dictionary entries.", + "Izberite, ali želite izpisati samo strokovno pregledane slovarske sestavke.": "Choose whether you want to list only terminologically reviewed dictionary entries.", + "Izberite, ali želite izpisati vse slovarske sestavke ali samo tiste, ki so v določeni fazi urejanja.": "Choose whether you want to list all dictionary entries or only those in a certain editing phase.", + "Izberite, katere slovarske sestavke želite izpisati glede na njihovo veljavnost.": "Choose which dictionary entries you want to list based on their validity.", + "Izbrišete lahko celoten slovar z vsemi metapodatki. Dejanja ni mogoče preklicati.": "You can delete the entire dictionary with all metadata. The action is irreversible.", + "Izbrišete lahko vse doslej obdelane slovarske sestavke in ohranite vse metapodatke o slovarju. Dejanja ni mogoče preklicati.": "You can delete all dictionary entries you have edited until now and keep all dictionary metadata. The action is irreversible.", + "Izbriši": "Delete", + "IZBRIŠI": "DELETE", + "Izbriši obstoječe slovarske sestavke.": "Delete existing dictionary entries.", + "Izbriši polje": "Remove the field", + "Izbriši račun": "Delete account", + "Izgovor": "Pronunciation", + "Izpolnite polja in na kratko opišite vsebino terminološkega slovarja.": "Fill in the fields and briefly describe the contents of the terminology dictionary.", + "Izvoz": "Export", + "Izvozi": "Export", + "IZVOZI": "EXPORT", + "Izvozi termine od številke": "Export entries between the numbers", + "je že v tabeli.": "is already in the table.", + "Jeziki": "Languages", + "Jeziki iskanja": "Search languages", + "Jezikovni pregled": "Language review", + "Jezikovno pregledano": "Linguistically reviewed", + "KANONIČNA OBLIKA": "CANONICAL FORM", + "KLJUČNE BESEDE": "KEY WORDS", + "Ko bo luščenje uspešno zaključeno, boste na svoj elektronski naslov, ki ste ga navedli ob registraciji, prejeli obvestilo.": "After the extraction is successfully completed, you will receive a notification to the email address that you provided during registration.", + "Ko boste končali z urejanjem svojega terminološkega slovarja, lahko objavite vse slovarske sestavke, ki bodo postali vidni vsem uporabnikom. Vaše dejanje mora potrditi še administrator portala.": "After you finish editing your terminology dictionary, you can publish all the dictionary entries, which will become visible to all users. Your action must be approved by the portal administrator.", + "komentar": "comment", + "komentarja": "comments", + "komentarjev": "comments", + "komentarji": "comments", + "Komentarji": "Comments", + "Končan": "Finished", + "Konec": "Finish", + "Kontrola objave": "Publication control", + "KONTROLA OBJAVE": "PUBLICATION CONTROL", + "Korpus OSS": "OSS Corpus", + "Lastna svetovalnica": "Your own consultancy", + "Lastni dokumenti": "Your own documents", + "Lastnosti": "Properties", + "LETO": "YEAR", + "Luščenje": "Extraction", + "Luščenje | dodaj opravilo": "Extraction | add a task", + "Luščenje iz korpusa besedil OSS": "Extraction from the texts of the OSS corpus", + "Luščenje iz lastnih besedil": "Extraction from your own texts", + "Luščenje iz lastnih besedil | dodaj opravilo ": "Extraction from your own texts | add a task", + "Luščenje je bilo dano v fazo obdelave.": "The extraction was sent to the processing phase.", + "Luščenje končano": "Extraction completed", + "Luščenje terminoloških kandidatov iz besedil, ki jih ima uporabnik shranjena pri sebi. Za boljšo učinkovitost svetujemo format .txt.": "Extracting term candidates from texts that the user has stored on his own devices. We recommend using .txt file format for better results.", + "Luščenje terminoloških kandidatov iz besedil, ki jih uporabnik izbere med vsemi besedili, vključenimi v Nacionalni portal odprte znanosti.": "Extracting term candidates from texts selected by the user among all texts included in the National Open Science Portal.", + "Luščenje terminoloških kandidatov iz besedil, ki jih uporabnik namensko izbere, je najučinkovitejše. Primerno izbrano ime opravila, vam omogoča, da lahko sledite, katera luščenja ste že opravili. Ko vnesete podatke, morate vse spremembe shraniti. Zaradi omejenega prostora za shranjevanje lahko shranite največ pet zadnjih luščenj. ": "Extracting term candidates from texts purposefully selected by the user is more effective. Using a well-chosen name for the task helps you track which extractions have already been performed. After entering the data, make sure to save all changes. Due to limited storage space, only the last five extractions will be saved.", + "Luščenje terminoloških kandidatov iz besedil, ki so že predhodno oblikoslovno označena, nudi dobre rezultate, vendar je treba nabor besedil omejiti. Svetujemo vam, da zoožite področje in dodatno omejite izbiro s tipi besedil in časovnim razponom, v katerih so nastala. Ko vnesete podatke, morate vse spremembe shraniti. Po vnosu podatkov, s katerimi boste omejili nabor izbranih besedil, morate pritisniti gumb Najdi. Izbiro boste shranili lahko le v primeru, da ne bo število najdenih dokumentov preveliko.": "Extracting term candidates from texts annotated with linguistic metadate provides good results, but the selection of texts must be limited. We recommend narrowing down the domain and selecting the text types and year of publication to further limit the scope. Make sure to save all changes after entering the data. After filling in the data to limit the text selection, click the Search button. You will only be able to save the selection if the number of found texts does not exceed the limit.", + "MAKSIMALNO ŠTEVILO KOPIJ SLOVARSKIH SESTAVKOV": "MAXIMUM NUMBER OF DICTIONARY ENTRY VERSIONS", + "Meni": "Menu", + "MENI": "MENU", + "MINIMALNO ŠTEVILO SLOVARSKIH SESTAVKOV": "MINIMUM NUMBER OF DICTIONARY ENTRIES", + "Modul za luščenje terminoloških kandidatov iz besedil.": "Module for extracting term candidates from texts.", + "Modul za svetovanje pri terminoloških zagatah.": "Terminology consulting module.", + "Modul za urejanje terminoloških slovarjev.": "Module for editing terminology dictionaries.", + "MODULI": "MODULES", + "Moj Profil": "My Profile", + "Moji slovarji": "My dictionaries", + "Morebitne že obstoječe poimenovalne rešitve, če obstajajo.": "Any already existing naming solutions.", + "Morebitni primeri rabe termina v besedilih ali povezave do njih, če obstajajo.": "Any examples of term use in texts or links to text where the term is used.", + "MOŽNOST OBJAVE SLOVARSKEGA SESTAVKA MED UREJANJEM": "OPTION OF PUBLISHING THE DICTIONARY ENTRY WHILE EDITING", + "MULTIMEDIJA": "MULTIMEDIA", + "Na kratko opišite zasnovo in namen slovarja.": "Briefly describe the composition and purpose of the dictionary.", + "Na seznamu uporabnikov lahko določite posameznemu uporabniku dodatne vloge ali urejate njihove podatke.": "You can assign additional roles or edit user data from all users on the list.", + "Na tem mestu lahko dodajate svetovalce, ki so registrirani uporabniki terminološkega portala. Če boste pripisali področje, boste lahko svetovalcu dodeljevali samo vprašanja, ki sodijo na izbrano področje, vsem drugim pa vsa.": "Here you can add consultants who are registered users of the terminology portal. If you assign them a domain, you will only be able to assign them questions related to this domain, otherwise you can assign them all questions.", + "Na tem mestu lahko določite področne oznake, če želite posamezne termine v svojem terminološkem slovarju razvrstiti še podrobneje.": "Here you can define domain labels, if you want to classify individual terms in your terminology dictionary in more detail.", + "Na tem mestu so zbrani vsi komentarji, povezani s terminološkim portalom.": "All comments related to the terminology portal can be found here.", + "Na tem mestu so zbrani vsi komentarji, povezani z izbranim terminološkim slovarjem.": "All comments related to the selected terminology dictionary can be found here.", + "na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili svoj uporabniški račun na Terminološkem portalu. Povezava za potrditev je veljavna ": "we a message with a link to verify your Termonology Portal user account to your e-mail address. The verification link is valid.", + "Na vaš elektronski naslov smo vam posredovali povezavo za ponastavitev gesla. Prosimo preverite svoj elektronski predal.": "We sent a password reset link to your e-mail address. Please, check your inbox.", + "Nabor vseh luščenj, s katerimi lahko uporabnik iz izbranih besedil pridobi sezname terminoloških kandidatov. Posamezne terminološke kandidate lahko preveri tudi v konkordančniku.": "All extractions the users can use to obtain a list of term candidates from the selected texts. The user can also check individual term candidates using the concordance tool.", + "Nabor vseh luščenj, s pomočjo katerih uporabnik iz vhodnih besedil pridobi sezname terminoloških kandidatov in dostop do konkordančnika po teh besedilih.": "All extractions the users can use to obtain a list of term candidates from the selected texts and access to the concordance tools for these texts.", + "Nabor vseh slovarjev, ki jih uporabnik lahko ureja kot glavni urednik ali pa ima dodeljeno pravico urejanja, pregledovanja, popravljanja ipd.": "All dictionaries where the user is the main editor or has the editing, reviewing, correcting, etc. rights.", + "NAČIN UVOZA": "IMPORT METHOD", + "Naglas": "Accent", + "Najdi": "Search", + "Naložbo sofinancirata Republika Slovenija in Evropska unija iz Evropskega sklada za regionalni razvoj": "The investment is co-financed by the Republic of Slovenia and the EU via the European Regional Development Fund", + "Napačno geslo.": "Wrong password.", + "Napaka": "Error", + "Napaka na strežniku": "Server error", + "NAPAKA pri iskanju": "Search ERROR", + "Napaka pri nalaganju datoteke": "Error loading file", + "NAPAKA V UVOZU": "IMPORT ERROR", + "Napredno": "Advanced", + "Napredno urejanje omogoča spreminjanje večjega števila podatkov v terminološkem slovarju.": "Advanced editing allows the user to change more data in the terminology dictionary.", + "Naprej": "Next", + "Naslednje datoteke niso bile naložene zaradi navedenih razlogov:": "The following files were not uploaded due to the following reasons:", + "Naslov": "Address", + "NASLOV": "ADDRESS", + "NASLOV SLOVARJA *": "DICTIONARY TITLE *", + "Nastavitve": "Settings", + "Nastavitve portala": "Portal settings", + "Nastavitve računa": "Account settings", + "Nastavitve slovarjev": "Dictionaries settings", + "Nastavitve svetovalnice": "Consultancy settings", + "Nazaj": "Back", + "Nazaj na prijavo": "Back to login", + "Nazaj na zadetke": "Back to results", + "Ne": "No", + "Ne izbriši": "Do not delete", + "Ne morete imeti več kot 5 luščenj!": "You cannot have more than 5 extractions!", + "Ne shrani": "Do not save", + "nedefinirano": "undefined", + "Nemško-slovenski slovar tehnike in naravoslovja": "German-Slovenian dictionary of technology and natural sciences", + "Neobjavljeni": "Unpublished", + "Nepravilna vrednost strani": "Invalid page value", + "Nepravilno uporabniško ime, elektronski naslov ali geslo.": "Incorrect username, e-mail address or password.", + "Nepregledani": "Not reviewed", + "Neveljavna e-pošta": "Invalid e-mail", + "Neveljavni": "Invalid", + "Neveljavni elektronski naslov.": "Invalid e-mail address", + "Ni področja": "No domain", + "Ni še dodanih uvozov.": "No imports have been added.", + "Ni še povezanih portalov": "No portals have been connected", + "Ni slovarskih sestavkov za urejanje.": "There are no dictionary entries to edit.", + "Ni vprašanj.": "No questions.", + "Ni zadetkov": "No results", + "Nimate izbranega področja CERIF.": "No CERIF domain selected.", + "Nimate luščenj za urejanje.": "No extractions to edit.", + "Nimate povezav za urejanje.": "No linked portals to edit.", + "Nimate slovarjev za urejanje.": "No dictionaries to edit.", + "Nimate ustreznih pravic": "You do not have the required rights", + "Niste izbrali glavnega področja.": "Primary domain missing.", + "Niste vnesli področnih oznak.": "Domain labels missing.", + "Niste vpisali angleškega naslova slovarja.": "Dictionary title in English missing.", + "Niste vpisali imena": "First name missing.", + "Niste vpisali imena slovarja.": "Dictionary name missing.", + "Niste vpisali naslova slovarja.": "Dictionary title missing.", + "Niste vpisali opisa terminološkega problema.": "Terminology problem description missing.", + "Niste vpisali priimka.": "Last name missing.", + "Niste vpisali tujih jezikov.": "Foreign languages missing.", + "Nov": "New", + "Nov avtor": "New author", + "NOV AVTOR SLOVARJA": "NEW DICTIONARY AUTHOR", + "Nov povezan termin.": "New related term.", + "Nov slovar": "New dictionary", + "NOV UPORABNIK": "NEW USER", + "Nov video": "New video", + "NOV VIDEO": "NEW VIDEO", + "Nov video.": "New video.", + "Nov zvok": "New audio file", + "NOV ZVOK": "NEW AUDIO FILE", + "Nov zvok.": "New audio file.", + "Nova povezava": "New link", + "Nova slika": "New image", + "NOVA SLIKA": "NEW IMAGE", + "Nova slika.": "New image.", + "Novo": "New", + "NOVO GESLO": "NEW PASSWORD", + "Novo podpodročje": "New secondary domain", + "NOVO PODPODROČJE": "NEW SECONDARY DOMAIN", + "NOVO PODPODROČJE (angleško)": "NEW SECONDARY DOMAIN (English)", + "Novo podpodročje (angleško).": "New secondary domain (English).", + "NOVO PODPODROČJE (slovensko)": "NEW SECONDARY DOMAIN (Slovenian)", + "Novo terminološko vprašanje": "New terminology question", + "Novo vprašanje": "New question", + "Novo vprašanje za Terminološko svetovalnico": "New question for the Terminological Consulting", + "O slovarju": "About the dictionary", + "O terminu": "About the term", + "Objava med urejanjem": "Publish while editing", + "Objava terminološkega vprašanja": "Publish a terminology question", + "Objava vseh slovarskih sestavkov": "Publish all dictionary entries", + "Objavi": "Publish", + "OBJAVI": "PUBLISH", + "Objavljeni": "Published", + "Objavljeno": "Published", + "Oblike": "Forms", + "Oblike, naglasi, izgovor": "Forms, accents, pronunciation", + "Obljavljeno": "Published", + "Obnovi": "Restore", + "Obstoječe poimenovalne rešitve": "Existing naming solutions", + "OBSTOJEČE POIMENOVALNE REŠITVE": "EXISTING NAMING SOLUTIONS", + "Obvestilo": "Notification", + "Obvestilo o številu gesel": "Notification about the number of entries", + "Odgovor": "Answer", + "Odgovori": "Answer", + "Odgovori na vprašanja:": "Answers to the questions:", + "Odjava": "Log out", + "odprt": "published", + "Odprt": "Published", + "Odstrani": "Remove", + "Omogočeno": "Enabled", + "OPIS PORTALA": "PORTAL DESCRIPTION", + "OPIS SLOVARJA": "DICTIONARY DESCRIPTION", + "Opis terminološkega problema": "Description of the terminology problem", + "OPIS TERMINOLOŠKEGA PROBLEMA": "DESCRIPTION OF THE TERMINOLOGY PROBLEM", + "Opis terminološkega problema:": "Description of the terminology problem:", + "Opis terminološkega problema: ": "Description of the terminology problem: ", + "Osnovne nastavitve": "Basic settings", + "Osnovni podatki": "Basic data", + "Ožji": "Narrow", + "Oznaka portala": "Portal label", + "OZNAKA PORTALA *": "PORTAL LABEL *", + "Po koncu urejanja vseh slovarskih sestavkov lahko slovar objavite in ga tako prikažete na javnem delu terminološkega portala.": "After all dictionary entires are edited, you can publish the dictionary to make it available on the public part of the terminology portal.", + "Počisti filtre": "Remove filters", + "POČISTI FILTRE": "REMOVE FILTERS", + "Podnaslov": "Subtitle", + "Podpodročja": "Secondary domains", + "Podpodročja so namenjena podrobnejšemu razvrščanju terminoloških slovarjev. Administrator mora vsa novo predlagana podpodročja potrditi, preden jih lahko na seznamu vidijo tudi drugi uporabniki portala.": "Secondary domains are used for more detailed classification of terminology dictionaries. All new suggested secondary domains must be approved by the Portal Administrator before other portal users can see them on the list.", + "Podpodročje": "Secondary domain", + "PODPODROČJE": "SECONDARY DOMAIN", + "Podpodročje.": "Secondary domain.", + "Področja": "Domains", + "Področje": "Domain", + "PODROČJE": "DOMAIN", + "PODROČJE *": "DOMAIN *", + "Področje, v katero sodi opisani terminološki problem.": "Domain of the described terminology problem.", + "Področje.": "Domain.", + "Področna oznaka": "Domain label", + "PODROČNA OZNAKA": "DOMAIN LABEL", + "PODROČNA OZNAKA:": "DOMAIN LABEL:", + "Področne oznake": "Domain labels", + "PODROČNE OZNAKE": "DOMAIN LABELS", + "Podvoji": "Duplicate", + "PODVOJI": "DUPLICATE", + "Poiščite naslove terminoloških slovarjev, s katerimi želite dopolniti iskalne zadetke na svojem portalu. Lahko izberete vse slovarje ali le nekatere. Svoje izbire morate shraniti. Vse vaše izbire mora potrditi administrator povezanega portala.": "Find the titles of terminology dictionaries you want to add to the search results on your portal. You can select all the dictionaries or only a few. You have to save your selection. Your selection must be approved by the administrator of the linked portal.", + "Pojasnilo": "Qualifier", + "POJASNILO": "QUALIFIER", + "POJASNILO:": "QUALIFIER:", + "POJAVITVE": "APPEARANCES", + "Poleg objave slovarskega sestavka v fazi \"Urejeno\" je omogočena tudi objava v fazi \"V urejanju\".": "In addition to the publication of dictionary entries in the \"Edited\" phase, they can also be published while \"In editing\" phase.", + "Polja naslov, vprašanje in mnenje so obvezna!": "The Title, Question and Opinion fields are mandatory!", + "Polno ime terminološkega portala.": "Full name of the terminology portal.", + "Pomoč": "Help", + "Ponastavi geslo": "Reset password", + "Ponovi Geslo": "Repeat password", + "PONOVI GESLO": "REPEAT PASSWORD", + "PONOVI NOVO GESLO": "REPEAT NEW PASSWORD", + "Portal": "Portal", + "Portal lahko povežete s Terminološko svetovalnico ZRC SAZU in tako prikazujete terminološke odgovore na svojem portalu, lahko pa vklopite lastno svetovalnico. V tem primeru morate med registriranimi uporabniki izbrati svetovalce in urednika svetovalnice, ki bodo odgovarjali na terminološka vprašanja uporabnikov.": "You can connect your portal to the ZRC SAZU Terminological counselling and show their terminology answers on your portal, or you can activate your own consultancy service. In the latter case, you have to select the consultants and the consultancy service editor who will answer terminology questions of your portal users. The consultants and consultancy service editor must be registered users.", + "Portali": "Portals", + "Pošlji": "Send", + "Potrdi": "Confirm", + "POTRDI": "CONFIRM", + "Potrditev objave": "Confirm publication", + "Potrjen": "Confirmed", + "Povezani slovarji": "Linked dictionaries", + "Povezani termin": "Related term", + "POVEZANI TERMIN": "RELATED TERM", + "POVEZANI TERMIN:": "RELATED TERM:", + "POVEZAVA": "LINK", + "Povezava ni (več) veljavna. Prosimo, da ponovno zahtevate ponastavitev gesla.": "The link is (no longer) valid. Please, request new password reset link.", + "Povezave": "Links", + "Povezave s portali": "Links to other portals", + "Pozabljeno geslo": "Forgoten password", + "Pozdravljeni ": "Hello ", + "Pozor": "Attention", + "Prazno obvezno polje": "Mandatory field is empty", + "Preden naložite besedila, jih shranite v besedilni obliki (končnica .txt).": "Before uploading texts, save them in text format (.txt file).", + "Predlog": "Suggestion", + "Predogled": "Preview", + "Pregledani": "Reviewed", + "Prekinjen": "Interrupted", + "Prekliči": "Cancel", + "Preverite, če ste pravilno vpisali uporabniško ime. Bodite pozorni na velike in male črke.": "Check if you entered the username correctly. Pay attention to capital letters.", + "Prevod": "Translation", + "Pri brisanju datoteke je prišlo do napake.": "An error occurred during file deletion.", + "Priimek": "Last name", + "PRIIMEK": "LAST NAME", + "Priimek registriranega uporabnika.": "Last name of the registered user.", + "Prijava": "Login", + "PRIJAVA": "LOGIN", + "Prijava uspešna": "Login successful", + "Prikaži datume": "Show dates", + "Prikaži filtre": "Show filters", + "PRIKAŽI VSE": "SHOW ALL", + "Primeri rabe": "Examples of use", + "PRIMERI RABE V BESEDILIH": "EXAMPLES OF USE IN TEXTS", + "Pripravljeno": "Ready", + "Prišlo je do napake pri pošiljanju sporočila na vaš elektronski naslov. Poskusite ponovno.": "An error occurred while sending the message to your e-mail address. Please, try again.", + "Prišlo je do napake.": "An error has occurred.", + "Prišlo je do strežniške napake. Poskusite kasneje.": "A server error has occurred. Please, try again later.", + "Prosim, ponovno vnesite geslo": "Please re-enter your password", + "Prosim, vnesite geslo": "Please enter your password", + "RAČUN": "ACCOUNT", + "Razumem": "I understand", + "registracija": "registration", + "Registracija": "Registration", + "Registracija uporabnika je potrjena.": "User registration confirmed.", + "Registracija uspešna": "Registration successful", + "Registrirani uporabniki lahko dobijo različne vloge na portalu. Kot administrator jim lahko dodelite tudi vlogo skrbnika slovarjev in/ali skrbnika svetovalnice.": "Registered users on the portal can have different roles. As Portal Administrator you can assign them the role of Dictionary Administrator and/or Consultancy Administrator.", + "REPUBLIKA SLOVENIJA MINISTRSTVO ZA KULTURO": "REPUBLIC OF SLOVENIA, MINISTRY OF CULTURE", + "rezultat": "result", + "Rezultat luščenja so terminološki kandidati. Z uvozom rezultatov posameznega luščenja lahko dopolnite geslovnik terminološkega slovarja, ki ga urejate.": "Extraction results in a list of term candidates. You can import the results of individual extractions to add the term candidates to the list of terms in the dictionary you are editing.", + "rezultata": "results", + "rezultati": "results", + "Rezultati: ": "Results: ", + "rezultatov": "results", + "S ključnimi besedami, ki so vključene v večino znanstvenih in strokovnih, lahko bolj natančno izberete besedila, ki jih želite uporabiti za luščenje terminoloških kandidatov.": "Most of the scientific and specialized texts have keywords you can use for a more precise selection of texts from which to extract term candidates.", + "S prijavo pridobite možnost uporabe vseh funkcij terminološkega portala.": "By registering, you get access to all functions of the terminology portal.", + "S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju in vse metapodatke. Dejanja ni mogoče razveljaviti.": "This action will delete all dictionary entries in the dictionary and all metadata. The action cannot be undone.", + "S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju. Metapodatki bodo ostali. Dejanja ni mogoče razveljaviti.": "This action will delete all dictionary entries in the dictionary. The metadata will not be deleted. The action cannot be undone.", + "Še niste registriran uporabnik, registrirajte se.": "You are not a registered user. Please, register.", + "Settings": "Settings", + "Seznam": "List", + "Seznam luščenj": "List of exctractions", + "Seznam neželenih terminov": "List of unwanted terms", + "Seznam objavljenih vprašanj.": "List of published questions.", + "Seznam povezav": "List of linked portals", + "Seznam slovarjev": "List of dictionaries", + "Seznam slovarjev s povezanega portala.": "List of dictionaries from the linked portal.", + "Seznam svetovalcev": "List of consultants", + "Seznam terminoloških kandidatov": "List of term candidates", + "Seznam terminoloških kandidatov bo natančnejši, če boste dodali tudi seznam neželenih besed, ki naj jih luščilnik izloči iz seznama. Seznam lahko vsebuje splošne termine, npr. tabela, kazalnik, ali pogoste slovnične besede, zlasti veznike, pomožne glagole.": "The list of term candidates will be more accurate if you also add a list of unwanted words that the extractor should exclude from the list of term candidates. This list of unwanted words should include general terms, such as table or indicator, and commonly used grammatical words, such as conjunctions and auxiliary vers.", + "Seznam terminoloških kandidatov, ki so rezultat izbranega luščenja.": "List of term candidates resulting from the selected extraction.", + "Seznam uporabnikov": "List of users", + "Seznam urejenih vprašanj, ki čakajo na potrditev dokončne objave.": "List of edited questions waiting for publication approval.", + "Seznam vprašanj, ki so jih moderatorji zavrnili in jih je treba dodeliti nekomu drugemu.": "List of questions rejected by moderators that should be assigned to someone else.", + "Seznam vprašanj, ki so jih poslali uporabniki, in še niso bila dodeljena moderatorjem.": "List of questions sent by the users waiting to be assigned to moderators.", + "Seznam vprašanj, ki so še v urejanju.": "List of questions that are still being edited.", + "Seznam vseh slovarjev, ki jih uredniki, sicer registrirani uporabniki, urejajo na tem portalu.": "List of all dictionaries being edited on this portal by editors, who are also registered users.", + "Seznam vseh slovarjev, ki so na tem portalu na voljo uporabnikom.": "List of all dictionaries available to users on this portal.", + "Seznam zadetkov": "List of results", + "Shemo za format xml si prenesete tukaj.": "You can download the xml format scheme here.", + "Shrani": "Save", + "Shranjeno": "Saved", + "Shranjujem ...": "Saving...", + "Sinhroniziraj": "Synchronize", + "Sinonim": "Synonym", + "SINONIM:": "SYNONYM:", + "Sinonimi": "Synonyms", + "SINONIMI": "SYNONYMS", + "Širši": "Broad", + "SKRAJŠANI NASLOV SLOVARJA": "SHORT DICTIONARY TITLE", + "Skrbnik portala": "Portal administrator", + "Skrbnik slovarjev": "Dictionary administrator", + "Skrbnik svetovalnice": "Consultancy administrator", + "Skrbniki portala": "Portal administrators", + "slika": "image", + "Slika": "Image", + "SLIKA": "IMAGE", + "SLIKA:": "IMAGE:", + "Slovar": "Dictionary", + "Slovar je bil izbrisan.": "The dictionary has been deleted.", + "Slovar je odprt.": "The dictionary is published.", + "Slovar je pregledal jezikoslovec.": "The dictionary was reviewed by a linguist.", + "Slovar je pregledal področni strokovnjak.": "The dictionary has been reviewed by a terminology expert.", + "Slovar je v odpiranju - čaka na potrditev skrbnika slovarjev": "The dictionary is being published - waiting for approval from the dictionary administrator", + "Slovar odprt": "Dictionary is published", + "SLOVAR UVOŽEN": "DICTIONARY IMPORTED", + "Slovarji": "Dictionaries", + "Slovarji portala": "Dictionaries on the portal", + "Slovarski sestavek je neveljaven": "The dictionary entry is invalid", + "Slovarski sestavek je pregledan in dokončan.": "The dictionary entry has been reviewed and completed.", + "Slovarski sestavek je v fazi, ko se dodajajo in oblikujejo elementi, ki ste jih izbrali v strukturi slovarskega sestavka.": "The dictionary entry is in the editing phase, in which the elements of the dictionary entry structure are being added and edited.", + "Slovarski sestavki so bili izbrisani.": "Dictionary entries have been deleted.", + "Slovarski sestavki so bili objavljeni.": "Dictionary entries have been published.", + "SLOVARSKIH SESTAVKOV": "DICTIONARY ENTRIES", + "Slovenščina": "Slovenian", + "Soglašam s politiko zasebnosti": "I agree with the Privacy policy", + "Sorodni": "Similar", + "Sprememba stanja slovarja": "Dictionary status change", + "Spremeni geslo": "Change password", + "SPREMENJEN": "CHANGED", + "ŠT. GESEL": "NO. OF ENTRIES", + "STARO GESLO": "OLD PASSWORD", + "Statistika": "Statistics", + "Status": "Status", + "STATUS": "STATUS", + "Ste pozabili geslo? Napišite svoje uporabniško ime ali elektronski naslov, ki ste ga uporabili ob registraciji. Na ta naslov vam bomo poslali sporočilo, s pomočjo katerega boste lahko vnesli novo geslo.": "Forgot your password? Enter you username or the e-mail address you used for registration. We send you a message with instructions for password reset to this e-mail address.", + "Število dokumentov": "Number of documents", + "Število slovarskih sestavkov je manjše od zahtevanega števila - povečajte število slovarskih sestavkov, sicer ga skrbnik slovarjev lahko zapre": "The dictionary has less entries than the required minimum - add more entries to the dictionary, otherwise it may be unpublished by the Dictionary administrator", + "Število slovarskih sestavkov, ki jih mora vsebovati slovar, da je omogočena objava slovarja na portalu.": "Minimal number of dictionary entries required for a dictionary to be published on the portal.", + "Število terminov": "Number of terms", + "Število vprašanj:": "Number of questions:", + "ŠTEVILO ZADETKOV NA STRANI": "NUMBER OF RESULTS ON THE PAGE", + "Število zadnjih kopij spremenjenih slovarskih sestavkov, ki se hranijo. Če vrednost ni določena, omejitve ni.": "The largest allowed number of saved of recent dictionary entry versions. If no value is entered, there is not limit.", + "Stop termini": "Stop terms", + "Stran ne obstaja": "Page does not exist", + "Strežnik ni dosegljiv. Poskusite kasneje.": "Unable to connect to server. Try again later.", + "Strinjam se s pogoji uporabe": "I agree to the Terms of use", + "Strokovni pregled": "Terminology review", + "Strokovno pregledano": "Terminologically reviewed", + "Struktura": "Structure", + "STRUKTURA - ELEMENTI SLOVARSKEGA SESTAVKA:": "STRUCTURE - ELEMENTS OF THA DICTIONARY ENTRY:", + "Struktura slovarskega sestavka": "The structure of the dictionary entry", + "Svetovalci": "Consultants", + "Svetovalec": "Consultant", + "Svetovalnica": "Consultancy", + "Svetovanje": "Consulting", + "Svoj terminološki portal lahko povežete še z drugimi terminološkimi portali in med iskalnimi prikazujete tudi njihove zadetke.": "You can link your terminology portal to other terminology portals and show their terminology resources in your results.", + "Ta slovar nima opisa": "This dictionary has no description", + "Termin": "Term", + "TERMIN": "TERM", + "TERMIN:": "TERM:", + "TERMINI": "TERMS", + "Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj.": "The Terminological consulting is aimed at wider expert public when faced with the problem of naming specific concepts, whether they are completely new concept that do not yet have a term, or already known concepts for which several different terms are used.", + "Terminološka svetovalnica ZRC SAZU": "ZRC SAZU Terminological counselling", + "Terminološke slovarje lahko v celoti ali po izbranih kriterijih izvozite v različnih formatih in shranite na svojem računalniku.": "Terminological dictionaries in their entirety or according to selected criteria can be exported formats in various and saved to your computer.", + "Terminološkemu slovarju določite status. Izbirate lahko med zaprt, v urejanju in odprt.": "Set the status of the terminology dictionary. Possible statuses are unpublished, in editing, and published.", + "Terminološki kandidati": "Term candidates", + "Terminološko svetovanje": "Terminology counseling", + "Tip napake 1 (ni slovenskega termina)": "Error type 1 (no Slovenian term)", + "Tip napake 2 (ni definicije ali tujejezičnega termina)": "Error type 2 (no definition or term in foreign language)", + "Tu so zbrani vsi komentarji, povezani z vašim slovarjem.": "All comments related to the your dictionary can be found here.", + "Tuj sinonim": "Synonym in a foreign language", + "Tuj termin": "Term in a foreign language", + "Tuja definicija": "Definition in a foreign language", + "Tuji jezik": "Foreign language", + "TUJI JEZIKI": "FOREIGN LANGUAGES", + "Tuji slovarji": "Dictionaries in a foreign language", + "TUJI TERMIN": "TERMS IN A FOREIGN LANGUAGE", + "Tukaj lahko izbrišete svoj račun.": "Here you can delete your account.", + "Tukaj lahko ponovno ideksirate slovar.": "Here you can re-index the dictionary.", + "Tukaj lahko spremenite geslo za prijavo.": "Here you can change your login password.", + "Tukaj lahko spremenite ime, priimek in elektronski naslov uporabnika.": "Here you can change the user's first name, last name and e-mail address.", + "Tukaj lahko spremenite nekatere nastavitve, vezane na posameznega uporabnika.": "Here you can change certain settings related to individual users.", + "Unikatna oznaka povezanega terminološkega portala za prikaz na vašem portalu, sestavljena iz dveh črk ali črke in številke.": "A unique identifier of the linked terminology portal to be display on your portal, consisting of two letters or a letter and a number.", + "Uporabi": "Use", + "Uporabnik": "User", + "UPORABNIK": "USER", + "Uporabnik na tem mestu izdela specializirani korpus iz besedil, ki jih je zbral in shranil sam. Besedila naj bodo izbrana po načelih tvorjenja specializiranih korpusov. Za uspešno luščenje priporočamo najmanj 10 besedil. Vsa besedila naj bodo shranjena v besedilnem formatu (.txt). Luščenje podpira tudi formate .docx in .pdf, vendar so rezultati slabši.": "Here the user can create a specialized corpus from texts selected and saved by the user. The texts should be selected in accordance with the principles for creation of specialized corpora. For successful extraction, at least 10 texts should be used. All texts should be saved in a .txt format. While the extractor also supports the .docx and .pdf formats, the results will not be as good.", + "Uporabniki": "Users", + "Uporabnikovi dokumenti": "User documents", + "Uporabniške pravice/vloge": "User rights/roles", + "Uporabniške vloge": "User roles", + "Uporabniški korpus": "User's corpus", + "Uporabniški račun še ni aktiviran. Kliknite aktivacijsko povezavo, katero smo vam poslali po elektronski pošti.": "The user account has not been activated. Click the link sent to your e-mail to activate your account.", + "Uporabniško ime": "Username", + "UPORABNIŠKO IME": "USERNAME", + "Uporabniško ime ali elektronski naslov": "Username or e-mail address", + "Uporabniško ime že obstaja": "Username already exists", + "Uredi": "Edit", + "UREDI": "EDIT", + "Uredi lastnosti": "Edit properties", + "Uredi povezavo": "Edit the link", + "Uredi vsebino": "Edit content", + "Urednik slovarja": "Dictionary editor", + "Urejanje": "Edit", + "Urejanje terminološkega odgovora": "Edit terminological answer", + "Urejeni": "Edited", + "Urejeno": "Edited", + "URL naslov terminološkega portala, s katerim boste sinhronizirali podatke iz terminoloških virov.": "URL address of the terminology portal hosting terminology resources with data you want to show on your portal.", + "URL naslov terminološkega portala, s katerim želimo povezati svoj portal.": "URL address of the terminology portal you want to link to your portal.", + "URL naslov terminološkega portala, s katerim želimo povezati svoj portal. (API klic)": "URL address of the terminology portal with which we want to connect our portal. (API call)", + "URL naslov terminološkega portala, s katerim želite povezati svoj portal. (API klic)": "URL address of the terminology portal you want to link to your portal. (API call)", + "URL povezava do mnenja:": "URL link to opinion:", + "URL za sinhronizacijo slovarjev *": "URL for synchronization of dictionaries *", + "URL za sinhronizacijo slovarskih sestavkov *": "URL for synchronization of dictionary entries *", + "Uspešno ste ponastavili svoje geslo.": "You have successfully reset your password.", + "Ustvari": "Create", + "USTVARJEN": "CREATED", + "Ustvarjeno novo vprašanje v svetovalnici": "A new question has been created in the Consultancy service", + "UTEŽ": "WEIGHT", + "Uvoz": "Import", + "Uvoz iz datoteke": "Import from file", + "Uvoz iz luščilnika": "Import from the extractor", + "UVOZI": "IMPORTS", + "Uvozi termine od številke": "Import appointments between the numbers", + "V delu": "In progress", + "V DRUGI VSEBINI": "IN OTHER PARTS OF THE ENTRY", + "V IZTOČNICAH": "IN THE TERMS", + "V obdelavi": "In progress", + "V odpiranju": "In the process of publication", + "v predogledu": "in preview", + "V slovarju so samo terminološki kandidati. Slovarja ne morete objaviti.": "The dictionary contains only term candidates. The dictionary cannot be published.", + "V tem razdelku lahko določite elemente slovarskega sestavka. Z izbiro elementov se vam prikazuje podoba slovarskega sestavka. Izbiro lahko tudi med urejanjem vsebine kadarkoli spremenite.": "In this section, you can select the elements of the dictionary entry. When you select the elements, you can see what the entry will look like. You can change your selection any time during the editing process.", + "V tem razdelku lahko določite glavne administratorske pravice na terminološkem portalu.": "In this section, you can assign the main administrative rights on the terminology portal.", + "V tem razdelku lahko določite uporabniške vloge posameznega uporabnika in urejate njegove podatke.": "In this section, you can define the user roles of individual users and edit their data.", + "V TUJEJEZIČNIH USTREZNIKIH": "IN FOREIGN LANGUAGE EQUIVALENTS", + "V urejanju": "In editing", + "Vaš komentar je brez vsebine.": "Your comment is empty.", + "Vaše iskanje ni bilo uspešno. Vpišite novo iskalno poizvedbo in poizkusite znova.": "Your search was not successful. Enter a new search string and try again.", + "Več ...": "More...", + "Več …": "More...", + "Več...": "More...", + "VELIKOST": "SIZE", + "Veljavni": "Valid", + "Veljavni elektronski naslov uporabnika, na katerega uporabnik prejema sporočila, povezana s portalom.": "A valid e-mail address of the user to which the user receives messages related to the portal.", + "Veljavno": "Valid", + "Veljavnost": "Validity", + "Verzija": "Version", + "Verzija 1": "Version 1", + "Verzija:": "Version:", + "video": "video", + "Video": "Video", + "VIDEO": "VIDEO", + "VIDEO:": "VIDEO:", + "Vidno": "Visible", + "Vir": "Source", + "Viri": "Sources", + "Vnesite e-naslov, ki ga za obveščanje o novih terminoloških vprašanjih uporabljajo terminološki svetovalci.": "Enter the email address used to notify Terminology Consultants about new terminology questions.", + "Vnesite naslov spletnega mesta, kjer so zbrani vsi odgovori terminološke svetovalnice.": "Enter the URL of the website where all the answers of your consultancy service are collected.", + "Vnesite novo geslo. Ko ga potrdite, se boste v vaš uporabniški račun lahko spet prijavili z novim geslom.": "Enter a new password. After confirming it, you will be able to log into your account with the new password.", + "Vnesite podatke terminološkega portala, s katerim želite povezati svoj portal.": "Enter the information of the terminology portal with which you want to link your portal.", + "Vnesite podatke, ki niso sistemsko vključeni v druga polja, npr. vir, zgled rabe.": "Enter data that is not systemically included in other fields, e.g. examples of use.", + "Vnesite polno ime terminološkega portala, s katerim se povezujete.": "Enter the full name of the terminology portal you are linking with.", + "Vnesite termin.": "Enter the term.", + "Vnesti morate termin": "You must enter the term", + "Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime.": "Enter the author of the dictionary. If you are the main author, write your name first.", + "Vpišite definicijo v tujem jeziku.": "Enter the definition in a foreign language.", + "Vpišite iskalni niz": "Enter a search string", + "Vpišite novo podpodročje. Na seznamu podpodročij bo vidno takoj po potrditvi administratorja portala.": "Enter a new secondary domain. It will be visible on the list of secondary domain immediately after Portal Administrator's approval.", + "Vpišite podpodročje glavnega področja, ki ste ga izbrali. Če podpodročja ni med naborom, izberite polje Novo podpodročje.": "Enter the secondary domain of the domain you selected. If you cannot find the secondary domain on the list, select New secondary domain.", + "Vprašanje poslano:": "Question sent:", + "VRSTA": "TYPE", + "VRSTA DOKUMENTA": "TYPE OF DOCUMENT", + "Vsebina": "Content", + "Vsebina slovarja": "Contents of the dictionary", + "Vsebina slovarskega sestavka je bila duplicirana.": "The content of the dictionary entry has been duplicated.", + "Vsi povezani slovarji": "All linked dictionaries", + "Vsi slovarji": "All dictionaries", + "Vsi slovarski sestavki": "All dictionary entries", + "Vstavite definicijo pojma.": "Enter the definition of the term.", + "Vstavite termine, ki se za definirani pojem tudi uporabljajo.": "Enter terms which are also used for the defined concept.", + "Vstavite ustrezno področno oznako, ki jo želite določiti za posamezni termin.": "Enter the domain label that you want to assign to the individual term.", + "Z izbiro te možnosti boste pobrisali samo slovarske sestavke, ki so v določeni fazi urejanja.": "By selecting this option, you will only delete dictionary entries that are in a certain editing phase.", + "Z registracijo pridobite možnost uporabe vseh funkcij terminološkega portala.": "By registering, you get access to all functions of the terminology portal.", + "Za luščenje terminoloških kandidatov morate biti prijavljeni.": "You must be logged in to extract term candidates.", + "Za prvo objavo slovarja je potrebno dovoljenje skrbnika slovarjev.": "Dictionary administrator's approval is required before the first publication of the dictionary.", + "Za to besedo še ni podatkov.": "There is no data for this word.", + "Za urejanje slovarjev morate biti prijavljeni.": "You must be logged in to edit dictionaries.", + "Za zastavljanje terminoloških vprašanj morate biti prijavljeni.": "You must be logged in to ask terminology questions.", + "Začetek": "Start", + "Začni": "Start", + "ZAČNI": "START", + "Zadnja sprememba": "Last change", + "Zadnji izvozi": "Recent exports", + "Zadnji objavljeni slovarji": "Recent published dictionaries", + "Zadnji odgovori na vprašanja": "Recent answers to questions", + "Zadnji uvozi": "Recent imports", + "Zapomni si prijavo": "Remember my login", + "Zaporedje:": "Sequence:", + "Zapri": "Close", + "ZAPRI": "CLOSE", + "zaprt": "unpublished", + "Zaprt": "Unpublished", + "Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesede\tnje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki.": "Due to the organization of the data on the portal, we suggest using only one word for the short dictionary title, which will show next to the dictionary, e.g. Tax Terminology Dictionary→ Taxes", + "Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesedenje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki": "Due to the organization of the data on the portal, we suggest using only one word for the short dictionary title, which will show next to the dictionary, e.g. Tax Terminology Dictionary→ Taxes", + "Zastavi novo vprašanje": "Ask a new question", + "Zastavi terminološko vprašanje": "Ask a terminology question", + "ZAVRJEN": "REJECTED", + "ZAVRNI": "REJECT", + "Zavrnjeno": "Rejected", + "Zunanji": "External", + "zvok": "audio", + "Zvok": "Audio", + "ZVOK": "AUDIO", + "ZVOK:": "AUDIO:", + "titleTermsOfUse": "Terms of Use", + "titlePrivacyPolicy": "Privacy Policy" +} diff --git a/express/public/locales/en/extended.json b/express/public/locales/en/extended.json new file mode 100644 index 0000000..5b8a229 --- /dev/null +++ b/express/public/locales/en/extended.json @@ -0,0 +1,50 @@ +{ + "helpPageTitleHelp": "Help", + "helpPageTitleAbout": "General information about the Portal", + "helpPageTitleRegistration": "Registration", + "helpPageTitleSearchIndex": "Search", + "helpPageTitleSearchBasic": "Basic search", + "helpPageTitleSearchAdvanced": "Advanced search", + "helpPageTitleExtractionIndex": "Extraction", + "helpPageTitleExtractionSpecializedCorpora": "Specialized corpora", + "helpPageTitleExtractionPersonalCorpus": "User’s personal corpus", + "helpPageTitleExtractionOssCorpus": "OSS Corpus", + "helpPageTitleExtractionStopTerms": "Lists of unwanted words or \"Stop terms\"", + "helpPageTitleExtractionDomains": "Domains", + "helpPageTitleExtractionTermCandidates": "Term candidates", + "helpPageTitleEditingIndex": "Editing", + "helpPageTitleEditingNewDictionary": "New dictionary", + "helpPageTitleEditingDictionaryProperties": "Editing dictionary properties", + "helpPageTitleEditingUsers": "Users", + "helpPageTitleEditingStructure": "Structure of dictionary entries", + "helpPageTitleEditingComments": "Comments", + "helpPageTitleEditingDictionaryContent": "The content of the dictionary", + "helpPageTitleEditingImportingData": "Importing data", + "helpPageTitleEditingEntriesIndex": "Adding new dictionary entries", + "helpPageTitleEditingEntriesDomainLabels": "Domain labels", + "helpPageTitleEditingEntriesLabel": "Qualifiers", + "helpPageTitleEditingEntriesDefinition": "Definition", + "helpPageTitleEditingEntriesSynonyms": "Synonyms", + "helpPageTitleEditingEntriesRelatedTerms": "Related terms", + "helpPageTitleEditingEntriesOtherLanguages": "Other languages", + "helpPageTitleEditingEntriesOther": "Other", + "helpPageTitleEditingEntriesEditingHistory": "Editing history", + "helpPageTitleEditingEntriesComments": "Comments", + "helpPageTitleEditingEntriesDataExport": "Data export", + "helpPageTitleConsultancyIndex": "Consultancy", + "helpPageTitleConsultancyQuestions": "How to ask a question", + "helpPageTitleConsultancyTerminologyPrinciples": "Terminology principles", + "helpPageTitleConsultancyLink": "Link", + "helpPageTitleConsultancyAnswers": "Published answers", + "helpPageTitleAdministrationIndex": "Administration", + "helpPageTitleAdministrationBasicSettingsIndex": "Basic settings", + "helpPageTitleAdministrationBasicSettingsPortal": "Portal", + "helpPageTitleAdministrationBasicSettingsDictionaries": "Dictionaries", + "helpPageTitleAdministrationBasicSettingsConsultancy": "Consultancy", + "helpPageTitleAdministrationLinksToOtherPortalsIndex": "Links to other portals", + "helpPageTitleAdministrationLinksToOtherPortalsList": "List", + "helpPageTitleAdministrationLinksToOtherPortalsDictionaries": "Dictionaries", + "helpPageTitleAdministrationDictionaries": "Dictionaries", + "helpPageTitleAdministrationUsers": "Users", + "helpPageTitleAdministrationSecondaryDomains": "Secondary domains" +} diff --git a/express/public/locales/sl/core.json b/express/public/locales/sl/core.json new file mode 100644 index 0000000..8a5b2ba --- /dev/null +++ b/express/public/locales/sl/core.json @@ -0,0 +1,710 @@ +{ + " Terminološke odgovore pripravljajo sodelavci Terminološke sekcije Inštituta za slovenski jezik Frana Ramovša ZRC SAZU (in so objavljeni tudi na spletišču Terminologišče), ki pri delu upoštevajo osnovna terminološka načela.": " Terminološke odgovore pripravljajo sodelavci Terminološke sekcije Inštituta za slovenski jezik Frana Ramovša ZRC SAZU (in so objavljeni tudi na spletišču Terminologišče), ki pri delu upoštevajo osnovna terminološka načela.", + "(STOP) Termini": "(STOP) Termini", + "[ni termina]": "[ni termina]", + "#": "#", + "Odgovor:": "Odgovor:", + "Administracija": "Administracija", + "Administrator": "Administrator", + "Administratorska konzola": "Administratorska konzola", + "Administratorski del portala je namenjen skupini oseb, ki vsebinsko in tehnično ureja portal, odpira slovarske vire, daje pooblastila posameznim uporabnikom in skrbi za vsebinsko in tehnično urejenost portala. Izberite module, ki jih boste ponudili na terminološkem portalu in na kratko opišite, kaj ponuja vaš portal.": "Administratorski del portala je namenjen skupini oseb, ki vsebinsko in tehnično ureja portal, odpira slovarske vire, daje pooblastila posameznim uporabnikom in skrbi za vsebinsko in tehnično urejenost portala. Izberite module, ki jih boste ponudili na terminološkem portalu in na kratko opišite, kaj ponuja vaš portal.", + "Aktivacija računa": "Aktivacija računa", + "Ali res želite izbrisati vnos? Dejanja ni mogoče razveljaviti.": "Ali res želite izbrisati vnos? Dejanja ni mogoče razveljaviti.", + "Ali želite indeksirati?": "Ali želite indeksirati?", + "Ali želite izbrisati slovarski sestavek?": "Ali želite izbrisati slovarski sestavek?", + "Ali želite objaviti vsa gesla?": "Ali želite objaviti vsa gesla?", + "Ali želite zbrisati ta profil. S tem bodo izbrisani vsi podatki, ki ste jih ustvarili": "Ali želite zbrisati ta profil. S tem bodo izbrisani vsi podatki, ki ste jih ustvarili", + "Ali želite zbrisati ta vnos?": "Ali želite zbrisati ta vnos?", + "angleščina": "angleščina", + "ANGLEŠKI NASLOV SLOVARJA *": "ANGLEŠKI NASLOV SLOVARJA *", + "ANGLEŠKI OPIS PORTALA": "ANGLEŠKI OPIS PORTALA", + "ANGLEŠKO IME PORTALA": "ANGLEŠKO IME PORTALA", + "Angleško ime terminološkega portala.": "Angleško ime terminološkega portala.", + "Avtor": "Avtor", + "AVTOR": "AVTOR", + "AVTOR SLOVARJA": "AVTOR SLOVARJA", + "Avtor:": "Avtor:", + "Avtorja": "Avtorja", + "Avtorji": "Avtorji", + "Avtorji mnenja:": "Avtorji mnenja:", + "Basic": "Basic", + "Besedila": "Besedila", + "Besedilo gre tukaj": "Besedilo gre tukaj", + "Brisanje je onemogočeno, doker se nalagajo nove datoteke.": "Brisanje je onemogočeno, doker se nalagajo nove datoteke.", + "Brisanje je onemogočeno, saj je eno še v procesu.": "Brisanje je onemogočeno, saj je eno še v procesu.", + "Brisanje slovarja": "Brisanje slovarja", + "Brisanje slovarskih sestavkov": "Brisanje slovarskih sestavkov", + "Briši": "Briši", + "BRIŠI": "BRIŠI", + "Briši uporabnika": "Briši uporabnika", + "Če imate pripravljen slovar v enem od formatov, ki so navedeni spodaj, lahko svoje podatke uvozite.": "Če imate pripravljen slovar v enem od formatov, ki so navedeni spodaj, lahko svoje podatke uvozite.", + "Če izberete to možnost, se bodo vsi doslejšnji slovarski sestavki ob uvozu nove datoteke izbrisali.": "Če izberete to možnost, se bodo vsi doslejšnji slovarski sestavki ob uvozu nove datoteke izbrisali.", + "Če želite dodati novo luščenje, morate najprej pobrisati vsaj eno od obstoječih, saj je na posameznega uporabnika dovoljenih največ 5 luščenj.": "Če želite dodati novo luščenje, morate najprej pobrisati vsaj eno od obstoječih, saj je na posameznega uporabnika dovoljenih največ 5 luščenj.", + "Celotni naslov slovarja v angleščini.": "Celotni naslov slovarja v angleščini.", + "Celotni naslov slovarja, ki bo zabeležen v bibliografskih podatkih.": "Celotni naslov slovarja, ki bo zabeležen v bibliografskih podatkih.", + "Ciljni jeziki": "Ciljni jeziki", + "Čim bolj natančno opišite terminološki problem, zlasti opišite vsebino pojma.": "Čim bolj natančno opišite terminološki problem, zlasti opišite vsebino pojma.", + "DATOTEKA": "DATOTEKA", + "Datoteka uspešno naložena": "Datoteka uspešno naložena", + "DATUM": "DATUM", + "Datum objave/spremembe": "Datum objave/spremembe", + "Datum vprašanja:": "Datum vprašanja:", + "Definicija": "Definicija", + "DEFINICIJA": "DEFINICIJA", + "DEFINICIJA:": "DEFINICIJA:", + "Delete profile": "Delete profile", + "Deli": "Deli", + "DELI": "DELI", + "dni.": "dni.", + "do številke": "do številke", + "Dodaj": "Dodaj", + "DODAJ": "DODAJ", + "Dodaj besedilo za luščenje terminoloških kandidatov iz lastnega specializiranega korpusa. Ko boste dodali vsa besedila, ki ste jih izbrali, morate izbiro shraniti.": "Dodaj besedilo za luščenje terminoloških kandidatov iz lastnega specializiranega korpusa. Ko boste dodali vsa besedila, ki ste jih izbrali, morate izbiro shraniti.", + "Dodaj datoteko": "Dodaj datoteko", + "Dodaj komentar...": "Dodaj komentar...", + "Dodaj odgovor...": "Dodaj odgovor...", + "Dodaj opravilo": "Dodaj opravilo", + "Dodaj povezani termin.": "Dodaj povezani termin.", + "Dodaj povezavo": "Dodaj povezavo", + "Dodaj slovar": "Dodaj slovar", + "Dodajte ime in priimek naslednjega avtorja slovarja.": "Dodajte ime in priimek naslednjega avtorja slovarja.", + "Dodajte ime in priimek naslednjega avtorja slovarja..": "Dodajte ime in priimek naslednjega avtorja slovarja..", + "Dodajte leto izida besedil, ki jih želite luščiti. Lahko dodate več posameznih let. Če boste polje pustili prazno, bodo vključena vsa leta. Če bo besedil preveč, boste morali omejiti izbiro.": "Dodajte leto izida besedil, ki jih želite luščiti. Lahko dodate več posameznih let. Če boste polje pustili prazno, bodo vključena vsa leta. Če bo besedil preveč, boste morali omejiti izbiro.", + "Dodajte podatek o zunajjezikovnih okoliščinah, ki niso povezane s pojmom, npr. letnico.": "Dodajte podatek o zunajjezikovnih okoliščinah, ki niso povezane s pojmom, npr. letnico.", + "Dodajte povezavo do slike.": "Dodajte povezavo do slike.", + "Dodajte povezavo do videa.": "Dodajte povezavo do videa.", + "Dodajte povezavo do zvočnega posnetka.": "Dodajte povezavo do zvočnega posnetka.", + "Dodajte termin, ki je sicer definiran v samostojnem slovarskem sestavku, vendar je povezan s terminom, ki ga opisujete v tem slovarskem sestavku.": "Dodajte termin, ki je sicer definiran v samostojnem slovarskem sestavku, vendar je povezan s terminom, ki ga opisujete v tem slovarskem sestavku.", + "Dodajte terminološki odgovor in ga utemeljite.": "Dodajte terminološki odgovor in ga utemeljite.", + "Dodajte tujejezične ustreznike, ki se za opisani pojem tudi uporabljajo v tujem jeziku.": "Dodajte tujejezične ustreznike, ki se za opisani pojem tudi uporabljajo v tujem jeziku.", + "Dodajte tujejezični ustreznik.": "Dodajte tujejezični ustreznik.", + "Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Več...": "Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Več...", + "Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Na koncu morate spremembe shraniti. Več ...": "Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Na koncu morate spremembe shraniti. Več ...", + "Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Vse spremembe morate shraniti. Več...": "Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Vse spremembe morate shraniti. Več...", + "Dodaten avtor.": "Dodaten avtor.", + "DODATNI POVEZANI TERMIN": "DODATNI POVEZANI TERMIN", + "Dodeli": "Dodeli", + "DODELI": "DODELI", + "Določite lastnosti terminoloških virov na portalu, zlasti minimalno število sestavkov, ki so pogoj za objavo, možnosti potrjevanja objave novih slovarjev, število različic slovarja, ki jih lahko hrani posamezni uporabnik. Te nastavitve veljajo za vse terminološke vire na portalu.": "Določite lastnosti terminoloških virov na portalu, zlasti minimalno število sestavkov, ki so pogoj za objavo, možnosti potrjevanja objave novih slovarjev, število različic slovarja, ki jih lahko hrani posamezni uporabnik. Te nastavitve veljajo za vse terminološke vire na portalu.", + "Domači slovarji": "Domači slovarji", + "Drugo": "Drugo", + "DRUGO": "DRUGO", + "DRUGO:": "DRUGO:", + "Dvočrkovna oznaka terminološkega portala.": "Dvočrkovna oznaka terminološkega portala.", + "E - naslov": "E - naslov", + "E-naslov": "E-naslov", + "E-NASLOV": "E-NASLOV", + "E-pošta": "E-pošta", + "ELEKTRONSKI NASLOV": "ELEKTRONSKI NASLOV", + "Elektronski naslov trenutno prijavljenega uporabnika.": "Elektronski naslov trenutno prijavljenega uporabnika.", + "Elektronski naslov že obstaja": "Elektronski naslov že obstaja", + "EVROPSKI SKLAD ZA REGIONALNI RAZVOJ": "EVROPSKI SKLAD ZA REGIONALNI RAZVOJ", + "FAZA UREJANJA": "FAZA UREJANJA", + "Faze urejanja": "Faze urejanja", + "Filtri": "Filtri", + "Filtriranje": "Filtriranje", + "FORMAT ZAPISA": "FORMAT ZAPISA", + "Gesli se ne ujemata": "Gesli se ne ujemata", + "Gesli se ujemata!": "Gesli se ujemata!", + "Geslo": "Geslo", + "GESLO": "GESLO", + "Geslo je prekratko": "Geslo je prekratko", + "Geslo je prekratko.": "Geslo je prekratko.", + "Geslo se ne ujema": "Geslo se ne ujema", + "Geslo se ne ujema.": "Geslo se ne ujema.", + "Hrvaščina": "Hrvaščina", + "Hvala za poslano vprašanje, ki je bilo posredovano svetovalcem terminološkega portala. Odgovor boste prejeli na e-naslov, ki ste ga navedli ob registraciji.": "Hvala za poslano vprašanje, ki je bilo posredovano svetovalcem terminološkega portala. Odgovor boste prejeli na e-naslov, ki ste ga navedli ob registraciji.", + "Hvala za poslano vprašanje, ki je bilo posredovano v Terminološko svetovalnico ZRC SAZU. Odgovor boste prejeli na e-naslov, ki ste ga navedli ob registraciji.": "Hvala za poslano vprašanje, ki je bilo posredovano v Terminološko svetovalnico ZRC SAZU. Odgovor boste prejeli na e-naslov, ki ste ga navedli ob registraciji.", + "ID Slovarja": "ID Slovarja", + "ID: Ni idja": "ID: Ni idja", + "IDEKSIRAJ": "IDEKSIRAJ", + "Imate neshranjene spremebe. Ali jih želite shraniti?": "Imate neshranjene spremebe. Ali jih želite shraniti?", + "Imate neshranjene spremembe. Ali jih želite shraniti?": "Imate neshranjene spremembe. Ali jih želite shraniti?", + "Ime": "Ime", + "IME": "IME", + "IME DATOTEKE": "IME DATOTEKE", + "Ime in opis": "Ime in opis", + "IME IN PRIIMEK": "IME IN PRIIMEK", + "Ime in priimek trenutno prijavljenega uporabnika.": "Ime in priimek trenutno prijavljenega uporabnika.", + "IME LUŠČENJA": "IME LUŠČENJA", + "IME PORTALA": "IME PORTALA", + "IME PORTALA *": "IME PORTALA *", + "Ime Priimek": "Ime Priimek", + "Ime registriranega uporabnika.": "Ime registriranega uporabnika.", + "Ime slovarja": "Ime slovarja", + "Ime, s katerim se uporabnik predstavlja na terminološkem portalu.": "Ime, s katerim se uporabnik predstavlja na terminološkem portalu.", + "Indeksiranje slovarja": "Indeksiranje slovarja", + "Institucija": "Institucija", + "INSTITUCIJA": "INSTITUCIJA", + "Institucija, kjer trenutno prijavljeni uporabnik deluje.": "Institucija, kjer trenutno prijavljeni uporabnik deluje.", + "Interni": "Interni", + "Išči": "Išči", + "Išči po slovarju": "Išči po slovarju", + "ISKANI NIZ JE BIL NAJDEN": "ISKANI NIZ JE BIL NAJDEN", + "Iskanje": "Iskanje", + "Iskanje po slovarjih": "Iskanje po slovarjih", + "Iskanje po vseh": "Iskanje po vseh", + "ISSN": "ISSN", + "ISSN OZNAKA": "ISSN OZNAKA", + "ISSN oznaka.": "ISSN oznaka.", + "Italijanščina": "Italijanščina", + "IZBERI": "IZBERI", + "Izberi datoteko": "Izberi datoteko", + "IZBERI VEČ": "IZBERI VEČ", + "Izberite enega od rezultatov luščenja s seznama.": "Izberite enega od rezultatov luščenja s seznama.", + "Izberite format datoteke, v kateri je slovar shranjen na vašem računalniku.": "Izberite format datoteke, v kateri je slovar shranjen na vašem računalniku.", + "Izberite format izpisa terminološkega slovarja.": "Izberite format izpisa terminološkega slovarja.", + "Izberite luščenje": "Izberite luščenje", + "Izberite področje": "Izberite področje", + "Izberite področje iz seznama področij, da zmanjšate obseg besedil, iz katerih bo potekalo luščenje.": "Izberite področje iz seznama področij, da zmanjšate obseg besedil, iz katerih bo potekalo luščenje.", + "Izberite področje svojega terminološkega slovarja na seznamu področij.": "Izberite področje svojega terminološkega slovarja na seznamu področij.", + "Izberite slovarske podatke, ki ste jih shranili na svojem računalniku.": "Izberite slovarske podatke, ki ste jih shranili na svojem računalniku.", + "Izberite takšno ime, ki vam bo pomagalo slediti rezultatom, če boste luščenje besedil opravili večkrat.": "Izberite takšno ime, ki vam bo pomagalo slediti rezultatom, če boste luščenje besedil opravili večkrat.", + "Izberite vrsto dokumenta iz seznama, npr. članek, diplomsko delo.": "Izberite vrsto dokumenta iz seznama, npr. članek, diplomsko delo.", + "Izberite vse tuje jezike, ki jih bo terminološki slovar vseboval.": "Izberite vse tuje jezike, ki jih bo terminološki slovar vseboval.", + "Izberite, ali želite izpisati samo jezikovno pregledane slovarske sestavke.": "Izberite, ali želite izpisati samo jezikovno pregledane slovarske sestavke.", + "Izberite, ali želite izpisati samo objavljene ali tudi neobjavljene slovarske sestavke.": "Izberite, ali želite izpisati samo objavljene ali tudi neobjavljene slovarske sestavke.", + "Izberite, ali želite izpisati samo strokovno pregledane slovarske sestavke.": "Izberite, ali želite izpisati samo strokovno pregledane slovarske sestavke.", + "Izberite, ali želite izpisati vse slovarske sestavke ali samo tiste, ki so v določeni fazi urejanja.": "Izberite, ali želite izpisati vse slovarske sestavke ali samo tiste, ki so v določeni fazi urejanja.", + "Izberite, katere slovarske sestavke želite izpisati glede na njihovo veljavnost.": "Izberite, katere slovarske sestavke želite izpisati glede na njihovo veljavnost.", + "Izbrišete lahko celoten slovar z vsemi metapodatki. Dejanja ni mogoče preklicati.": "Izbrišete lahko celoten slovar z vsemi metapodatki. Dejanja ni mogoče preklicati.", + "Izbrišete lahko vse doslej obdelane slovarske sestavke in ohranite vse metapodatke o slovarju. Dejanja ni mogoče preklicati.": "Izbrišete lahko vse doslej obdelane slovarske sestavke in ohranite vse metapodatke o slovarju. Dejanja ni mogoče preklicati.", + "Izbriši": "Izbriši", + "IZBRIŠI": "IZBRIŠI", + "Izbriši obstoječe slovarske sestavke.": "Izbriši obstoječe slovarske sestavke.", + "Izbriši polje": "Izbriši polje", + "Izbriši račun": "Izbriši račun", + "Izgovor": "Izgovor", + "Izpolnite polja in na kratko opišite vsebino terminološkega slovarja.": "Izpolnite polja in na kratko opišite vsebino terminološkega slovarja.", + "Izvoz": "Izvoz", + "Izvozi": "Izvozi", + "IZVOZI": "IZVOZI", + "Izvozi termine od številke": "Izvozi termine od številke", + "je že v tabeli.": "je že v tabeli.", + "Jeziki": "Jeziki", + "Jeziki iskanja": "Jeziki iskanja", + "Jezikovni pregled": "Jezikovni pregled", + "Jezikovno pregledano": "Jezikovno pregledano", + "KANONIČNA OBLIKA": "KANONIČNA OBLIKA", + "KLJUČNE BESEDE": "KLJUČNE BESEDE", + "Ko bo luščenje uspešno zaključeno, boste na svoj elektronski naslov, ki ste ga navedli ob registraciji, prejeli obvestilo.": "Ko bo luščenje uspešno zaključeno, boste na svoj elektronski naslov, ki ste ga navedli ob registraciji, prejeli obvestilo.", + "Ko boste končali z urejanjem svojega terminološkega slovarja, lahko objavite vse slovarske sestavke, ki bodo postali vidni vsem uporabnikom. Vaše dejanje mora potrditi še administrator portala.": "Ko boste končali z urejanjem svojega terminološkega slovarja, lahko objavite vse slovarske sestavke, ki bodo postali vidni vsem uporabnikom. Vaše dejanje mora potrditi še administrator portala.", + "komentar": "komentar", + "komentarja": "komentarja", + "komentarjev": "komentarjev", + "komentarji": "komentarji", + "Komentarji": "Komentarji", + "Končan": "Končan", + "Konec": "Konec", + "Kontrola objave": "Kontrola objave", + "KONTROLA OBJAVE": "KONTROLA OBJAVE", + "Korpus OSS": "Korpus OSS", + "Lastna svetovalnica": "Lastna svetovalnica", + "Lastni dokumenti": "Lastni dokumenti", + "Lastnosti": "Lastnosti", + "LETO": "LETO", + "Luščenje": "Luščenje", + "Luščenje | dodaj opravilo": "Luščenje | dodaj opravilo", + "Luščenje iz korpusa besedil OSS": "Luščenje iz korpusa besedil OSS", + "Luščenje iz lastnih besedil": "Luščenje iz lastnih besedil", + "Luščenje iz lastnih besedil | dodaj opravilo ": "Luščenje iz lastnih besedil | dodaj opravilo ", + "Luščenje je bilo dano v fazo obdelave.": "Luščenje je bilo dano v fazo obdelave.", + "Luščenje končano": "Luščenje končano", + "Luščenje terminoloških kandidatov iz besedil, ki jih ima uporabnik shranjena pri sebi. Za boljšo učinkovitost svetujemo format .txt.": "Luščenje terminoloških kandidatov iz besedil, ki jih ima uporabnik shranjena pri sebi. Za boljšo učinkovitost svetujemo format .txt.", + "Luščenje terminoloških kandidatov iz besedil, ki jih uporabnik izbere med vsemi besedili, vključenimi v Nacionalni portal odprte znanosti.": "Luščenje terminoloških kandidatov iz besedil, ki jih uporabnik izbere med vsemi besedili, vključenimi v Nacionalni portal odprte znanosti.", + "Luščenje terminoloških kandidatov iz besedil, ki jih uporabnik namensko izbere, je najučinkovitejše. Primerno izbrano ime opravila, vam omogoča, da lahko sledite, katera luščenja ste že opravili. Ko vnesete podatke, morate vse spremembe shraniti. Zaradi omejenega prostora za shranjevanje lahko shranite največ pet zadnjih luščenj. ": "Luščenje terminoloških kandidatov iz besedil, ki jih uporabnik namensko izbere, je najučinkovitejše. Primerno izbrano ime opravila, vam omogoča, da lahko sledite, katera luščenja ste že opravili. Ko vnesete podatke, morate vse spremembe shraniti. Zaradi omejenega prostora za shranjevanje lahko shranite največ pet zadnjih luščenj. ", + "Luščenje terminoloških kandidatov iz besedil, ki so že predhodno oblikoslovno označena, nudi dobre rezultate, vendar je treba nabor besedil omejiti. Svetujemo vam, da zoožite področje in dodatno omejite izbiro s tipi besedil in časovnim razponom, v katerih so nastala. Ko vnesete podatke, morate vse spremembe shraniti. Po vnosu podatkov, s katerimi boste omejili nabor izbranih besedil, morate pritisniti gumb Najdi. Izbiro boste shranili lahko le v primeru, da ne bo število najdenih dokumentov preveliko.": "Luščenje terminoloških kandidatov iz besedil, ki so že predhodno oblikoslovno označena, nudi dobre rezultate, vendar je treba nabor besedil omejiti. Svetujemo vam, da zoožite področje in dodatno omejite izbiro s tipi besedil in časovnim razponom, v katerih so nastala. Ko vnesete podatke, morate vse spremembe shraniti. Po vnosu podatkov, s katerimi boste omejili nabor izbranih besedil, morate pritisniti gumb Najdi. Izbiro boste shranili lahko le v primeru, da ne bo število najdenih dokumentov preveliko.", + "MAKSIMALNO ŠTEVILO KOPIJ SLOVARSKIH SESTAVKOV": "MAKSIMALNO ŠTEVILO KOPIJ SLOVARSKIH SESTAVKOV", + "Meni": "Meni", + "MENI": "MENI", + "MINIMALNO ŠTEVILO SLOVARSKIH SESTAVKOV": "MINIMALNO ŠTEVILO SLOVARSKIH SESTAVKOV", + "Modul za luščenje terminoloških kandidatov iz besedil.": "Modul za luščenje terminoloških kandidatov iz besedil.", + "Modul za svetovanje pri terminoloških zagatah.": "Modul za svetovanje pri terminoloških zagatah.", + "Modul za urejanje terminoloških slovarjev.": "Modul za urejanje terminoloških slovarjev.", + "MODULI": "MODULI", + "Moj Profil": "Moj Profil", + "Moji slovarji": "Moji slovarji", + "Morebitne že obstoječe poimenovalne rešitve, če obstajajo.": "Morebitne že obstoječe poimenovalne rešitve, če obstajajo.", + "Morebitni primeri rabe termina v besedilih ali povezave do njih, če obstajajo.": "Morebitni primeri rabe termina v besedilih ali povezave do njih, če obstajajo.", + "MOŽNOST OBJAVE SLOVARSKEGA SESTAVKA MED UREJANJEM": "MOŽNOST OBJAVE SLOVARSKEGA SESTAVKA MED UREJANJEM", + "MULTIMEDIJA": "MULTIMEDIJA", + "Na kratko opišite zasnovo in namen slovarja.": "Na kratko opišite zasnovo in namen slovarja.", + "Na seznamu uporabnikov lahko določite posameznemu uporabniku dodatne vloge ali urejate njihove podatke.": "Na seznamu uporabnikov lahko določite posameznemu uporabniku dodatne vloge ali urejate njihove podatke.", + "Na tem mestu lahko dodajate svetovalce, ki so registrirani uporabniki terminološkega portala. Če boste pripisali področje, boste lahko svetovalcu dodeljevali samo vprašanja, ki sodijo na izbrano področje, vsem drugim pa vsa.": "Na tem mestu lahko dodajate svetovalce, ki so registrirani uporabniki terminološkega portala. Če boste pripisali področje, boste lahko svetovalcu dodeljevali samo vprašanja, ki sodijo na izbrano področje, vsem drugim pa vsa.", + "Na tem mestu lahko določite področne oznake, če želite posamezne termine v svojem terminološkem slovarju razvrstiti še podrobneje.": "Na tem mestu lahko določite področne oznake, če želite posamezne termine v svojem terminološkem slovarju razvrstiti še podrobneje.", + "Na tem mestu so zbrani vsi komentarji, povezani s terminološkim portalom.": "Na tem mestu so zbrani vsi komentarji, povezani s terminološkim portalom.", + "Na tem mestu so zbrani vsi komentarji, povezani z izbranim terminološkim slovarjem.": "Na tem mestu so zbrani vsi komentarji, povezani z izbranim terminološkim slovarjem.", + "na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili svoj uporabniški račun na Terminološkem portalu. Povezava za potrditev je veljavna ": "na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili svoj uporabniški račun na Terminološkem portalu. Povezava za potrditev je veljavna ", + "Na vaš elektronski naslov smo vam posredovali povezavo za ponastavitev gesla. Prosimo preverite svoj elektronski predal.": "Na vaš elektronski naslov smo vam posredovali povezavo za ponastavitev gesla. Prosimo preverite svoj elektronski predal.", + "Nabor vseh luščenj, s katerimi lahko uporabnik iz izbranih besedil pridobi sezname terminoloških kandidatov. Posamezne terminološke kandidate lahko preveri tudi v konkordančniku.": "Nabor vseh luščenj, s katerimi lahko uporabnik iz izbranih besedil pridobi sezname terminoloških kandidatov. Posamezne terminološke kandidate lahko preveri tudi v konkordančniku.", + "Nabor vseh luščenj, s pomočjo katerih uporabnik iz vhodnih besedil pridobi sezname terminoloških kandidatov in dostop do konkordančnika po teh besedilih.": "Nabor vseh luščenj, s pomočjo katerih uporabnik iz vhodnih besedil pridobi sezname terminoloških kandidatov in dostop do konkordančnika po teh besedilih.", + "Nabor vseh slovarjev, ki jih uporabnik lahko ureja kot glavni urednik ali pa ima dodeljeno pravico urejanja, pregledovanja, popravljanja ipd.": "Nabor vseh slovarjev, ki jih uporabnik lahko ureja kot glavni urednik ali pa ima dodeljeno pravico urejanja, pregledovanja, popravljanja ipd.", + "NAČIN UVOZA": "NAČIN UVOZA", + "Naglas": "Naglas", + "Najdi": "Najdi", + "Naložbo sofinancirata Republika Slovenija in Evropska unija iz Evropskega sklada za regionalni razvoj": "Naložbo sofinancirata Republika Slovenija in Evropska unija iz Evropskega sklada za regionalni razvoj", + "Napačno geslo.": "Napačno geslo.", + "Napaka": "Napaka", + "Napaka na strežniku": "Napaka na strežniku", + "NAPAKA pri iskanju": "NAPAKA pri iskanju", + "Napaka pri nalaganju datoteke": "Napaka pri nalaganju datoteke", + "NAPAKA V UVOZU": "NAPAKA V UVOZU", + "Napredno": "Napredno", + "Napredno urejanje omogoča spreminjanje večjega števila podatkov v terminološkem slovarju.": "Napredno urejanje omogoča spreminjanje večjega števila podatkov v terminološkem slovarju.", + "Naprej": "Naprej", + "Naslednje datoteke niso bile naložene zaradi navedenih razlogov:": "Naslednje datoteke niso bile naložene zaradi navedenih razlogov:", + "Naslov": "Naslov", + "NASLOV": "NASLOV", + "NASLOV SLOVARJA *": "NASLOV SLOVARJA *", + "Nastavitve": "Nastavitve", + "Nastavitve portala": "Nastavitve portala", + "Nastavitve računa": "Nastavitve računa", + "Nastavitve slovarjev": "Nastavitve slovarjev", + "Nastavitve svetovalnice": "Nastavitve svetovalnice", + "Nazaj": "Nazaj", + "Nazaj na prijavo": "Nazaj na prijavo", + "Nazaj na zadetke": "Nazaj na zadetke", + "Ne": "Ne", + "Ne izbriši": "Ne izbriši", + "Ne morete imeti več kot 5 luščenj!": "Ne morete imeti več kot 5 luščenj!", + "Ne shrani": "Ne shrani", + "nedefinirano": "nedefinirano", + "Nemško-slovenski slovar tehnike in naravoslovja": "Nemško-slovenski slovar tehnike in naravoslovja", + "Neobjavljeni": "Neobjavljeni", + "Nepravilna vrednost strani": "Nepravilna vrednost strani", + "Nepravilno uporabniško ime, elektronski naslov ali geslo.": "Nepravilno uporabniško ime, elektronski naslov ali geslo.", + "Nepregledani": "Nepregledani", + "Neveljavna e-pošta": "Neveljavna e-pošta", + "Neveljavni": "Neveljavni", + "Neveljavni elektronski naslov.": "Neveljavni elektronski naslov.", + "Ni področja": "Ni področja", + "Ni še dodanih uvozov.": "Ni še dodanih uvozov.", + "Ni še povezanih portalov": "Ni še povezanih portalov", + "Ni slovarskih sestavkov za urejanje.": "Ni slovarskih sestavkov za urejanje.", + "Ni vprašanj.": "Ni vprašanj.", + "Ni zadetkov": "Ni zadetkov", + "Nimate izbranega področja CERIF.": "Nimate izbranega področja CERIF.", + "Nimate luščenj za urejanje.": "Nimate luščenj za urejanje.", + "Nimate povezav za urejanje.": "Nimate povezav za urejanje.", + "Nimate slovarjev za urejanje.": "Nimate slovarjev za urejanje.", + "Nimate ustreznih pravic": "Nimate ustreznih pravic", + "Niste izbrali glavnega področja.": "Niste izbrali glavnega področja.", + "Niste vnesli področnih oznak.": "Niste vnesli področnih oznak.", + "Niste vpisali angleškega naslova slovarja.": "Niste vpisali angleškega naslova slovarja.", + "Niste vpisali imena": "Niste vpisali imena", + "Niste vpisali imena slovarja.": "Niste vpisali imena slovarja.", + "Niste vpisali naslova slovarja.": "Niste vpisali naslova slovarja.", + "Niste vpisali opisa terminološkega problema.": "Niste vpisali opisa terminološkega problema.", + "Niste vpisali priimka.": "Niste vpisali priimka.", + "Niste vpisali tujih jezikov.": "Niste vpisali tujih jezikov.", + "Nov": "Nov", + "Nov avtor": "Nov avtor", + "NOV AVTOR SLOVARJA": "NOV AVTOR SLOVARJA", + "Nov povezan termin.": "Nov povezan termin.", + "Nov slovar": "Nov slovar", + "NOV UPORABNIK": "NOV UPORABNIK", + "Nov video": "Nov video", + "NOV VIDEO": "NOV VIDEO", + "Nov video.": "Nov video.", + "Nov zvok": "Nov zvok", + "NOV ZVOK": "NOV ZVOK", + "Nov zvok.": "Nov zvok.", + "Nova povezava": "Nova povezava", + "Nova slika": "Nova slika", + "NOVA SLIKA": "NOVA SLIKA", + "Nova slika.": "Nova slika.", + "Novo": "Novo", + "NOVO GESLO": "NOVO GESLO", + "Novo podpodročje": "Novo podpodročje", + "NOVO PODPODROČJE": "NOVO PODPODROČJE", + "NOVO PODPODROČJE (angleško)": "NOVO PODPODROČJE (angleško)", + "Novo podpodročje (angleško).": "Novo podpodročje (angleško).", + "NOVO PODPODROČJE (slovensko)": "NOVO PODPODROČJE (slovensko)", + "Novo terminološko vprašanje": "Novo terminološko vprašanje", + "Novo vprašanje": "Novo vprašanje", + "Novo vprašanje za Terminološko svetovalnico": "Novo vprašanje za Terminološko svetovalnico", + "O slovarju": "O slovarju", + "O terminu": "O terminu", + "Objava med urejanjem": "Objava med urejanjem", + "Objava terminološkega vprašanja": "Objava terminološkega vprašanja", + "Objava vseh slovarskih sestavkov": "Objava vseh slovarskih sestavkov", + "Objavi": "Objavi", + "OBJAVI": "OBJAVI", + "Objavljeni": "Objavljeni", + "Objavljeno": "Objavljeno", + "Oblike": "Oblike", + "Oblike, naglasi, izgovor": "Oblike, naglasi, izgovor", + "Obljavljeno": "Obljavljeno", + "Obnovi": "Obnovi", + "Obstoječe poimenovalne rešitve": "Obstoječe poimenovalne rešitve", + "OBSTOJEČE POIMENOVALNE REŠITVE": "OBSTOJEČE POIMENOVALNE REŠITVE", + "Obvestilo": "Obvestilo", + "Obvestilo o številu gesel": "Obvestilo o številu gesel", + "Odgovor": "Odgovor", + "Odgovori": "Odgovori", + "Odgovori na vprašanja:": "Odgovori na vprašanja:", + "Odjava": "Odjava", + "odprt": "odprt", + "Odprt": "Odprt", + "Odstrani": "Odstrani", + "Omogočeno": "Omogočeno", + "OPIS PORTALA": "OPIS PORTALA", + "OPIS SLOVARJA": "OPIS SLOVARJA", + "Opis terminološkega problema": "Opis terminološkega problema", + "OPIS TERMINOLOŠKEGA PROBLEMA": "OPIS TERMINOLOŠKEGA PROBLEMA", + "Opis terminološkega problema:": "Opis terminološkega problema:", + "Opis terminološkega problema: ": "Opis terminološkega problema: ", + "Osnovne nastavitve": "Osnovne nastavitve", + "Osnovni podatki": "Osnovni podatki", + "Ožji": "Ožji", + "Oznaka portala": "Oznaka portala", + "OZNAKA PORTALA *": "OZNAKA PORTALA *", + "Po koncu urejanja vseh slovarskih sestavkov lahko slovar objavite in ga tako prikažete na javnem delu terminološkega portala.": "Po koncu urejanja vseh slovarskih sestavkov lahko slovar objavite in ga tako prikažete na javnem delu terminološkega portala.", + "Počisti filtre": "Počisti filtre", + "POČISTI FILTRE": "POČISTI FILTRE", + "Podnaslov": "Podnaslov", + "Podpodročja": "Podpodročja", + "Podpodročja so namenjena podrobnejšemu razvrščanju terminoloških slovarjev. Administrator mora vsa novo predlagana podpodročja potrditi, preden jih lahko na seznamu vidijo tudi drugi uporabniki portala.": "Podpodročja so namenjena podrobnejšemu razvrščanju terminoloških slovarjev. Administrator mora vsa novo predlagana podpodročja potrditi, preden jih lahko na seznamu vidijo tudi drugi uporabniki portala.", + "Podpodročje": "Podpodročje", + "PODPODROČJE": "PODPODROČJE", + "Podpodročje.": "Podpodročje.", + "Področja": "Področja", + "Področje": "Področje", + "PODROČJE": "PODROČJE", + "PODROČJE *": "PODROČJE *", + "Področje, v katero sodi opisani terminološki problem.": "Področje, v katero sodi opisani terminološki problem.", + "Področje.": "Področje.", + "Področna oznaka": "Področna oznaka", + "PODROČNA OZNAKA": "PODROČNA OZNAKA", + "PODROČNA OZNAKA:": "PODROČNA OZNAKA:", + "Področne oznake": "Področne oznake", + "PODROČNE OZNAKE": "PODROČNE OZNAKE", + "Podvoji": "Podvoji", + "PODVOJI": "PODVOJI", + "Poiščite naslove terminoloških slovarjev, s katerimi želite dopolniti iskalne zadetke na svojem portalu. Lahko izberete vse slovarje ali le nekatere. Svoje izbire morate shraniti. Vse vaše izbire mora potrditi administrator povezanega portala.": "Poiščite naslove terminoloških slovarjev, s katerimi želite dopolniti iskalne zadetke na svojem portalu. Lahko izberete vse slovarje ali le nekatere. Svoje izbire morate shraniti. Vse vaše izbire mora potrditi administrator povezanega portala.", + "Pojasnilo": "Pojasnilo", + "POJASNILO": "POJASNILO", + "POJASNILO:": "POJASNILO:", + "POJAVITVE": "POJAVITVE", + "Poleg objave slovarskega sestavka v fazi \"Urejeno\" je omogočena tudi objava v fazi \"V urejanju\".": "Poleg objave slovarskega sestavka v fazi \"Urejeno\" je omogočena tudi objava v fazi \"V urejanju\".", + "Polja naslov, vprašanje in mnenje so obvezna!": "Polja naslov, vprašanje in mnenje so obvezna!", + "Polno ime terminološkega portala.": "Polno ime terminološkega portala.", + "Pomoč": "Pomoč", + "Ponastavi geslo": "Ponastavi geslo", + "Ponovi Geslo": "Ponovi Geslo", + "PONOVI GESLO": "PONOVI GESLO", + "PONOVI NOVO GESLO": "PONOVI NOVO GESLO", + "Portal": "Portal", + "Portal lahko povežete s Terminološko svetovalnico ZRC SAZU in tako prikazujete terminološke odgovore na svojem portalu, lahko pa vklopite lastno svetovalnico. V tem primeru morate med registriranimi uporabniki izbrati svetovalce in urednika svetovalnice, ki bodo odgovarjali na terminološka vprašanja uporabnikov.": "Portal lahko povežete s Terminološko svetovalnico ZRC SAZU in tako prikazujete terminološke odgovore na svojem portalu, lahko pa vklopite lastno svetovalnico. V tem primeru morate med registriranimi uporabniki izbrati svetovalce in urednika svetovalnice, ki bodo odgovarjali na terminološka vprašanja uporabnikov.", + "Portali": "Portali", + "Pošlji": "Pošlji", + "Potrdi": "Potrdi", + "POTRDI": "POTRDI", + "Potrditev objave": "Potrditev objave", + "Potrjen": "Potrjen", + "Povezani slovarji": "Povezani slovarji", + "Povezani termin": "Povezani termin", + "POVEZANI TERMIN": "POVEZANI TERMIN", + "POVEZANI TERMIN:": "POVEZANI TERMIN:", + "POVEZAVA": "POVEZAVA", + "Povezava ni (več) veljavna. Prosimo, da ponovno zahtevate ponastavitev gesla.": "Povezava ni (več) veljavna. Prosimo, da ponovno zahtevate ponastavitev gesla.", + "Povezave": "Povezave", + "Povezave s portali": "Povezave s portali", + "Pozabljeno geslo": "Pozabljeno geslo", + "Pozdravljeni ": "Pozdravljeni ", + "Pozor": "Pozor", + "Prazno obvezno polje": "Prazno obvezno polje", + "Preden naložite besedila, jih shranite v besedilni obliki (končnica .txt).": "Preden naložite besedila, jih shranite v besedilni obliki (končnica .txt).", + "Predlog": "Predlog", + "Predogled": "Predogled", + "Pregledani": "Pregledani", + "Prekinjen": "Prekinjen", + "Prekliči": "Prekliči", + "Preverite, če ste pravilno vpisali uporabniško ime. Bodite pozorni na velike in male črke.": "Preverite, če ste pravilno vpisali uporabniško ime. Bodite pozorni na velike in male črke.", + "Prevod": "Prevod", + "Pri brisanju datoteke je prišlo do napake.": "Pri brisanju datoteke je prišlo do napake.", + "Priimek": "Priimek", + "PRIIMEK": "PRIIMEK", + "Priimek registriranega uporabnika.": "Priimek registriranega uporabnika.", + "Prijava": "Prijava", + "PRIJAVA": "PRIJAVA", + "Prijava uspešna": "Prijava uspešna", + "Prikaži datume": "Prikaži datume", + "Prikaži filtre": "Prikaži filtre", + "PRIKAŽI VSE": "PRIKAŽI VSE", + "Primeri rabe": "Primeri rabe", + "PRIMERI RABE V BESEDILIH": "PRIMERI RABE V BESEDILIH", + "Pripravljeno": "Pripravljeno", + "Prišlo je do napake pri pošiljanju sporočila na vaš elektronski naslov. Poskusite ponovno.": "Prišlo je do napake pri pošiljanju sporočila na vaš elektronski naslov. Poskusite ponovno.", + "Prišlo je do napake.": "Prišlo je do napake.", + "Prišlo je do strežniške napake. Poskusite kasneje.": "Prišlo je do strežniške napake. Poskusite kasneje.", + "Prosim, ponovno vnesite geslo": "Prosim, ponovno vnesite geslo", + "Prosim, vnesite geslo": "Prosim, vnesite geslo", + "RAČUN": "RAČUN", + "Razumem": "Razumem", + "registracija": "registracija", + "Registracija": "Registracija", + "Registracija uporabnika je potrjena.": "Registracija uporabnika je potrjena.", + "Registracija uspešna": "Registracija uspešna", + "Registrirani uporabniki lahko dobijo različne vloge na portalu. Kot administrator jim lahko dodelite tudi vlogo skrbnika slovarjev in/ali skrbnika svetovalnice.": "Registrirani uporabniki lahko dobijo različne vloge na portalu. Kot administrator jim lahko dodelite tudi vlogo skrbnika slovarjev in/ali skrbnika svetovalnice.", + "REPUBLIKA SLOVENIJA MINISTRSTVO ZA KULTURO": "REPUBLIKA SLOVENIJA MINISTRSTVO ZA KULTURO", + "rezultat": "rezultat", + "Rezultat luščenja so terminološki kandidati. Z uvozom rezultatov posameznega luščenja lahko dopolnite geslovnik terminološkega slovarja, ki ga urejate.": "Rezultat luščenja so terminološki kandidati. Z uvozom rezultatov posameznega luščenja lahko dopolnite geslovnik terminološkega slovarja, ki ga urejate.", + "rezultata": "rezultata", + "rezultati": "rezultati", + "Rezultati: ": "Rezultati: ", + "rezultatov": "rezultatov", + "S ključnimi besedami, ki so vključene v večino znanstvenih in strokovnih, lahko bolj natančno izberete besedila, ki jih želite uporabiti za luščenje terminoloških kandidatov.": "S ključnimi besedami, ki so vključene v večino znanstvenih in strokovnih, lahko bolj natančno izberete besedila, ki jih želite uporabiti za luščenje terminoloških kandidatov.", + "S prijavo pridobite možnost uporabe vseh funkcij terminološkega portala.": "S prijavo pridobite možnost uporabe vseh funkcij terminološkega portala.", + "S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju in vse metapodatke. Dejanja ni mogoče razveljaviti.": "S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju in vse metapodatke. Dejanja ni mogoče razveljaviti.", + "S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju. Metapodatki bodo ostali. Dejanja ni mogoče razveljaviti.": "S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju. Metapodatki bodo ostali. Dejanja ni mogoče razveljaviti.", + "Še niste registriran uporabnik, registrirajte se.": "Še niste registriran uporabnik, registrirajte se.", + "Settings": "Settings", + "Seznam": "Seznam", + "Seznam luščenj": "Seznam luščenj", + "Seznam neželenih terminov": "Seznam neželenih terminov", + "Seznam objavljenih vprašanj.": "Seznam objavljenih vprašanj.", + "Seznam povezav": "Seznam povezav", + "Seznam slovarjev": "Seznam slovarjev", + "Seznam slovarjev s povezanega portala.": "Seznam slovarjev s povezanega portala.", + "Seznam svetovalcev": "Seznam svetovalcev", + "Seznam terminoloških kandidatov": "Seznam terminoloških kandidatov", + "Seznam terminoloških kandidatov bo natančnejši, če boste dodali tudi seznam neželenih besed, ki naj jih luščilnik izloči iz seznama. Seznam lahko vsebuje splošne termine, npr. tabela, kazalnik, ali pogoste slovnične besede, zlasti veznike, pomožne glagole.": "Seznam terminoloških kandidatov bo natančnejši, če boste dodali tudi seznam neželenih besed, ki naj jih luščilnik izloči iz seznama. Seznam lahko vsebuje splošne termine, npr. tabela, kazalnik, ali pogoste slovnične besede, zlasti veznike, pomožne glagole.", + "Seznam terminoloških kandidatov, ki so rezultat izbranega luščenja.": "Seznam terminoloških kandidatov, ki so rezultat izbranega luščenja.", + "Seznam uporabnikov": "Seznam uporabnikov", + "Seznam urejenih vprašanj, ki čakajo na potrditev dokončne objave.": "Seznam urejenih vprašanj, ki čakajo na potrditev dokončne objave.", + "Seznam vprašanj, ki so jih moderatorji zavrnili in jih je treba dodeliti nekomu drugemu.": "Seznam vprašanj, ki so jih moderatorji zavrnili in jih je treba dodeliti nekomu drugemu.", + "Seznam vprašanj, ki so jih poslali uporabniki, in še niso bila dodeljena moderatorjem.": "Seznam vprašanj, ki so jih poslali uporabniki, in še niso bila dodeljena moderatorjem.", + "Seznam vprašanj, ki so še v urejanju.": "Seznam vprašanj, ki so še v urejanju.", + "Seznam vseh slovarjev, ki jih uredniki, sicer registrirani uporabniki, urejajo na tem portalu.": "Seznam vseh slovarjev, ki jih uredniki, sicer registrirani uporabniki, urejajo na tem portalu.", + "Seznam vseh slovarjev, ki so na tem portalu na voljo uporabnikom.": "Seznam vseh slovarjev, ki so na tem portalu na voljo uporabnikom.", + "Seznam zadetkov": "Seznam zadetkov", + "Shemo za format xml si prenesete tukaj.": "Shemo za format xml si prenesete tukaj.", + "Shrani": "Shrani", + "Shranjeno": "Shranjeno", + "Shranjujem ...": "Shranjujem ...", + "Sinhroniziraj": "Sinhroniziraj", + "Sinonim": "Sinonim", + "SINONIM:": "SINONIM:", + "Sinonimi": "Sinonimi", + "SINONIMI": "SINONIMI", + "Širši": "Širši", + "SKRAJŠANI NASLOV SLOVARJA": "SKRAJŠANI NASLOV SLOVARJA", + "Skrbnik portala": "Skrbnik portala", + "Skrbnik slovarjev": "Skrbnik slovarjev", + "Skrbnik svetovalnice": "Skrbnik svetovalnice", + "Skrbniki portala": "Skrbniki portala", + "slika": "slika", + "Slika": "Slika", + "SLIKA": "SLIKA", + "SLIKA:": "SLIKA:", + "Slovar": "Slovar", + "Slovar je bil izbrisan.": "Slovar je bil izbrisan.", + "Slovar je odprt.": "Slovar je odprt.", + "Slovar je pregledal jezikoslovec.": "Slovar je pregledal jezikoslovec.", + "Slovar je pregledal področni strokovnjak.": "Slovar je pregledal področni strokovnjak.", + "Slovar je v odpiranju - čaka na potrditev skrbnika slovarjev": "Slovar je v odpiranju - čaka na potrditev skrbnika slovarjev", + "Slovar odprt": "Slovar odprt", + "SLOVAR UVOŽEN": "SLOVAR UVOŽEN", + "Slovarji": "Slovarji", + "Slovarji portala": "Slovarji portala", + "Slovarski sestavek je neveljaven": "Slovarski sestavek je neveljaven", + "Slovarski sestavek je pregledan in dokončan.": "Slovarski sestavek je pregledan in dokončan.", + "Slovarski sestavek je v fazi, ko se dodajajo in oblikujejo elementi, ki ste jih izbrali v strukturi slovarskega sestavka.": "Slovarski sestavek je v fazi, ko se dodajajo in oblikujejo elementi, ki ste jih izbrali v strukturi slovarskega sestavka.", + "Slovarski sestavki so bili izbrisani.": "Slovarski sestavki so bili izbrisani.", + "Slovarski sestavki so bili objavljeni.": "Slovarski sestavki so bili objavljeni.", + "SLOVARSKIH SESTAVKOV": "SLOVARSKIH SESTAVKOV", + "Slovenščina": "Slovenščina", + "Soglašam s politiko zasebnosti": "Soglašam s politiko zasebnosti", + "Sorodni": "Sorodni", + "Sprememba stanja slovarja": "Sprememba stanja slovarja", + "Spremeni geslo": "Spremeni geslo", + "SPREMENJEN": "SPREMENJEN", + "ŠT. GESEL": "ŠT. GESEL", + "STARO GESLO": "STARO GESLO", + "Statistika": "Statistika", + "Status": "Status", + "STATUS": "STATUS", + "Ste pozabili geslo? Napišite svoje uporabniško ime ali elektronski naslov, ki ste ga uporabili ob registraciji. Na ta naslov vam bomo poslali sporočilo, s pomočjo katerega boste lahko vnesli novo geslo.": "Ste pozabili geslo? Napišite svoje uporabniško ime ali elektronski naslov, ki ste ga uporabili ob registraciji. Na ta naslov vam bomo poslali sporočilo, s pomočjo katerega boste lahko vnesli novo geslo.", + "Število dokumentov": "Število dokumentov", + "Število slovarskih sestavkov je manjše od zahtevanega števila - povečajte število slovarskih sestavkov, sicer ga skrbnik slovarjev lahko zapre": "Število slovarskih sestavkov je manjše od zahtevanega števila - povečajte število slovarskih sestavkov, sicer ga skrbnik slovarjev lahko zapre", + "Število slovarskih sestavkov, ki jih mora vsebovati slovar, da je omogočena objava slovarja na portalu.": "Število slovarskih sestavkov, ki jih mora vsebovati slovar, da je omogočena objava slovarja na portalu.", + "Število terminov": "Število terminov", + "Število vprašanj:": "Število vprašanj:", + "ŠTEVILO ZADETKOV NA STRANI": "ŠTEVILO ZADETKOV NA STRANI", + "Število zadnjih kopij spremenjenih slovarskih sestavkov, ki se hranijo. Če vrednost ni določena, omejitve ni.": "Število zadnjih kopij spremenjenih slovarskih sestavkov, ki se hranijo. Če vrednost ni določena, omejitve ni.", + "Stop termini": "Stop termini", + "Stran ne obstaja": "Stran ne obstaja", + "Strežnik ni dosegljiv. Poskusite kasneje.": "Strežnik ni dosegljiv. Poskusite kasneje.", + "Strinjam se s pogoji uporabe": "Strinjam se s pogoji uporabe", + "Strokovni pregled": "Strokovni pregled", + "Strokovno pregledano": "Strokovno pregledano", + "Struktura": "Struktura", + "STRUKTURA - ELEMENTI SLOVARSKEGA SESTAVKA:": "STRUKTURA - ELEMENTI SLOVARSKEGA SESTAVKA:", + "Struktura slovarskega sestavka": "Struktura slovarskega sestavka", + "Svetovalci": "Svetovalci", + "Svetovalec": "Svetovalec", + "Svetovalnica": "Svetovalnica", + "Svetovanje": "Svetovanje", + "Svoj terminološki portal lahko povežete še z drugimi terminološkimi portali in med iskalnimi prikazujete tudi njihove zadetke.": "Svoj terminološki portal lahko povežete še z drugimi terminološkimi portali in med iskalnimi prikazujete tudi njihove zadetke.", + "Ta slovar nima opisa": "Ta slovar nima opisa", + "Termin": "Termin", + "TERMIN": "TERMIN", + "TERMIN:": "TERMIN:", + "TERMINI": "TERMINI", + "Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj.": "Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj.", + "Terminološka svetovalnica ZRC SAZU": "Terminološka svetovalnica ZRC SAZU", + "Terminološke slovarje lahko v celoti ali po izbranih kriterijih izvozite v različnih formatih in shranite na svojem računalniku.": "Terminološke slovarje lahko v celoti ali po izbranih kriterijih izvozite v različnih formatih in shranite na svojem računalniku.", + "Terminološkemu slovarju določite status. Izbirate lahko med zaprt, v urejanju in odprt.": "Terminološkemu slovarju določite status. Izbirate lahko med zaprt, v urejanju in odprt.", + "Terminološki kandidati": "Terminološki kandidati", + "Terminološko svetovanje": "Terminološko svetovanje", + "Tip napake 1 (ni slovenskega termina)": "Tip napake 1 (ni slovenskega termina)", + "Tip napake 2 (ni definicije ali tujejezičnega termina)": "Tip napake 2 (ni definicije ali tujejezičnega termina)", + "Tu so zbrani vsi komentarji, povezani z vašim slovarjem.": "Tu so zbrani vsi komentarji, povezani z vašim slovarjem.", + "Tuj sinonim": "Tuj sinonim", + "Tuj termin": "Tuj termin", + "Tuja definicija": "Tuja definicija", + "Tuji jezik": "Tuji jezik", + "TUJI JEZIKI": "TUJI JEZIKI", + "Tuji slovarji": "Tuji slovarji", + "TUJI TERMIN": "TUJI TERMIN", + "Tukaj lahko izbrišete svoj račun.": "Tukaj lahko izbrišete svoj račun.", + "Tukaj lahko ponovno ideksirate slovar.": "Tukaj lahko ponovno ideksirate slovar.", + "Tukaj lahko spremenite geslo za prijavo.": "Tukaj lahko spremenite geslo za prijavo.", + "Tukaj lahko spremenite ime, priimek in elektronski naslov uporabnika.": "Tukaj lahko spremenite ime, priimek in elektronski naslov uporabnika.", + "Tukaj lahko spremenite nekatere nastavitve, vezane na posameznega uporabnika.": "Tukaj lahko spremenite nekatere nastavitve, vezane na posameznega uporabnika.", + "Unikatna oznaka povezanega terminološkega portala za prikaz na vašem portalu, sestavljena iz dveh črk ali črke in številke.": "Unikatna oznaka povezanega terminološkega portala za prikaz na vašem portalu, sestavljena iz dveh črk ali črke in številke.", + "Uporabi": "Uporabi", + "Uporabnik": "Uporabnik", + "UPORABNIK": "UPORABNIK", + "Uporabnik na tem mestu izdela specializirani korpus iz besedil, ki jih je zbral in shranil sam. Besedila naj bodo izbrana po načelih tvorjenja specializiranih korpusov. Za uspešno luščenje priporočamo najmanj 10 besedil. Vsa besedila naj bodo shranjena v besedilnem formatu (.txt). Luščenje podpira tudi formate .docx in .pdf, vendar so rezultati slabši.": "Uporabnik na tem mestu izdela specializirani korpus iz besedil, ki jih je zbral in shranil sam. Besedila naj bodo izbrana po načelih tvorjenja specializiranih korpusov. Za uspešno luščenje priporočamo najmanj 10 besedil. Vsa besedila naj bodo shranjena v besedilnem formatu (.txt). Luščenje podpira tudi formate .docx in .pdf, vendar so rezultati slabši.", + "Uporabniki": "Uporabniki", + "Uporabnikovi dokumenti": "Uporabnikovi dokumenti", + "Uporabniške pravice/vloge": "Uporabniške pravice/vloge", + "Uporabniške vloge": "Uporabniške vloge", + "Uporabniški korpus": "Uporabniški korpus", + "Uporabniški račun še ni aktiviran. Kliknite aktivacijsko povezavo, katero smo vam poslali po elektronski pošti.": "Uporabniški račun še ni aktiviran. Kliknite aktivacijsko povezavo, katero smo vam poslali po elektronski pošti.", + "Uporabniško ime": "Uporabniško ime", + "UPORABNIŠKO IME": "UPORABNIŠKO IME", + "Uporabniško ime ali elektronski naslov": "Uporabniško ime ali elektronski naslov", + "Uporabniško ime že obstaja": "Uporabniško ime že obstaja", + "Uredi": "Uredi", + "UREDI": "UREDI", + "Uredi lastnosti": "Uredi lastnosti", + "Uredi povezavo": "Uredi povezavo", + "Uredi vsebino": "Uredi vsebino", + "Urednik slovarja": "Urednik slovarja", + "Urejanje": "Urejanje", + "Urejanje terminološkega odgovora": "Urejanje terminološkega odgovora", + "Urejeni": "Urejeni", + "Urejeno": "Urejeno", + "URL naslov terminološkega portala, s katerim boste sinhronizirali podatke iz terminoloških virov.": "URL naslov terminološkega portala, s katerim boste sinhronizirali podatke iz terminoloških virov.", + "URL naslov terminološkega portala, s katerim želimo povezati svoj portal.": "URL naslov terminološkega portala, s katerim želimo povezati svoj portal.", + "URL naslov terminološkega portala, s katerim želimo povezati svoj portal. (API klic)": "URL naslov terminološkega portala, s katerim želimo povezati svoj portal. (API klic)", + "URL naslov terminološkega portala, s katerim želite povezati svoj portal. (API klic)": "URL naslov terminološkega portala, s katerim želite povezati svoj portal. (API klic)", + "URL povezava do mnenja:": "URL povezava do mnenja:", + "URL za sinhronizacijo slovarjev *": "URL za sinhronizacijo slovarjev *", + "URL za sinhronizacijo slovarskih sestavkov *": "URL za sinhronizacijo slovarskih sestavkov *", + "Uspešno ste ponastavili svoje geslo.": "Uspešno ste ponastavili svoje geslo.", + "Ustvari": "Ustvari", + "USTVARJEN": "USTVARJEN", + "Ustvarjeno novo vprašanje v svetovalnici": "Ustvarjeno novo vprašanje v svetovalnici", + "UTEŽ": "UTEŽ", + "Uvoz": "Uvoz", + "Uvoz iz datoteke": "Uvoz iz datoteke", + "Uvoz iz luščilnika": "Uvoz iz luščilnika", + "UVOZI": "UVOZI", + "Uvozi termine od številke": "Uvozi termine od številke", + "V delu": "V delu", + "V DRUGI VSEBINI": "V DRUGI VSEBINI", + "V IZTOČNICAH": "V IZTOČNICAH", + "V obdelavi": "V obdelavi", + "V odpiranju": "V odpiranju", + "v predogledu": "v predogledu", + "V slovarju so samo terminološki kandidati. Slovarja ne morete objaviti.": "V slovarju so samo terminološki kandidati. Slovarja ne morete objaviti.", + "V tem razdelku lahko določite elemente slovarskega sestavka. Z izbiro elementov se vam prikazuje podoba slovarskega sestavka. Izbiro lahko tudi med urejanjem vsebine kadarkoli spremenite.": "V tem razdelku lahko določite elemente slovarskega sestavka. Z izbiro elementov se vam prikazuje podoba slovarskega sestavka. Izbiro lahko tudi med urejanjem vsebine kadarkoli spremenite.", + "V tem razdelku lahko določite glavne administratorske pravice na terminološkem portalu.": "V tem razdelku lahko določite glavne administratorske pravice na terminološkem portalu.", + "V tem razdelku lahko določite uporabniške vloge posameznega uporabnika in urejate njegove podatke.": "V tem razdelku lahko določite uporabniške vloge posameznega uporabnika in urejate njegove podatke.", + "V TUJEJEZIČNIH USTREZNIKIH": "V TUJEJEZIČNIH USTREZNIKIH", + "V urejanju": "V urejanju", + "Vaš komentar je brez vsebine.": "Vaš komentar je brez vsebine.", + "Vaše iskanje ni bilo uspešno. Vpišite novo iskalno poizvedbo in poizkusite znova.": "Vaše iskanje ni bilo uspešno. Vpišite novo iskalno poizvedbo in poizkusite znova.", + "Več ...": "Več ...", + "Več …": "Več …", + "Več...": "Več...", + "VELIKOST": "VELIKOST", + "Veljavni": "Veljavni", + "Veljavni elektronski naslov uporabnika, na katerega uporabnik prejema sporočila, povezana s portalom.": "Veljavni elektronski naslov uporabnika, na katerega uporabnik prejema sporočila, povezana s portalom.", + "Veljavno": "Veljavno", + "Veljavnost": "Veljavnost", + "Verzija": "Verzija", + "Verzija 1": "Verzija 1", + "Verzija:": "Verzija:", + "video": "video", + "Video": "Video", + "VIDEO": "VIDEO", + "VIDEO:": "VIDEO:", + "Vidno": "Vidno", + "Vir": "Vir", + "Viri": "Viri", + "Vnesite e-naslov, ki ga za obveščanje o novih terminoloških vprašanjih uporabljajo terminološki svetovalci.": "Vnesite e-naslov, ki ga za obveščanje o novih terminoloških vprašanjih uporabljajo terminološki svetovalci.", + "Vnesite naslov spletnega mesta, kjer so zbrani vsi odgovori terminološke svetovalnice.": "Vnesite naslov spletnega mesta, kjer so zbrani vsi odgovori terminološke svetovalnice.", + "Vnesite novo geslo. Ko ga potrdite, se boste v vaš uporabniški račun lahko spet prijavili z novim geslom.": "Vnesite novo geslo. Ko ga potrdite, se boste v vaš uporabniški račun lahko spet prijavili z novim geslom.", + "Vnesite podatke terminološkega portala, s katerim želite povezati svoj portal.": "Vnesite podatke terminološkega portala, s katerim želite povezati svoj portal.", + "Vnesite podatke, ki niso sistemsko vključeni v druga polja, npr. vir, zgled rabe.": "Vnesite podatke, ki niso sistemsko vključeni v druga polja, npr. vir, zgled rabe.", + "Vnesite polno ime terminološkega portala, s katerim se povezujete.": "Vnesite polno ime terminološkega portala, s katerim se povezujete.", + "Vnesite termin.": "Vnesite termin.", + "Vnesti morate termin": "Vnesti morate termin", + "Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime.": "Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime.", + "Vpišite definicijo v tujem jeziku.": "Vpišite definicijo v tujem jeziku.", + "Vpišite iskalni niz": "Vpišite iskalni niz", + "Vpišite novo podpodročje. Na seznamu podpodročij bo vidno takoj po potrditvi administratorja portala.": "Vpišite novo podpodročje. Na seznamu podpodročij bo vidno takoj po potrditvi administratorja portala.", + "Vpišite podpodročje glavnega področja, ki ste ga izbrali. Če podpodročja ni med naborom, izberite polje Novo podpodročje.": "Vpišite podpodročje glavnega področja, ki ste ga izbrali. Če podpodročja ni med naborom, izberite polje Novo podpodročje.", + "Vprašanje poslano:": "Vprašanje poslano:", + "VRSTA": "VRSTA", + "VRSTA DOKUMENTA": "VRSTA DOKUMENTA", + "Vsebina": "Vsebina", + "Vsebina slovarja": "Vsebina slovarja", + "Vsebina slovarskega sestavka je bila duplicirana.": "Vsebina slovarskega sestavka je bila duplicirana.", + "Vsi povezani slovarji": "Vsi povezani slovarji", + "Vsi slovarji": "Vsi slovarji", + "Vsi slovarski sestavki": "Vsi slovarski sestavki", + "Vstavite definicijo pojma.": "Vstavite definicijo pojma.", + "Vstavite termine, ki se za definirani pojem tudi uporabljajo.": "Vstavite termine, ki se za definirani pojem tudi uporabljajo.", + "Vstavite ustrezno področno oznako, ki jo želite določiti za posamezni termin.": "Vstavite ustrezno področno oznako, ki jo želite določiti za posamezni termin.", + "Z izbiro te možnosti boste pobrisali samo slovarske sestavke, ki so v določeni fazi urejanja.": "Z izbiro te možnosti boste pobrisali samo slovarske sestavke, ki so v določeni fazi urejanja.", + "Z registracijo pridobite možnost uporabe vseh funkcij terminološkega portala.": "Z registracijo pridobite možnost uporabe vseh funkcij terminološkega portala.", + "Za luščenje terminoloških kandidatov morate biti prijavljeni.": "Za luščenje terminoloških kandidatov morate biti prijavljeni.", + "Za prvo objavo slovarja je potrebno dovoljenje skrbnika slovarjev.": "Za prvo objavo slovarja je potrebno dovoljenje skrbnika slovarjev.", + "Za to besedo še ni podatkov.": "Za to besedo še ni podatkov.", + "Za urejanje slovarjev morate biti prijavljeni.": "Za urejanje slovarjev morate biti prijavljeni.", + "Za zastavljanje terminoloških vprašanj morate biti prijavljeni.": "Za zastavljanje terminoloških vprašanj morate biti prijavljeni.", + "Začetek": "Začetek", + "Začni": "Začni", + "ZAČNI": "ZAČNI", + "Zadnja sprememba": "Zadnja sprememba", + "Zadnji izvozi": "Zadnji izvozi", + "Zadnji objavljeni slovarji": "Zadnji objavljeni slovarji", + "Zadnji odgovori na vprašanja": "Zadnji odgovori na vprašanja", + "Zadnji uvozi": "Zadnji uvozi", + "Zapomni si prijavo": "Zapomni si prijavo", + "Zaporedje:": "Zaporedje:", + "Zapri": "Zapri", + "ZAPRI": "ZAPRI", + "zaprt": "zaprt", + "Zaprt": "Zaprt", + "Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesede\tnje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki.": "Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesede\tnje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki.", + "Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesedenje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki": "Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesedenje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki", + "Zastavi novo vprašanje": "Zastavi novo vprašanje", + "Zastavi terminološko vprašanje": "Zastavi terminološko vprašanje", + "ZAVRJEN": "ZAVRJEN", + "ZAVRNI": "ZAVRNI", + "Zavrnjeno": "Zavrnjeno", + "Zunanji": "Zunanji", + "zvok": "zvok", + "Zvok": "Zvok", + "ZVOK": "ZVOK", + "ZVOK:": "ZVOK:", + "titleTermsOfUse": "Pogoji uporabe", + "titlePrivacyPolicy": "Politika zasebnosti" +} diff --git a/express/public/locales/sl/extended.json b/express/public/locales/sl/extended.json new file mode 100644 index 0000000..32921f8 --- /dev/null +++ b/express/public/locales/sl/extended.json @@ -0,0 +1,50 @@ +{ + "helpPageTitleHelp": "Pomoč", + "helpPageTitleAbout": "Splošno o portalu", + "helpPageTitleRegistration": "Registracija", + "helpPageTitleSearchIndex": "Iskanje", + "helpPageTitleSearchBasic": "Osnovno iskanje", + "helpPageTitleSearchAdvanced": "Napredno iskanje", + "helpPageTitleExtractionIndex": "Luščenje", + "helpPageTitleExtractionSpecializedCorpora": "Specializirani korpusi", + "helpPageTitleExtractionPersonalCorpus": "Lastni uporabniški korpus", + "helpPageTitleExtractionOssCorpus": "Korpus OSS", + "helpPageTitleExtractionStopTerms": "Seznami neželenih besed ali \"Stop termini\"", + "helpPageTitleExtractionDomains": "Področja", + "helpPageTitleExtractionTermCandidates": "Terminološki kandidati", + "helpPageTitleEditingIndex": "Urejanje", + "helpPageTitleEditingNewDictionary": "Nov slovar", + "helpPageTitleEditingDictionaryProperties": "Urejanje lastnosti slovarja", + "helpPageTitleEditingUsers": "Uporabniki", + "helpPageTitleEditingStructure": "Struktura slovarskega sestavka", + "helpPageTitleEditingComments": "Komentiranje", + "helpPageTitleEditingDictionaryContent": "Vsebina slovarja", + "helpPageTitleEditingImportingData": "Uvoz podatkov", + "helpPageTitleEditingEntriesIndex": "Dodajanje novih slovarskih sestavkov", + "helpPageTitleEditingEntriesDomainLabels": "Področne oznake", + "helpPageTitleEditingEntriesLabel": "Pojasnilo", + "helpPageTitleEditingEntriesDefinition": "Definicja", + "helpPageTitleEditingEntriesSynonyms": "Sinonimi", + "helpPageTitleEditingEntriesRelatedTerms": "Povezani termini", + "helpPageTitleEditingEntriesOtherLanguages": "Tuji jeziki", + "helpPageTitleEditingEntriesOther": "Drugo", + "helpPageTitleEditingEntriesEditingHistory": "Zgodovina urejanja", + "helpPageTitleEditingEntriesComments": "Komentarji", + "helpPageTitleEditingEntriesDataExport": "Izvoz podatkov", + "helpPageTitleConsultancyIndex": "Svetovanje", + "helpPageTitleConsultancyQuestions": "Pošiljanje vprašanj", + "helpPageTitleConsultancyTerminologyPrinciples": "Terminološka načela", + "helpPageTitleConsultancyLink": "Povezava", + "helpPageTitleConsultancyAnswers": "Objava odgovorov", + "helpPageTitleAdministrationIndex": "Administracija", + "helpPageTitleAdministrationBasicSettingsIndex": "Osnovne nastavitve", + "helpPageTitleAdministrationBasicSettingsPortal": "Portal", + "helpPageTitleAdministrationBasicSettingsDictionaries": "Slovarji", + "helpPageTitleAdministrationBasicSettingsConsultancy": "Svetovalnica", + "helpPageTitleAdministrationLinksToOtherPortalsIndex": "Povezave s portali", + "helpPageTitleAdministrationLinksToOtherPortalsList": "Seznam", + "helpPageTitleAdministrationLinksToOtherPortalsDictionaries": "Slovarji", + "helpPageTitleAdministrationDictionaries": "Slovarji", + "helpPageTitleAdministrationUsers": "Uporabniki", + "helpPageTitleAdministrationSecondaryDomains": "Podpodročja" +} diff --git a/express/public/sass/common/_common.scss b/express/public/sass/common/_common.scss index 1d3335a..eba83be 100644 --- a/express/public/sass/common/_common.scss +++ b/express/public/sass/common/_common.scss @@ -6,6 +6,67 @@ padding: 0; } +body { + color: v.$tp-black; +} + +// .check { +// -webkit-appearance: none; /*hides the default checkbox*/ +// height: 20px; +// width: 20px; +// position: relative; +// top: 20px; +// left: 20px; +// transition: 0.10s; +// background-color: #FE0006; +// text-align: center; +// font-weight: 600; +// color: white; +// border-radius: 3px; +// outline: none; +// } + +/* +input[type="checkbox"]:checked { + appearance: none; + height: 14px; + width: 14px; + font-size: 10px !important; + transition: 0.1s; + background-color: v.$navigation-maincolor; + text-align: center; + // font-weight: 600; + color: white; + border-radius: 3px; + outline: none; + + &:before { + content: "✔"; + } +}*/ + +/* +.check:checked { + background-color: #0E9700; +} + +.check:before { + content: "✖"; +} +.check:checked:before { + content: "✔"; +} + +.check:hover { + cursor: pointer; + opacity: 0.8; +} +*/ + +.dropdown-item:active { + background-color: v.$navigation-maincolor; +} + // input:focus-visible, // button:focus-visible, // button:focus { @@ -17,6 +78,15 @@ cursor: pointer; } +.outline-enabled { + outline: auto; +} + +.hover-opacity-blue:hover { + color: v.$navigation-maincolor; + opacity: 0.8; +} + .index-font-size { font-size: 1rem; } @@ -76,10 +146,32 @@ margin-top: 0.25rem; } -.h-41 { +.rpm-corr { + margin-top: -1.75rem; +} + +.h-40px { + height: 40px !important; +} + +// Todo check if this is needed +.h-41px { display: block; height: 41px !important; } +// end check + +.w320px { + width: 320px !important; +} + +.rm-outline:focus { + outline: none; +} + +.max-512px { + max-width: 512px !important; +} .navigation-text-color { color: v.$navigation-maincolor; @@ -123,6 +215,11 @@ width: 32px !important; } +.i40x40 { + height: 40px !important; + width: 40px !important; +} + .height60 { height: 60px !important; } @@ -147,6 +244,10 @@ } } +.clipx { + overflow-x: hidden; +} + .strength500 { font-weight: 500; } @@ -278,6 +379,14 @@ border: 1px solid v.$gray-2; } +.mt-minus-1 { + margin-top: -0.25rem; +} + +.mt-minus-p3 { + margin-top: -0.3rem; +} + .ps-2rem { padding-left: 2rem !important; } @@ -328,6 +437,7 @@ .pager { white-space: nowrap; + // min-width: 13.4rem; * { display: inline-block; @@ -337,6 +447,14 @@ opacity: 25%; } + button { + opacity: 75%; + } + + button:not([disabled]):hover { + opacity: 100%; + } + .pager-input { width: 54px; height: 38px; @@ -428,6 +546,9 @@ .termin-offset-up-wide { top: 85px !important; + max-height: calc( + 100vh - 130px + 10px + ) !important; // correction due to bigger panel size } } @@ -436,6 +557,10 @@ margin-right: 2rem !important; } +.me-2rem { + margin-right: 2rem !important; +} + .mx-max-md-2rem { @media (max-width: v.$layout-breakpoint-medium) { margin-left: 2rem !important; @@ -475,7 +600,7 @@ .txt { font-size: 0.75rem !important; } - $padding-wide: 3rem; + $padding-wide: 2rem; img.rsmzk { height: 30px; padding-left: $padding-wide; diff --git a/express/public/sass/common/_index-definitions.scss b/express/public/sass/common/_index-definitions.scss index 5cb6f67..f0a7685 100644 --- a/express/public/sass/common/_index-definitions.scss +++ b/express/public/sass/common/_index-definitions.scss @@ -241,13 +241,21 @@ html { border: none; background: none; + + @media screen and (min-width: v.$layout-breakpoint-medium) { + font-size: 1.3rem; + } + + &.pdf { + font-size: 1rem; + } } .search-button-keyboard { display: flex; flex-shrink: 0; margin: 0 20px; - width: 30px; + // width: 30px; height: 100%; line-height: 10px; align-items: center; @@ -312,6 +320,7 @@ html { } .menutxt { + margin-left: 0.5rem; display: flex !important; } @@ -421,7 +430,7 @@ html { .search-button-keyboard { margin: 0 20px; - width: 25px; + // width: 25px; line-height: 10px; } } @@ -429,7 +438,7 @@ html { .descriptionArea { width: 48rem; - padding: 5rem 2rem; + padding: 5rem 2rem 3.5rem; // add specific bottom padding to align with footer padding margin: 0 auto; } diff --git a/express/public/sass/components/_admin-main.scss b/express/public/sass/components/_admin-main.scss index 3460281..5df90de 100644 --- a/express/public/sass/components/_admin-main.scss +++ b/express/public/sass/components/_admin-main.scss @@ -73,7 +73,7 @@ /*responsive*/ -@media (max-width: 1192px) { +@media (max-width: v.$layout-breakpoint-x-large) { .table-users thead { display: none; } @@ -109,6 +109,23 @@ } } +#page-results { + td { + min-width: 32px; + &.tdata-area { + width: 40%; + } + + &.tdata-translation { + width: 40%; + } + + &.buttons-group { + min-width: 48px; + } + } +} + #add-area { height: 41px; padding: 10px 40px; diff --git a/express/public/sass/components/_admin-side-menu.scss b/express/public/sass/components/_admin-side-menu.scss index bfc07e2..37b73de 100644 --- a/express/public/sass/components/_admin-side-menu.scss +++ b/express/public/sass/components/_admin-side-menu.scss @@ -41,7 +41,15 @@ body { font-weight: 400; color: #46535b; text-decoration: none; - display: block; + // display: block; + display: flex; + align-items: center; + // gap: 0.5rem; + + > * { + padding: 0; + margin: 0; + } } a:visited { diff --git a/express/public/sass/components/_comments-main.scss b/express/public/sass/components/_comments-main.scss index 0924a81..1239f2d 100644 --- a/express/public/sass/components/_comments-main.scss +++ b/express/public/sass/components/_comments-main.scss @@ -6,6 +6,11 @@ align-items: center; } +.pager-container { + display: flex; + width: 130px; +} + .comments-container { list-style: none; margin: 0; @@ -173,7 +178,7 @@ form.comment-form::after { .comments-top-hr { color: #b6bec4; - opacity: 0.7; + opacity: 0.8; margin-top: 0.063rem; margin-bottom: 0rem; height: 10%; @@ -354,7 +359,7 @@ form.comment-reply-form button { .comments-top-hr { color: #b6bec4; - opacity: 0.7; + opacity: 0.8; margin-top: 0.63rem; margin-bottom: 0rem; height: 10%; @@ -450,7 +455,7 @@ form.comment-reply-form button { .comments-holder { padding-left: 16px; } -@media only screen and (min-width: 1200px) { +@media only screen and (min-width: v.$layout-breakpoint-x-large) { .comments-holder { padding-left: 36px; } diff --git a/express/public/sass/components/_consultancy-main.scss b/express/public/sass/components/_consultancy-main.scss index 78f4c2d..6888a87 100644 --- a/express/public/sass/components/_consultancy-main.scss +++ b/express/public/sass/components/_consultancy-main.scss @@ -9,6 +9,12 @@ // margin: 0 1rem; } +#text-description.page-description.pe-0 { + a { + font-size: 0.875rem; + } +} + .consultancy-padding-admin { display: flex; flex-grow: 1; @@ -43,6 +49,12 @@ span.question-asked-time { font-size: 0.75rem; } + .title-cons { + color: v.$navigation-maincolor; + * { + color: v.$navigation-maincolor; + } + } } } } diff --git a/express/public/sass/components/_content-header-section.scss b/express/public/sass/components/_content-header-section.scss index 3bd8646..4c85839 100644 --- a/express/public/sass/components/_content-header-section.scss +++ b/express/public/sass/components/_content-header-section.scss @@ -263,6 +263,17 @@ background-color: #f5f5f5; } +#show-dates:focus { + background-color: inherit; + border-color: #b6bec3; + box-shadow: none; +} + +#hide-dates:focus { + border-color: #b6bec3; + box-shadow: none; +} + .btn.disabled, .btn:disabled, fieldset:disabled .btn { diff --git a/express/public/sass/components/_content-side-menu.scss b/express/public/sass/components/_content-side-menu.scss index d12476a..8515e6d 100644 --- a/express/public/sass/components/_content-side-menu.scss +++ b/express/public/sass/components/_content-side-menu.scss @@ -36,6 +36,12 @@ $content-nav-content-width-medium: 22rem; } } +.content-nav-title { + position: fixed; + top: 85px; + left: 35px; +} + .term-button { max-width: 445px; font-size: 1rem; @@ -492,6 +498,28 @@ $content-nav-content-width-medium: 22rem; } */ +.dark-gray-tooltip.tooltip > .tooltip-inner { + background-color: v.$header-description-gray !important; + max-width: 500px; + padding: 8px 17px; + border-radius: 4px; +} + +.dark-gray-tooltip.bs-tooltip-top .tooltip-arrow::before { + border-top-color: v.$header-description-gray !important; +} +.dark-gray-tooltip.bs-tooltip-bottom .tooltip-arrow::before { + border-bottom-color: v.$header-description-gray !important; +} + +.dark-gray-tooltip.bs-tooltip-start .tooltip-arrow::before { + border-left-color: v.$header-description-gray !important; +} + +.dark-gray-tooltip.bs-tooltip-end .tooltip-arrow::before { + border-right-color: v.$header-description-gray !important; +} + .gray-tooltip.tooltip > .tooltip-inner { background-color: #b6bec4; max-width: 500px; diff --git a/express/public/sass/components/_dictionaries-main.scss b/express/public/sass/components/_dictionaries-main.scss index cb5102e..3cd2501 100644 --- a/express/public/sass/components/_dictionaries-main.scss +++ b/express/public/sass/components/_dictionaries-main.scss @@ -391,7 +391,7 @@ body { /*responsive*/ -@media (max-width: 1192px) { +@media (max-width: v.$layout-breakpoint-x-large) { .table-users thead { display: none; } @@ -711,6 +711,13 @@ textarea.form-control { border-radius: 0; } +.select2-container--default.select2-container--focus + .select2-selection--multiple { + box-shadow: 0 0 0 0.25rem #0d6efd40; + border-color: #86b7fe; + outline: 0; +} + .autocomplete { background-color: white; // border-radius: 6px; diff --git a/express/public/sass/components/_extraction-main.scss b/express/public/sass/components/_extraction-main.scss index 41e000f..9983d90 100644 --- a/express/public/sass/components/_extraction-main.scss +++ b/express/public/sass/components/_extraction-main.scss @@ -83,6 +83,14 @@ } .task-name { + width: unset; +} + +.td-index { + width: 5%; +} + +.td-cannon { width: 45%; } @@ -165,40 +173,53 @@ font-size: 1rem; } -// li { -// list-style: none; -// } - -#terminology-candidates { +.terminology-candidates { width: 100%; } -#user-corpus-btn { +.ext-secondary-btn { width: 100%; } -#kas-corpus-btn { - width: 100%; +@media screen and (min-width: v.$layout-breakpoint-small) { + .task-name { + width: 295px; + } + + .dates-div { + margin-left: 60px; + } } @media screen and (min-width: v.$layout-breakpoint-large) { - #terminology-candidates { + .terminology-candidates { width: unset; height: 49px; } - #user-corpus-btn { + .ext-secondary-btn { width: unset; height: 49px; } - #kas-corpus-btn { - width: unset; - height: 49px; + .task-name { + width: 295px; + } + + .dates-div { + margin-left: 60px; } } @media screen and (min-width: v.$layout-breakpoint-x-large) { .extraction-margin-left { margin-left: 1.6rem; } + + .task-name { + width: 745px; + } + + .dates-div { + margin-left: 80px; + } } diff --git a/express/public/sass/components/_header-section.scss b/express/public/sass/components/_header-section.scss index ca70d3f..17379b0 100644 --- a/express/public/sass/components/_header-section.scss +++ b/express/public/sass/components/_header-section.scss @@ -15,6 +15,11 @@ background-color: #f5f5f5; padding-left: 2rem; padding-right: 2rem; + + @media (max-width: v.$layout-breakpoint-small) { + padding-left: 1rem; + padding-right: 1rem; + } } .header-section-root-scroll { @@ -144,7 +149,7 @@ flex-wrap: wrap; background-color: #f5f5f5; /*left: 35px;*/ - right: 35px; + right: 2rem; margin-left: 15px; margin-right: 15px; @@ -267,7 +272,9 @@ overflow: hidden; text-overflow: ellipsis; } - + #thead { + background-color: inherit !important; + } tr { color: v.$header-description-gray; @@ -284,9 +291,9 @@ background-color: v.$white-1; } - tr:first-child { - background-color: v.$nav-white; - } + // tr:first-child { + // background-color: v.$nav-white; + // } .btn { width: -moz-fit-content; @@ -538,6 +545,30 @@ // height: 20px; } +.btn-delete-style { + @extend .btn-primary; + background-color: v.$button-danger-color; + width: 100%; + max-width: 258px; + + &:hover { + background-color: v.$button-danger-color; + opacity: 0.8; + } + + &:active { + background-color: v.$button-danger-color; + border-color: v.$button-danger-color; + box-shadow: 0 0 0 0.25rem rgba(v.$button-danger-color, 0.5); + } + + &:focus { + background-color: v.$button-danger-color; + border-color: v.$button-danger-color; + box-shadow: 0 0 0 0.25rem rgba(v.$button-danger-color, 0.5); + } +} + .chevrons-left-margin { margin-top: 20px; } diff --git a/express/public/sass/components/_help.scss b/express/public/sass/components/_help.scss index 55e9047..8326d85 100644 --- a/express/public/sass/components/_help.scss +++ b/express/public/sass/components/_help.scss @@ -46,7 +46,7 @@ body { transition: 0.3s; // max-height: calc(100vh - 80px); height: 100vh; - max-height: calc(100vh - 130px - 75px); + max-height: calc(100vh - 130px - 35px); overflow-y: auto; a { @@ -190,10 +190,17 @@ body { .nav-pills .nav-link.active { background-color: transparent; - color: #006cb7; + color: v.$navigation-maincolor; font-weight: 700; } +.nav-pills .nav-link:hover { + background-color: transparent; + color: v.$navigation-maincolor; + // font-weight: 700; + opacity: 0.8; +} + .help-subheader-title { font-family: "Roboto"; font-style: normal; diff --git a/express/public/sass/components/_side-menu.scss b/express/public/sass/components/_side-menu.scss index 49416ce..d38b6fd 100644 --- a/express/public/sass/components/_side-menu.scss +++ b/express/public/sass/components/_side-menu.scss @@ -2,7 +2,9 @@ @use "search-filter/search-filter-navigation" as sf; // current footer size: 80px -$navbarSize: calc(100vh - 130px - 72px); // 130px from top minus footer size +$footer-size: 35px; +$navbarSize: calc(100vh - 130px - 35px); // 130px from top minus footer size +// $navbarSizeDict: calc(100vh - 130px - 80px); .slidable { list-style: none; @@ -31,6 +33,10 @@ $navbarSize: calc(100vh - 130px - 72px); // 130px from top minus footer size // max-height: calc(100vh - 80px); max-height: $navbarSize; overflow-y: auto; + + // &.bottom-offset-dict { + // max-height: $navbarSizeDict; + // } } @media (min-width: v.$layout-breakpoint-x-large) { @@ -44,6 +50,12 @@ $navbarSize: calc(100vh - 130px - 72px); // 130px from top minus footer size } } +.admin-nav-content { + a.sel-anchr { + align-items: start; + } +} + .sel-anchr { > div { > img { diff --git a/express/public/sass/components/_toolbar.scss b/express/public/sass/components/_toolbar.scss index 78014f3..2bb100c 100644 --- a/express/public/sass/components/_toolbar.scss +++ b/express/public/sass/components/_toolbar.scss @@ -200,7 +200,7 @@ html { display: flex; flex-shrink: 0; margin: 0 20px; - width: 30px; + // width: 30px; height: 100%; line-height: 10px; align-items: center; @@ -212,7 +212,7 @@ html { .dropdown-btn { display: flex; - width: 30px; + // width: 30px; flex-shrink: 0; margin-right: 20px; align-items: center; diff --git a/express/public/sass/components/search-filter/_res-panel.scss b/express/public/sass/components/search-filter/_res-panel.scss index df741fe..63d4c9c 100644 --- a/express/public/sass/components/search-filter/_res-panel.scss +++ b/express/public/sass/components/search-filter/_res-panel.scss @@ -50,20 +50,21 @@ .rl { display: block; text-decoration: none; - color: #000; + color: v.$tp-black; padding-top: 6px; - padding-left: 6px; + padding-left: 0.5rem; + padding-right: 0.5rem; // * { // word-break: normal; // } &:visited { - color: #000; + color: v.$tp-black; } &:hover { - color: #000; + color: v.$tp-black; background-color: #fff; box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.2); } @@ -253,6 +254,7 @@ font-size: 0.75rem; font-weight: 700; border-radius: 2px; + width: 2.25rem; &.te { color: v.$gray-1; diff --git a/express/public/sass/components/search-filter/_search-filter-modal.scss b/express/public/sass/components/search-filter/_search-filter-modal.scss index ac9cec2..51355e4 100644 --- a/express/public/sass/components/search-filter/_search-filter-modal.scss +++ b/express/public/sass/components/search-filter/_search-filter-modal.scss @@ -175,10 +175,6 @@ } - .clear-filter-section { - } - - */ .search-modal-filter { diff --git a/express/public/sass/components/search-filter/_search-filter-navigation.scss b/express/public/sass/components/search-filter/_search-filter-navigation.scss index cb036cc..2d4b7ab 100644 --- a/express/public/sass/components/search-filter/_search-filter-navigation.scss +++ b/express/public/sass/components/search-filter/_search-filter-navigation.scss @@ -15,7 +15,7 @@ $sfPanelWidth: 24rem; box-shadow: none; left: 0; background-color: v.$nav-white; - z-index: 5; + z-index: 100; } @media (min-width: v.$layout-breakpoint-medium) { @@ -33,6 +33,7 @@ $sfPanelWidth: 24rem; v.$padding-left-nav: 1rem; .clear-filter-section { + margin: 0.1rem 0 0 0.1rem; // corrects the outline cetting cropped from absolute layout display: flex; align-items: center; // margin-top: 16px; @@ -40,9 +41,7 @@ v.$padding-left-nav: 1rem; color: v.$navigation-maincolor !important; align-content: center; font-weight: 200; - - img { - } + width: fit-content; span { padding-left: 8px; @@ -87,15 +86,22 @@ v.$padding-left-nav: 1rem; background: none; padding-right: 0; justify-content: flex-end; + align-self: center; button { display: flex; - width: 32px; - height: 24px; + // width: 32px; + // height: 24px; padding: 0; margin: 0; background: none; box-shadow: none; + justify-content: flex-end; + flex: 0 0 auto; + + &:disabled { + opacity: 0.3; + } .select-more-text, div { @@ -103,6 +109,9 @@ v.$padding-left-nav: 1rem; /*padding-right: 10px;*/ justify-content: flex-end; align-items: center; + width: max-content; + flex-grow: 0; + flex-shrink: 1; } .select-more-text { @@ -136,6 +145,7 @@ v.$padding-left-nav: 1rem; } .nav-section-content { + margin-left: 0.1rem; display: flex; flex-direction: column; padding-left: 0; @@ -204,18 +214,3 @@ v.$padding-left-nav: 1rem; min-height: 600px; background-color: v.$nav-white; } - -@media (min-width: v.$layout-breakpoint-medium) { - /*.nav-section { - padding-top: 28px; - }*/ - - //.search-filter-nav-content { - //position: fixed; - //} - - .clear-filter-section { - // margin-top: 43px; - // padding-left: v.$padding-left-nav; - } -} diff --git a/express/public/sass/variables/_variables.scss b/express/public/sass/variables/_variables.scss index 37db13e..313febc 100644 --- a/express/public/sass/variables/_variables.scss +++ b/express/public/sass/variables/_variables.scss @@ -21,6 +21,7 @@ $error-red: #d12525; $logged-in-green: #6cf3ab; $dark-gray-1: #3a464e; +$tp-black: #2e373c; $border-bottom-inputs: rgba(19, 19, 21, 0.6); @@ -38,6 +39,7 @@ $button-maincolor: $navigation-maincolor; $button-primary-color: $navigation-maincolor; $button-secondary-color: $gray-2; +$button-danger-color: #ac7171; /* CUSTOM RULES */ diff --git a/express/routes/admin.js b/express/routes/admin.js index 9d2c3ad..c873992 100644 --- a/express/routes/admin.js +++ b/express/routes/admin.js @@ -97,6 +97,9 @@ router.get( dictionary.showImportFromFileForm ) +// Import dictionary data from file. +router.post('/slovarji/:dictionaryId/uvoz/datoteka', dictionary.importFromFile) + router.get( '/slovarji/:dictionaryId/uvoz/luscenje', dictionary.showImportFromExtractionForm diff --git a/express/routes/api/v1/consultancy.js b/express/routes/api/v1/consultancy.js index 485bf83..799b257 100644 --- a/express/routes/api/v1/consultancy.js +++ b/express/routes/api/v1/consultancy.js @@ -17,19 +17,22 @@ const { sendToReview, publish, updateQuestion, - updateSharedAuthors + updateSharedAuthors, + sendPaginationData } = require('../../../controllers/api/v1/consultancy') +router.get('/entry-pagination', sendPaginationData) + +router.get('/entry', listEntries) + +router.get('/new-entry', listNewEntries) + // All routes require an authenticated user. router.use((req, res, next) => { if (req.isAuthenticated()) return next() res.status(400).send('Unauthenticated') }) -router.get('/entry', listEntries) - -router.get('/new-entry', listNewEntries) - router.post('/entry', createQuestion) router.put('/entry', updateQuestion) diff --git a/express/routes/api/v1/dictionaries.js b/express/routes/api/v1/dictionaries.js index 5158390..4e073fc 100644 --- a/express/routes/api/v1/dictionaries.js +++ b/express/routes/api/v1/dictionaries.js @@ -20,6 +20,18 @@ router.get('/:dictionaryId/listDomainLabels', dictionaries.listDomainLabels) // Change page in pagination router.get('/listSecondaryDomains', dictionaries.listSecondaryDomains) +// Change page in pagination +router.get( + '/:dictionaryId/showImportFromFileForm', + dictionaries.showImportFromFileForm +) + +// Change page in pagination +router.get( + '/:dictionaryId/showExportToFileForm', + dictionaries.showExportToFileForm +) + // Delete all entries of specific dictionary. router.delete('/:dictionaryId/entries/all', dictionaries.deleteAllEntries) @@ -28,8 +40,15 @@ router.put('/:dictionaryId/entries/all/publish', dictionaries.publishAllEntries) // Import term candidates from extraction. router.post( - '/:id/import/extraction/:extractionId', - dictionaries.extractionImport + '/:id/import-extraction/:extractionId', + dictionaries.importFromExtraction ) +// Export dictionary into a file. +router.post('/:id/export-begin', dictionaries.exportBegin) + +router.get('/:dictionaryId/domainLabels', dictionaries.listFilteredDomainLabels) + +router.get('/secondaryDomains', dictionaries.listSecondaryDomainData) + module.exports = router diff --git a/express/routes/consultancy.js b/express/routes/consultancy.js index 3cf7129..a408156 100644 --- a/express/routes/consultancy.js +++ b/express/routes/consultancy.js @@ -17,7 +17,7 @@ router.use((req, res, next) => { router.get('/vprasanje/admin/novo', consultancyAdmin.new) -router.get('/vprasanje/admin/uporabniki', consultancyAdmin.users) +router.get('/vprasanje/admin/svetovalci', consultancyAdmin.users) router.get('/vprasanje/admin/zavrnjeno', consultancyAdmin.rejected) diff --git a/express/routes/dictionaries.js b/express/routes/dictionaries.js index 3d54059..d517a1a 100644 --- a/express/routes/dictionaries.js +++ b/express/routes/dictionaries.js @@ -88,7 +88,7 @@ router.get( // Show a page to preview, create or edit dictionary entries and their comments. router.get( '/:dictionaryId/vsebina', - user.isDictionaryEditor, + user.canContentEdit, dictionary.showContent ) @@ -127,4 +127,7 @@ router.get( dictionary.showImportFromExtractionForm ) +// Download dictionary export file. +router.get('/export-download/:exportId', dictionary.exportDownload) + module.exports = router diff --git a/express/routes/index.js b/express/routes/index.js index 1f20a1a..ef8201a 100644 --- a/express/routes/index.js +++ b/express/routes/index.js @@ -1,4 +1,5 @@ const router = require('express-promise-router')() +const { isAuthenticated: isUserAuthenticated } = require('../middleware/user') const db = require('../models/db') const cache = require('../models/cache') const { searchEngineClient } = require('../models/search-engine') @@ -67,12 +68,12 @@ router.get('/demo-paginacija', demoPaginacija.izrišiStran) // Render help page router.get('/pomoc', (req, res) => { - res.render('pages/help', { title: 'Pomoč' }) + res.render('pages/help', { title: req.t('Pomoč') }) }) // Create a route to download pdf router.get('/pomoc/pdf', (req, res) => { - res.download('public/documents/Podrocja_TP_2022-11-28.pdf') + res.download('public/documents/Tabela.pdf') }) // Create a route to download guidance pdf @@ -80,6 +81,11 @@ router.get('/pomoc/guidance-pdf', (req, res) => { res.download('public/documents/RSDO_smernice.pdf') }) +// Create a route to download schema +router.get('/slovarji/xml-schema', (req, res) => { + res.download('public/documents/dictionary_schema.xsd') +}) + // Render demo help pug page. router.get('/pomoc-pug-demo', (req, res) => { res.render('pages/help-pug-demo-frame', { title: 'Pomoč - pug demo' }) @@ -93,24 +99,29 @@ router.post('/users/logout', user.logout) // Render privacy policy page router.get('/politika-zasebnosti', (req, res) => { - res.render('pages/privacy-policy', { title: 'Politika zasebnosti' }) + res.render(`pages/privacy-policy_${req.language}`, { + title: req.t('titlePrivacyPolicy') + }) }) // Render terms of use page router.get('/pogoji-uporabe', (req, res) => { - res.render('pages/terms-of-use', { title: 'Pogoji uporabe' }) + res.render(`pages/terms-of-use_${req.language}`, { + title: req.t('titleTermsOfUse') + }) }) -// All routes require an authenticated user. -router.use((req, res, next) => { - if (req.isAuthenticated()) return next() - res.redirect('/') -}) +router.get('/moj-racun', isUserAuthenticated, index.myProfile) -router.get('/moj-racun', index.myProfile) +router.get('/izbrisi-racun', isUserAuthenticated, index.deleteProfile) -router.get('/spremeni-geslo', index.changePassword) +router.get('/spremeni-geslo', isUserAuthenticated, index.changePassword) -router.get('/nastavitve-racuna', index.userSettings) +router.get('/nastavitve-racuna', isUserAuthenticated, index.userSettings) + +router.get('/ponastavitev-gesla', index.resetPassword) + +// Change user locale. +router.get('/spremeni-jezik/:languageCode', index.changeUserLanguage) module.exports = router diff --git a/express/scheduled/batch_update_linked.js b/express/scheduled/batch_update_linked.js index b07fd44..4a1c871 100644 --- a/express/scheduled/batch_update_linked.js +++ b/express/scheduled/batch_update_linked.js @@ -19,11 +19,13 @@ const async = require('async') const needle = require('needle') const xmlFlow = require('xml-flow') const debug = require('debug')('termPortal:batch_update_linked') +const debugDetail = require('debug')('termPortal:batch_update_linked_detail') const dbc = require('../models/db').getExtraClient() +const Dictionary = require('../models/dictionary') const ConsultancyEntry = require('../models/consultancy-entry') const batch = require('../models/batch') const path = require('path') -const stream = require('stream') +const { deleteDictionaryEntriesFromIndex } = require('../models/search-engine') const jobName = 'update_linked_dictionaries' const threadId = '' + process.pid const args = process.argv.slice(2) @@ -49,17 +51,26 @@ if (args.length > 0) { if (args.length !== 2) printSynopsisAndExit() limitType = args[0] limitCode = args[1] - updateDictionaries() + updateDictionaries(() => { + // all errors are already handled/recorded inside the procedure + debug('exiting updateDictionaries') + process.exit() + }) break case 'dict': if (args.length !== 2) printSynopsisAndExit() limitType = args[0] limitCode = args[1] - updateDictionaries() + updateDictionaries(() => { + // all errors are already handled/recorded inside the procedure + debug('exiting updateDictionaries') + process.exit() + }) break case 'zrc': updateZrcSvetovalnica(err => { if (err) console.error(err) + debug('exiting zrc') process.exit() }) break @@ -67,22 +78,24 @@ if (args.length > 0) { printSynopsisAndExit() } } else { - updateDictionaries() + // default operation update all linked dictionaries + updateDictionaries(() => { + // all errors are already handled/recorded inside the procedure + debug('exiting updateDictionaries') + process.exit() + }) } -function updateDictionaries() { +function updateDictionaries(done) { async.waterfall( [ function (cbw) { - debug('init') batch.init(dbc, report, cbw) }, function (cbw) { - debug('phase') batch.reportPhase(dbc, report, 'preparing', 0, cbw) }, function (cbw) { - debug('item') batch.reportItem(dbc, report, 'languages', 50, cbw) }, function (cbw) { @@ -92,13 +105,13 @@ function updateDictionaries() { (err, result) => { if (err) return cbw(err) languageList = result.rows - debug(languageList) + debugDetail(languageList) cbw() } ) }, function (cbw) { - debug('item') + debug('reportItem servers') batch.reportItem(dbc, report, 'servers', 100, err => { cbw(err) }) @@ -120,12 +133,12 @@ function updateDictionaries() { dbc.query(sql, (err, result) => { if (err) return cbw(err) linkedPortals = result.rows - debug(linkedPortals) + debugDetail(linkedPortals) cbw() }) }, function (cbw) { - debug('item') + debug('reportItem dictionaries') batch.reportItem(dbc, report, 'dictionaries', 60, err => { cbw(err) }) @@ -154,14 +167,15 @@ function updateDictionaries() { dbc.query(sql, (err, result) => { if (err) return cbw(err) linkedDictionaries = result.rows - debug(linkedDictionaries) + debugDetail(linkedDictionaries) cbw() }) }, function (cbw) { if (!linkedDictionaries || linkedDictionaries.length === 0) { - debug('finalize') - return batch.finalize(dbc, report, 'no linked dictionaries', cbw) + debug('finalize: no linked dictionaries') + batch.finalize(dbc, report, 'no linked dictionaries', cbw) + process.exit() } const stepPercent = 100 / linkedDictionaries.length async.eachOfSeries( @@ -199,17 +213,37 @@ function updateDictionaries() { }, function (cbwd) { applyUpdates(dbc, ldict, errd => { - cbwd(errd) + if (errd) return cbwd(errd) + dbc.query( + 'UPDATE linked_dictionary SET time_last_synced=$1 WHERE id=$2', + [dictionarySyncStartTimestamp, ldict.id], + errd => { + cbwd(errd) + } + ) }) }, function (cbwd) { - dbc.query( - 'UPDATE linked_dictionary SET time_last_synced=$1 WHERE id=$2', - [dictionarySyncStartTimestamp, ldict.id], - errd => { - cbwd(errd) - } - ) + if (linkedDictionaryEntriesToUpdate.length) { + debug('reindexing') + const startIndex = Date.now() + const dictionaryId = ldict.target_dictionary_id + deleteDictionaryEntriesFromIndex(dictionaryId) + .then(() => { + return Dictionary.indexIntoSearchEngine(dictionaryId) + }) + .catch(e => + console.error('dictionary reindex failed:', e) + ) + .finally(() => { + const indexMilis = Date.now() - startIndex + debug(`done indexing: ${indexMilis}ms`) + cbwd() + }) + } else { + debug('skipping reindexing (no new entries)') + cbwd() + } } ], errw2 => { @@ -224,16 +258,19 @@ function updateDictionaries() { } ], err => { + // end of waterfall if (err) { console.error(err) batch.fail(dbc, report, err, errf => { if (errf) console.error(errf) dbc.end() + done() }) } else { batch.finalize(dbc, report, 'completed', errf => { if (errf) console.error(errf) dbc.end() + done() }) } } @@ -247,7 +284,7 @@ function updateDictionaries() { * @param done */ function fetchUpdates(ldict, done) { - const lastSync = ldict.synced + const lastSync = ldict.time_last_synced ? ldict.time_last_synced.toISOString().substring(0, 19).replace('T', ' ') : '2000-01-01 00:00:00' dictionarySyncStartTimestamp = new Date() @@ -273,14 +310,18 @@ function fetchUpdates(ldict, done) { if (Array.isArray(body)) { debug('fetched:', body.length) linkedDictionaryEntriesToUpdate = body - } else { + } else if (body.entries) { debug('fetched:', body.entries.length) linkedDictionaryEntriesToUpdate = body.entries + } else { + console.error('invalid body', body) + linkedDictionaryEntriesToUpdate = [] } done() }) } +/* deprecated for burning identity values const sqlUpsertEntry = 'INSERT INTO entry ' + ' (dictionary_id, is_valid, is_published, term, label, definition, synonym, external_id, external_url)' + @@ -288,6 +329,16 @@ const sqlUpsertEntry = ' ON CONFLICT (dictionary_id,external_id)' + ' DO UPDATE SET time_modified = now(),' + ' term = $4, label = $5, definition = $6, synonym = $7, external_url = $9' +*/ +const sqlInsertEntry = + 'INSERT INTO entry ' + + ' (dictionary_id, is_valid, is_published, term, label, definition, synonym, external_id, external_url, status)' + + " VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,'complete') returning id" +const sqlUpdateEntry = + "UPDATE entry SET time_modified = now(), term = $1, label = $2, definition = $3, synonym = $4, external_url = $5, status = 'complete'" + + ' WHERE id = $6' +const sqlSelectEntryByExternalId = + 'SELECT id FROM entry WHERE dictionary_id = $1 AND external_id = $2' const sqlUpsertEntryTranslation = 'INSERT INTO entry_foreign ' + @@ -314,43 +365,76 @@ function applyUpdates(dbc, ldict, done) { linkedDictionaryEntriesToUpdate, (entry, key, cbe) => { let entryId = 0 + const entryIdExternal = '' + entry.id currentEntry = entry async.waterfall( [ function (cbw) { - debug('upsert entry', entry) + // debug(`find existing entry ${targetDictionaryId}.${entryIdExternal}`) dbc.query( - sqlUpsertEntry, - [ - targetDictionaryId, - true, - true, - entry.term, - entry.label, - entry.definition, - entry.synonym, - '' + entry.id, - entry.url - ], - errq => { - cbw(errq) - } - ) - }, - function (cbw) { - debug('fetch entry id') - dbc.query( - 'SELECT id FROM entry WHERE dictionary_id=$1 AND external_id=$2', - [targetDictionaryId, '' + entry.id], - (errq, result) => { - if (errq) return cbw(errq) - entryId = result.rows[0].id + sqlSelectEntryByExternalId, + [targetDictionaryId, entryIdExternal], + (errdb, result) => { + if (errdb) { + return cbw(errdb) + } + if (result && result.rows && result.rows.length > 0) { + entryId = result.rows[0].id + } else { + entryId = 0 + } cbw() } ) }, function (cbw) { - debug('deleting removed entry translations') + const synonymArray = entry.synonyms + if (entryId > 0) { + // entry exists -> update it + // SET term = $1, label = $2, definition = $3, synonym = $4, external_url = $5 + debugDetail('update entry', entry) + dbc.query( + sqlUpdateEntry, + [ + entry.term, + entry.label, + entry.definition, + synonymArray, + entry.url, + entryId + ], + errq => { + cbw(errq) + } + ) + } else { + // entry doesnt exists -> insert it + debugDetail('insert entry', entry) + dbc.query( + sqlInsertEntry, + [ + targetDictionaryId, + true, + true, + entry.term, + entry.label, + entry.definition, + synonymArray, + entryIdExternal, + entry.url + ], + (errq, result) => { + if (errq) return cbw(errq) + if (result && result.rows && result.rows.length > 0) + entryId = result.rows[0].id + else return cbw(new Error('no insert id')) + cbw() + } + ) + } + }, + function (cbw) { + debugDetail('deleting removed entry translations') let includedLangIds = [0] entry.translations.forEach(et => { const lng = languageList.find(l => { @@ -360,7 +444,7 @@ function applyUpdates(dbc, ldict, done) { }) // keep unique ids includedLangIds = Array.from(new Set(includedLangIds)) - debug('except for languages', includedLangIds) + debugDetail('except for languages', includedLangIds) dbc.query( 'DELETE FROM entry_foreign WHERE entry_id=$1 AND language_id NOT IN (' + includedLangIds.join(',') + @@ -368,21 +452,21 @@ function applyUpdates(dbc, ldict, done) { [entryId], (errq, result) => { if (!errq && result.rowCount) - debug('deleted translations: ', result.rowCount) + debugDetail('deleted translations: ', result.rowCount) cbw(errq) } ) }, function (cbw) { - debug('upserting entry translations') + debugDetail('upserting entry translations') async.eachOfSeries( entry.translations, - (trans, key, cbst) => { + (trans, key, cbTrans) => { const transLang = languageList.find(l => { - return l.code === trans.language_code + return l.code === trans.lang }) if (!transLang) { - debug( + console.warn( 'WARN: language not mapped, skipping translation upsert: ', trans ) @@ -391,17 +475,12 @@ function applyUpdates(dbc, ldict, done) { trans.language_code ) report.warnings++ - return cbst() + return cbTrans() } - debug( - 'updaing translation for entry', - entryId, - trans.lang, - trans.terms - ) - termsSqlArray = '{"' + trans.term.join('","') + '"}' - if (trans.synonym && trans.synonym.length) { - synonymsSqlArray = '{"' + trans.synonym.join('","') + '"}' + debugDetail('upsert translation', entryId, transLang.id, trans) + termsSqlArray = trans.terms + if (trans.synonyms && trans.synonyms.length) { + synonymsSqlArray = trans.synonyms } dbc.query( sqlUpsertEntryTranslation, @@ -414,13 +493,17 @@ function applyUpdates(dbc, ldict, done) { ], errq => { if (errq) { - debug('WARN: error upserting translation: ', trans, errq) + console.warn( + 'WARN: error upserting translation: ', + trans, + errq + ) report.trace.push( 'WARN: error upserting translation: ' + errq ) report.warnings++ } - cbst() + cbTrans() } ) }, @@ -429,6 +512,44 @@ function applyUpdates(dbc, ldict, done) { cbw(errt) } ) + }, + function (cbw) { + debugDetail('clearing entry links') + dbc.query( + 'DELETE FROM entry_link WHERE entry_id = $1', + [entryId], + errq => { + if (errq) { + const msg = `WARN: error clearing entry links: ${errq}` + console.error(msg) + report.trace.push(msg) + report.warnings++ + } + cbw() + } + ) + }, + function (cbw) { + if (!entry.links || entry.links.length === 0) return cbw() + debugDetail('setting entry links:', entry.links.length) + let sql = 'INSERT INTO entry_link (entry_id, type, link) VALUES ' + entry.links.forEach(link => { + if (typeof link === 'string') { + sql += `(${entryId},'related','${link}'),` + } else { + sql += `(${entryId},'${link.type}','${link.link}'),` + } + }) + sql = sql.substring(0, sql.length - 1) + dbc.query(sql, errq => { + if (errq) { + const msg = `WARN: error setting entry links:\n${errq}\n${sql}` + console.error(msg) + report.trace.push(msg) + report.warnings++ + } + cbw() + }) } ], errw => { @@ -443,22 +564,207 @@ function applyUpdates(dbc, ldict, done) { }, erre => { // entries callback - if (failed > 0) - console.error('Done with some entries failed:', failed, '/0', processed) - done(erre) + if (erre) return done(erre) + if (failed > 0) { + const msg = `Done with some entries failed: ${failed} out of ${ + failed + processed + }` + console.error(msg) + return done(msg) + } + done() } ) } -const sqlUpsertZRCConsultancy = - 'INSERT INTO consultancy_entry ' + - ' (id_external, status, time_published, institution, title, question, answer, answer_authors, domain_primary_id)' + - ' VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)' + - ' ON CONFLICT (id_external)' + - ' DO UPDATE SET status = $2, time_published = $3, title = $5, question = $6, answer = $7, answer_authors = $8, domain_primary_id = $9' +/*********************************************************************************************************************** + * ZRC Svetovalnica + */ /** - * Update consultancy entries/index for sites + * Convert row as parsed by npm xml-flow parser + * @param row + * @param domainPrimary + */ +function convertZrcConsultancyRow(row, domainPrimary) { + const targetRow = { + status: 'published', + institution: 'ZRC-SAZU', + description: 'Terminološka svetovalnica', + authors: [], + domain_primary_id: null + } + row.$markup.forEach(field => { + let domain = null + // debugDetail('processing field',field) + switch (field.$attrs.name) { + case 'nid': + targetRow.id_external = field.$markup.length ? field.$markup[0] : null + break + case 'title': + targetRow.title = field.$markup.length ? field.$markup.join(' ') : '' + break + case 'field_vprasanje_value': + targetRow.question = field.$markup.length ? field.$markup.join(' ') : '' + break + case 'body_value': + targetRow.answer = field.$markup.length ? field.$markup.join(' ') : '' + break + case 'changed': + // debugDetail('processing time_published',field) + targetRow.time_published = field.$markup.length + ? new Date(parseInt(field.$markup[0] * 1000)) + : null + break + case 'question_date': + // debugDetail('processing question_date',field) + targetRow.time_created = field.$markup.length + ? new Date(parseInt(field.$markup[0] * 1000)) + : null + break + case 'term_field_UDK': + domain = domainPrimary.find(d => { + return d.udk_code === field.$markup[0] + }) + if (domain) targetRow.domain_primary_id = domain.id + break + case 'term_field': + domain = domainPrimary.find(d => { + return d.name_sl === field.$markup[0] + }) + if (domain && !targetRow.domain_primary_id) + targetRow.domain_primary_id = domain.id + break + case 'authors': + field.$markup.forEach(author => { + targetRow.authors.push(author.$markup[0]) + }) + break + default: + break // ignore unmapped + } + }) + return targetRow +} + +/** + * persist a queue of consultancy records + * @param queue + * @param cb + */ +function persistSvetovalnica(queue, cb) { + async.eachOfSeries( + queue, + (qrow, x, cbrow) => { + // ' (id_external, consultancy_entry_status, time_published, institution, title, question, answer, answer_authors, domain_primary_id)' + + if (qrow === null) return cbrow() + if (!qrow.id_external) return cbrow() + dbc.query( + 'SELECT id FROM consultancy_entry WHERE id_external=$1', + [qrow.id_external], + (errdb, resultTest) => { + if (errdb) { + console.error( + 'WARN: error testing ZRC consultancy record', + qrow, + errdb + ) + return cbrow() + } + if (resultTest && resultTest.rows.length > 0) { + // record exists - > update it + // SET status = $1, time_published = $2, title = $3, question = $4, answer = $5, answer_authors = $6, + // domain_primary_id = $7, time_created = $8 + dbc.query( + sqlUpdateZRCConsultancy, + [ + qrow.status, + qrow.time_published, + qrow.title, + qrow.question, + qrow.answer, + qrow.authors, + qrow.domain_primary_id, + qrow.time_created, + resultTest.rows[0].id + ], + errdb => { + if (errdb) { + console.error( + 'WARN: error updating ZRC consultancy: ', + qrow, + errdb + ) + report.trace.push( + 'WARN: error updating ZRC consultancy: ' + errdb + ) + report.warnings++ + } + cbrow() + } + ) + } else { + // record doesn't exist - > insert it + dbc.query( + sqlInsertZRCConsultancy, + [ + qrow.id_external, + qrow.status, + qrow.time_published, + qrow.institution, + qrow.title, + qrow.question, + qrow.answer, + qrow.authors, + qrow.domain_primary_id, + qrow.time_created + ], + errdb => { + if (errdb) { + console.error( + 'WARN: error inserting ZRC consultancy: ', + qrow, + errdb + ) + report.trace.push( + 'WARN: error inserting consultancy: ' + errdb + ) + report.warnings++ + } + cbrow() + } + ) + } + } + ) + }, + qerr => { + if (qerr) console.error(qerr) + cb() + } + ) +} + +/* this is deprecated as it burns identity values +const sqlUpsertZRCConsultancy = + 'INSERT INTO consultancy_entry ' + + ' (id_external, status, time_published, institution, title, question, answer, answer_authors, domain_primary_id, time_created)' + + ' VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)' + + ' ON CONFLICT (id_external)' + + ' DO UPDATE SET status = $2, time_published = $3, title = $5, question = $6, answer = $7, answer_authors = $8, domain_primary_id = $9, time_created = $10' +*/ +const sqlInsertZRCConsultancy = + 'INSERT INTO consultancy_entry ' + + ' (id_external, status, time_published, institution, title, question, answer, answer_authors, domain_primary_id, time_created)' + + ' VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)' + +const sqlUpdateZRCConsultancy = + 'UPDATE consultancy_entry ' + + ' SET status = $1, time_published = $2, title = $3, question = $4, answer = $5, answer_authors = $6, domain_primary_id = $7, time_created = $8' + + ' WHERE id = $9' + +/** + * Update consultancy entries/index * that will have their consultancy linked to ZRC/Terminološka svetovalnica. * The data is available as full export on https://www.zrc-sazu.si/sl/sites/default/files/xml/term-exp.xml * Requires CULR installed on a machine. @@ -466,7 +772,8 @@ const sqlUpsertZRCConsultancy = function updateZrcSvetovalnica(cbf) { const file = path.join(__dirname, './term-exp.xml') let domainPrimary = null - let consultancyQueue = [] + const consultancyQueue = [] + let processMilis, indexMilis async.waterfall( [ function (cbw) { @@ -475,7 +782,7 @@ function updateZrcSvetovalnica(cbf) { "SELECT value FROM instance_settings WHERE name = 'consultancy_type'", [], (errq, result) => { - debug('fetched consultancy_type', errq, result) + debugDetail('fetched consultancy_type', errq, result) if (errq) return cbw(errq) if (result.rows[0].value !== 'ZRC') { return cbw('ZRC consultancy not enabled') @@ -490,7 +797,7 @@ function updateZrcSvetovalnica(cbf) { 'SELECT id, name_sl, udk_code FROM domain_primary', [], (errq, result) => { - debug('fetched domain_primary', errq, result) + debugDetail('fetched domain_primary', errq, result) if (errq) return cbw(errq) domainPrimary = result.rows cbw() @@ -512,13 +819,14 @@ function updateZrcSvetovalnica(cbf) { console.error(stderr) cbw(err) } else { - console.debug('done') + debugDetail('done download') cbw() } }) }, function (cbw) { debug('process source file') + const startProcess = Date.now() let targetRow const inFile = fs.createReadStream(file) const xmlStream = xmlFlow(inFile, { @@ -530,131 +838,52 @@ function updateZrcSvetovalnica(cbf) { }) xmlStream .on('tag:row', function (row) { - // debug(row); - targetRow = { - status: 'published', - institution: 'ZRC-SAZU', - description: 'Terminološka svetovalnica', - authors: [], - domain_primary_id: null - } - row.$markup.forEach(field => { - let domain = null - // debug('processing field',field) - switch (field.$attrs.name) { - case 'nid': - targetRow.id_external = field.$markup.length - ? field.$markup[0] - : null - break - case 'title': - targetRow.title = field.$markup.length - ? field.$markup.join(' ') - : '' - break - case 'field_vprasanje_value': - targetRow.question = field.$markup.length - ? field.$markup.join(' ') - : '' - break - case 'body_value': - targetRow.answer = field.$markup.length - ? field.$markup.join(' ') - : '' - break - case 'changed': - // debug('processing time_published',field) - targetRow.time_published = field.$markup.length - ? new Date(parseInt(field.$markup[0])) - : null - break - case 'term_field_UDK': - domain = domainPrimary.find(d => { - return d.udk_code === field.$markup[0] - }) - if (domain) targetRow.domain_primary_id = domain.id - break - case 'term_field': - domain = domainPrimary.find(d => { - return d.name_sl === field.$markup[0] - }) - if (domain && !targetRow.domain_primary_id) - targetRow.domain_primary_id = domain.id - break - case 'authors': - field.$markup.forEach(author => { - targetRow.authors.push(author.$markup[0]) - }) - break - default: - break // ignore unmapped - } - }) + targetRow = convertZrcConsultancyRow(row, domainPrimary) consultancyQueue.push(targetRow) - debug('row from xml', targetRow) - if (consultancyQueue.length > 10 && !xmlStream.isPaused) { - // above will accumulate the queue while here we're draining it - let lastX = -1 + // debugDetail('row from xml', targetRow) + if (consultancyQueue.length >= 10) { + if (xmlStream.isPaused) { + return // accumulating the queue while persisting + } + // persist a batch xmlStream.pause() const tempQueue = [] while (consultancyQueue.length > 0) tempQueue.push(consultancyQueue.shift()) - async.eachOfSeries( - tempQueue, - (qrow, x, cbrow) => { - // ' (id_external, consultancy_entry_status, time_published, institution, title, question, answer, answer_authors, domain_primary_id)' + - if (qrow === null) return cbrow() - lastX = x - dbc.query( - sqlUpsertZRCConsultancy, - [ - qrow.id_external, - qrow.status, - qrow.time_published, - qrow.institution, - qrow.title, - qrow.question, - qrow.answer, - qrow.authors, - qrow.domain_primary_id - ], - errdb => { - if (errdb) { - debug( - 'WARN: error upserting ZRC consultancy: ', - targetRow, - errdb - ) - report.trace.push( - 'WARN: error upserting translation: ' + errdb - ) - report.warnings++ - } - cbrow() - } - ) - }, - qerr => { - consultancyQueue = consultancyQueue.splice(0, lastX + 1) - xmlStream.resume() + debugDetail(`persisting batch of ${tempQueue.length}`) + persistSvetovalnica(tempQueue, errp => { + if (errp) { + console.warn(errp) } - ) + xmlStream.resume() + }) } }) .on('error', xerr => { console.error(xerr) }) .on('end', () => { - cbw() + debugDetail(`persisting final batch of ${consultancyQueue.length}`) + // delay last batch so that previous can be finished + setTimeout(() => { + processMilis = Date.now() - startProcess + persistSvetovalnica(consultancyQueue, cbw) + }, 2000) }) }, function (cbw) { + debug('reindexing') + const startIndex = Date.now() ConsultancyEntry.reindexAll() .catch(e => console.error('consultancy reindex failed:', e)) - .finally(() => cbw()) + .finally(() => { + indexMilis = Date.now() - startIndex + cbw() + }) } ], err => { + debug(`done, processing ${processMilis}ms, indexing ${indexMilis}ms`) cbf(err) } ) diff --git a/express/views/common/footer.pug b/express/views/common/footer.pug index 63fbd6a..255d959 100644 --- a/express/views/common/footer.pug +++ b/express/views/common/footer.pug @@ -1,35 +1,35 @@ -footer.footer-bg +footer.footer-bg.mt-5 .text-left.p-2.footer.footer-bg .wide-footer .d-flex.align-center img.rsmzk.align-self-center( src="/images/rsmzk.svg" - alt="REPUBLIKA SLOVENIJA MINISTRSTVO ZA KULTURO" + alt=t('REPUBLIKA SLOVENIJA MINISTRSTVO ZA KULTURO') ) .flex-grow-1.d-flex.justify-content-center - .my-auto.d-flex.flex-column.align-items-center.justify-content-center - span.d-block.txt.text-gray-2 Naložbo sofinancirata Republika Slovenija in Evropska unija iz Evropskega sklada za regionalni razvoj + .mx-3.my-auto.d-flex.flex-column.align-items-center.justify-content-center + span.d-block.txt.text-gray-2= t('Naložbo sofinancirata Republika Slovenija in Evropska unija iz Evropskega sklada za regionalni razvoj') a.d-block.txt.text-gray-2(href="https://www.eu-skladi.si") https://www.eu-skladi.si .d-flex.justify-content-end img.ekp( src="/images/EKP.svg" - alt="EVROPSKI SKLAD ZA REGIONALNI RAZVOJ" + alt=t('EVROPSKI SKLAD ZA REGIONALNI RAZVOJ') ) .narrow-footer .row .col-6.d-flex img.rsmzk.align-self-center( src="/images/rsmzk.svg" - alt="REPUBLIKA SLOVENIJA MINISTRSTVO ZA KULTURO" + alt=t('REPUBLIKA SLOVENIJA MINISTRSTVO ZA KULTURO') ) .col-6.d-flex.justify-content-end img.ekp( src="/images/EKP.svg" - alt="EVROPSKI SKLAD ZA REGIONALNI RAZVOJ" + alt=t('EVROPSKI SKLAD ZA REGIONALNI RAZVOJ') ) .row.padding-for-mobile-footer.mt-2 .col.justify-content-begin.d-flex .my-auto - span.d-block.txt.text-gray-2 Naložbo sofinancirata Republika Slovenija in Evropska unija iz Evropskega sklada za regionalni razvoj + span.d-block.txt.text-gray-2= t('Naložbo sofinancirata Republika Slovenija in Evropska unija iz Evropskega sklada za regionalni razvoj') a.d-block.txt.text-gray-2(href="https://www.eu-skladi.si") https://www.eu-skladi.si diff --git a/express/views/common/main-navigation-without-search.pug b/express/views/common/main-navigation-without-search.pug index 02925ac..71da80a 100644 --- a/express/views/common/main-navigation-without-search.pug +++ b/express/views/common/main-navigation-without-search.pug @@ -15,20 +15,20 @@ block main-navigation data-bs-toggle="dropdown" aria-expanded="false" ) - img.threedots(src="/images/threedots.svg" alt="") + img.threedots.mx-1(src="/images/threedots.svg" alt="") ul.dropdown-menu(aria-labelledby="dropdownMenuButton1") li - a.dropdown-item(href="/") Iskanje + a.dropdown-item(href="/")= t('Iskanje') if req.extractionEnabled li - a.dropdown-item(href="/luscenje") Luščenje + a.dropdown-item(href="/luscenje")= t('Luščenje') if req.dictionariesEnabled li - a.dropdown-item(href="/slovarji/moji") Urejanje + a.dropdown-item(href="/slovarji/moji")= t('Urejanje') if req.consultancyEnabled li - a.dropdown-item(href="#") Svetovanje + a.dropdown-item(href="#")= t('Svetovanje') li - a.dropdown-item(href="/admin/nastavitve/portal") Administracija + a.dropdown-item(href="/admin/nastavitve/portal")= t('Administracija') a.col-sm.nav-entry(href="#") img(src="/images/profile.svg" alt="") diff --git a/express/views/common/navigation-search-mixin.pug b/express/views/common/navigation-search-mixin.pug index 7653742..842ca6a 100644 --- a/express/views/common/navigation-search-mixin.pug +++ b/express/views/common/navigation-search-mixin.pug @@ -15,11 +15,11 @@ mixin navigation-search(search_included) placeholder="" value=`${searchString ? searchString : ''}` ) - button#keyboard.search-button-keyboard.me-0 + button#keyboard.search-button-keyboard.px-2.mx-2 span img.keyboard(src="/images/keyboard.svg" alt="Keys") //- - button#advanced-search.dropdown-btn + button#advanced-search.dropdown-btn.ms-2.px-2.me-3 img(src="/images/dropdown.svg" alt="Opt") .row .d-flex.justify-content-center @@ -42,7 +42,7 @@ mixin navigation-search-secondary(search_included) img.search_icon(src="/images/search.svg" alt="OK") input#search-query-sec.search-input.focused-for-enter.fw-300.placeholder-gray( type="text" - placeholder="Vpišite iskalni niz" + placeholder=t('Vpišite iskalni niz') value=`${searchString ? searchString : ''}` ) button#keyboard-sec.search-button-keyboard diff --git a/express/views/common/side-menu-fake.pug b/express/views/common/side-menu-fake.pug index 24fd5a8..0cefcdb 100644 --- a/express/views/common/side-menu-fake.pug +++ b/express/views/common/side-menu-fake.pug @@ -1,6 +1,8 @@ +//- TODO MARK FOR DELETION + mixin sideNavigationFake .admin-nav #admin-nav-mobile.admin-nav-mobile .mobile-left-holder.ms-3 - span#nav-title.nav-title Urejanje + span#nav-title.nav-title= t('Urejanje') #mobile-right-holder.d-flex diff --git a/express/views/common/side-menu-mixin-admin.pug b/express/views/common/side-menu-mixin-admin.pug index f61f8c4..fce4daa 100644 --- a/express/views/common/side-menu-mixin-admin.pug +++ b/express/views/common/side-menu-mixin-admin.pug @@ -7,7 +7,7 @@ mixin sideNavigation(sideNavigationData) src="/images/burger-menu-button-icon.svg" alt="Meni" ) - span#nav-title.nav-title Administrator + span#nav-title.nav-title= t('Administracija') #mobile-right-holder nav ul.admin-nav-content.slidable.scroller-style @@ -15,100 +15,100 @@ mixin sideNavigation(sideNavigationData) li.focused-menu a.active(href="/admin/nastavitve/portal") img(src="/images/cog.svg" alt="") - p Osnovne nastavitve + p= t('Osnovne nastavitve') ul.sub-links li a( href="/admin/nastavitve/portal" class=sideNavigationData.activeLvl2 === 'portals' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'portals' ? 'page' : false - ) Portal + )= t('Portal') li a( href="/admin/nastavitve/slovarji" class=sideNavigationData.activeLvl2 === 'dictionaries' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'dictionaries' ? 'page' : false - ) Slovarji + )= t('Slovarji') li a( href="/admin/nastavitve/svetovalnica" class=sideNavigationData.activeLvl2 === 'consultancy' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'consultancy' ? 'page' : false - ) Svetovalnica + )= t('Svetovalnica') else li.admin-nav-item a.active(href="/admin/nastavitve/portal") img(src="/images/cog.svg" alt="Settings") - p Osnovne nastavitve + p= t('Osnovne nastavitve') if sideNavigationData.activeLvl1 === 'connections' li.focused-menu a.active(href="/admin/povezave/seznam") img(src="/images/link.svg" alt="Atributes") - p Povezave + p= t('Povezave') ul.sub-links li a( href="/admin/povezave/seznam" class=sideNavigationData.activeLvl2 === 'list' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'list' ? 'page' : false - ) Seznam + )= t('Seznam') li a( href="/admin/povezave/slovarji" class=sideNavigationData.activeLvl2 === 'dictionaries' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'dictionaries' ? 'page' : false - ) Slovarji + )= t('Slovarji') else li.admin-nav-item a(href="/admin/povezave/seznam") img(src="/images/link.svg" alt="Atributes") - p Povezave + p= t('Povezave') if sideNavigationData.activeLvl1 === 'dictionaries' li.focused-menu a.active(href="/admin/slovarji") img(src="/images/book.svg" alt="Dictionaries") - p Slovarji + p= t('Slovarji') ul.sub-links li a( href="/admin/slovarji" class=sideNavigationData.activeLvl2 === 'list' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'list' ? 'page' : false - ) Seznam + )= t('Seznam') if (sideNavigationData.activeLvl2 === 'list') li.disabled-li-hover a( class=sideNavigationData.activeLvl2 === 'description' ? 'active' : 'disabled-menu' aria-current=sideNavigationData.activeLvl2 === 'description' ? 'page' : false - ) Osnovni podatki + )= t('Osnovni podatki') li.disabled-li-hover a( class=sideNavigationData.activeLvl2 === 'users' ? 'active' : 'disabled-menu' aria-current=sideNavigationData.activeLvl2 === 'users' ? 'page' : false - ) Uporabniki + )= t('Uporabniki') li.disabled-li-hover a( class=sideNavigationData.activeLvl2 === 'structure' ? 'active' : 'disabled-menu' aria-current=sideNavigationData.activeLvl2 === 'structure' ? 'page' : false - ) Struktura + )= t('Struktura') li.disabled-li-hover a( class=sideNavigationData.activeLvl2 === 'subareas' ? 'active' : 'disabled-menu' aria-current=sideNavigationData.activeLvl2 === 'subareas' ? 'page' : false - ) Področne oznake + )= t('Področne oznake') li.disabled-li-hover a( class=sideNavigationData.activeLvl2 === 'advanced' ? 'active' : 'disabled-menu' aria-current=sideNavigationData.activeLvl2 === 'advanced' ? 'page' : false - ) Napredno + )= t('Napredno') li.disabled-li-hover a( class=sideNavigationData.activeLvl2 === 'comments' ? 'active' : 'disabled-menu' aria-current=sideNavigationData.activeLvl2 === 'comments' ? 'page' : false - ) Komentarji + )= t('Komentarji') //- li.disabled-li-hover //- a( //- class=sideNavigationData.activeLvl2 === 'statistics' ? 'active' : 'disabled-menu' @@ -118,59 +118,59 @@ mixin sideNavigation(sideNavigationData) a( class=sideNavigationData.activeLvl2 === 'export' ? 'active' : 'disabled-menu' aria-current=sideNavigationData.activeLvl2 === 'export' ? 'page' : false - ) Izvoz + )= t('Izvoz') li.disabled-li-hover a( class=sideNavigationData.activeLvl2 === 'fileImport' ? 'active' : 'disabled-menu' aria-current=sideNavigationData.activeLvl2 === 'fileImport' ? 'page' : false - ) Uvoz iz datoteke + )= t('Uvoz iz datoteke') li.disabled-li-hover a( class=sideNavigationData.activeLvl2 === 'extractionImport' ? 'active' : 'disabled-menu' aria-current=sideNavigationData.activeLvl2 === 'extractionImport' ? 'page' : false - ) Uvoz iz luščilnika + )= t('Uvoz iz luščilnika') li.disabled-li-hover a( class=sideNavigationData.activeLvl2 === 'context' ? 'active' : 'disabled-menu' aria-current=sideNavigationData.activeLvl2 === 'context' ? 'page' : false - ) Vsebina + )= t('Vsebina') else li a( href=`/admin/slovarji/${dictionary.id}/podatki` class=sideNavigationData.activeLvl2 === 'description' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'description' ? 'page' : false - ) Osnovni podatki + )= t('Osnovni podatki') li a( href=`/admin/slovarji/${dictionary.id}/uporabniki` class=sideNavigationData.activeLvl2 === 'users' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'users' ? 'page' : false - ) Uporabniki + )= t('Uporabniki') li a( href=`/admin/slovarji/${dictionary.id}/struktura` class=sideNavigationData.activeLvl2 === 'structure' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'structure' ? 'page' : false - ) Struktura + )= t('Struktura') li a( href=`/admin/slovarji/${dictionary.id}/podrocne-oznake` class=sideNavigationData.activeLvl2 === 'subareas' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'subareas' ? 'page' : false - ) Področne oznake + )= t('Področne oznake') li a( href=`/admin/slovarji/${dictionary.id}/napredno` class=sideNavigationData.activeLvl2 === 'advanced' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'advanced' ? 'page' : false - ) Napredno + )= t('Napredno') li a( href=`/admin/slovarji/${dictionary.id}/komentarji` class=sideNavigationData.activeLvl2 === 'comments' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'comments' ? 'page' : false - ) Komentarji + )= t('Komentarji') //- li //- a( //- href="#" @@ -182,72 +182,72 @@ mixin sideNavigation(sideNavigationData) href=`/admin/slovarji/${dictionary.id}/izvoz` class=sideNavigationData.activeLvl2 === 'export' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'export' ? 'page' : false - ) Izvoz + )= t('Izvoz') li a( href=`/admin/slovarji/${dictionary.id}/uvoz/datoteka` class=sideNavigationData.activeLvl2 === 'fileImport' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'fileImport' ? 'page' : false - ) Uvoz iz datoteke + )= t('Uvoz iz datoteke') li a( href=`/admin/slovarji/${dictionary.id}/uvoz/luscenje` class=sideNavigationData.activeLvl2 === 'extractionImport' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'extractionImport' ? 'page' : false - ) Uvoz iz luščilnika + )= t('Uvoz iz luščilnika') li a( href=`/slovarji/${dictionary.id}/vsebina` class=sideNavigationData.activeLvl2 === 'context' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'context' ? 'page' : false - ) Vsebina + )= t('Vsebina') else li.admin-nav-item a.active(href="/admin/slovarji") img(src="/images/book.svg" alt="Dictionaries") - p Slovarji + p= t('Slovarji') if sideNavigationData.activeLvl1 === 'users' li.focused-menu a.active(href="/admin/uporabniki/portal") img(src="/images/users.svg" alt="Users") - p Uporabniki + p= t('Uporabniki') ul.sub-links li a( href="/admin/uporabniki/portal" class=sideNavigationData.activeLvl2 === 'portals' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'portals' ? 'page' : false - ) Portal + )= t('Portal') li a( href="/admin/uporabniki/seznam" class=sideNavigationData.activeLvl2 === 'list' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'list' ? 'page' : false - ) Seznam + )= t('Seznam') else li.admin-nav-item a(href="/admin/uporabniki/portal") img(src="/images/users.svg" alt="Users") - p Uporabniki + p= t('Uporabniki') if sideNavigationData.activeLvl1 === 'areas' li.focused-menu a.active(href="/admin/podpodrocja") img(src="/images/areas.svg" alt="Areas") - p Podpodročja + p= t('Podpodročja') else li.admin-nav-item a(href="/admin/podpodrocja") img(src="/images/areas.svg" alt="Areas") - p Podpodročja + p= t('Podpodročja') - if sideNavigationData.activeLvl1 === 'comments' + //- if sideNavigationData.activeLvl1 === 'comments' li.focused-menu a.active(href="/admin/komentarji") img(src="/images/u_comment-alt.svg" alt="Comments") p Komentarji - else + //- else li.admin-nav-item a(href="/admin/komentarji") img(src="/images/u_comment-alt.svg" alt="Comments") diff --git a/express/views/common/side-menu-mixin-consultancy.pug b/express/views/common/side-menu-mixin-consultancy.pug index 97f6769..204da0b 100644 --- a/express/views/common/side-menu-mixin-consultancy.pug +++ b/express/views/common/side-menu-mixin-consultancy.pug @@ -10,11 +10,11 @@ mixin sideNavigation(metaData) span#nav-title.nav-title= 'Svetovalnica' #mobile-right-holder nav - ul.admin-nav-content.slidable.z-20 + ul.admin-nav-content.slidable.z-20.scroller-style li a(href="/svetovanje") img(src="/images/chevrons-left.svg") - p Nazaj + p= t('Nazaj') if user.hasRole('consultancy admin') li(class=metaData.new ? 'focused-menu' : '') a( @@ -25,7 +25,7 @@ mixin sideNavigation(metaData) src=metaData.new ? '/images/star-blue.svg' : '/images/star.svg' alt="" ) - p Novo + p= t('Novo') li(class=metaData.rejected ? 'focused-menu' : '') a( href="/svetovanje/vprasanje/admin/zavrnjeno" @@ -35,7 +35,7 @@ mixin sideNavigation(metaData) src=metaData.rejected ? '/images/x-circle-blue.svg' : '/images/x-circle.svg' alt="" ) - p Zavrnjeno + p= t('Zavrnjeno') li(class=metaData.inProgress ? 'focused-menu' : '') a( href="/svetovanje/vprasanje/admin/v-delu" @@ -45,7 +45,7 @@ mixin sideNavigation(metaData) src=metaData.inProgress ? '/images/edit-blue.svg' : '/images/edit.svg' alt="" ) - p V delu + p= t('V delu') if user.hasRole('consultancy admin') li(class=metaData.prepared ? 'focused-menu' : '') a( @@ -56,7 +56,7 @@ mixin sideNavigation(metaData) src=metaData.prepared ? '/images/cog-blue.svg' : '/images/cog.svg' alt="" ) - p Pripravljeno + p= t('Pripravljeno') li(class=metaData.published ? 'focused-menu' : '') a( href="/svetovanje/vprasanje/admin/objavljeno" @@ -66,7 +66,7 @@ mixin sideNavigation(metaData) src=metaData.published ? '/images/book-open-blue.svg' : '/images/book-open.svg' alt="" ) - p Objavljeno + p= t('Objavljeno') //- li(class=metaData.stats ? 'focused-menu' : '') a( href="/svetovanje/vprasanje/admin/statistika" @@ -80,11 +80,11 @@ mixin sideNavigation(metaData) if user.hasRole('consultancy admin') li(class=metaData.users ? 'focused-menu' : '') a( - href="/svetovanje/vprasanje/admin/uporabniki" + href="/svetovanje/vprasanje/admin/svetovalci" class=metaData.users ? 'active' : '' ) img( src=metaData.users ? '/images/users-blue.svg' : '/images/users.svg' alt="" ) - p Svetovalci + p= t('Svetovalci') diff --git a/express/views/common/side-menu-mixin-dictionaries.pug b/express/views/common/side-menu-mixin-dictionaries.pug index d0c9a52..570c1f7 100644 --- a/express/views/common/side-menu-mixin-dictionaries.pug +++ b/express/views/common/side-menu-mixin-dictionaries.pug @@ -8,115 +8,115 @@ mixin sideNavigation(sideNavigationData) src="/images/burger-menu-button-icon.svg" alt="Meni" ) - span#nav-title.nav-title Urejanje + span#nav-title.nav-title= t('Urejanje') #mobile-right-holder nav - ul.admin-nav-content.slidable + ul.admin-nav-content.slidable.scroller-style //- li.side-menu-heading.mb-4 Urejanje if sideNavigationData.activeLvl1 === 'myDictionaries' li.focused-menu a.active(href="/slovarji/moji") img(src="/images/fi_folder.svg" alt="") - p Moji slovarji + p= t('Moji slovarji') else li.admin-nav-item a.active(href="/slovarji/moji") img(src="/images/fi_folder.svg" alt="") - p Moji slovarji + p= t('Moji slovarji') if sideNavigationData.activeLvl1 === 'attributes' li.focused-menu a.active(href=`/slovarji/${dictionary.id}/podatki`) img(src="/images/areas.svg" alt="Atributes") - p Lastnosti + p= t('Lastnosti') ul.sub-links li a( href=`/slovarji/${dictionary.id}/podatki` class=sideNavigationData.activeLvl2 === 'description' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'description' ? 'page' : false - ) Osnovni podatki + )= t('Osnovni podatki') li a( href=`/slovarji/${dictionary.id}/uporabniki` class=sideNavigationData.activeLvl2 === 'users' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'users' ? 'page' : false - ) Uporabniki + )= t('Uporabniki') li a( href=`/slovarji/${dictionary.id}/struktura` class=sideNavigationData.activeLvl2 === 'structure' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'structure' ? 'page' : false - ) Struktura + )= t('Struktura') li a( href=`/slovarji/${dictionary.id}/podrocne-oznake` class=sideNavigationData.activeLvl2 === 'areas' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'areas' ? 'page' : false - ) Področne oznake + )= t('Področne oznake') li a( href=`/slovarji/${dictionary.id}/napredno` class=sideNavigationData.activeLvl2 === 'advanced' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'advanced' ? 'page' : false - ) Napredno + )= t('Napredno') li a( href=`/slovarji/${dictionary.id}/komentarji` class=sideNavigationData.activeLvl2 === 'comments' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'comments' ? 'page' : false - ) Komentarji + )= t('Komentarji') else li.admin-nav-item a(href=`/slovarji/${dictionary.id}/podatki`) img(src="/images/areas.svg" alt="Atributes") - p Lastnosti + p= t('Lastnosti') if sideNavigationData.activeLvl1 === 'content' li.focused-menu a.active(href=`/slovarji/${dictionary.id}/vsebina`) img(src="/images/book.svg" alt="") - p Vsebina + p= t('Vsebina') else li.admin-nav-item a(href=`/slovarji/${dictionary.id}/vsebina`) img(src="/images/book.svg" alt="") - p Vsebina + p= t('Vsebina') if sideNavigationData.activeLvl1 === 'export' li.focused-menu a.active(href=`/slovarji/${dictionary.id}/izvoz`) img(src="/images/fi_download.svg" alt="") - p Izvoz + p= t('Izvoz') else li.admin-nav-item a(href=`/slovarji/${dictionary.id}/izvoz`) img(src="/images/fi_download.svg" alt="") - p Izvoz + p= t('Izvoz') if sideNavigationData.activeLvl1 === 'import' li.focused-menu a.active(href=`/slovarji/${dictionary.id}/uvoz/datoteka`) img(src="/images/fi_upload.svg" alt="") - p Uvoz + p= t('Uvoz') ul.sub-links li a( href=`/slovarji/${dictionary.id}/uvoz/datoteka` class=sideNavigationData.activeLvl2 === 'file' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'file' ? 'page' : false - ) Uvoz iz datoteke + )= t('Uvoz iz datoteke') li a( href=`/slovarji/${dictionary.id}/uvoz/luscenje` class=sideNavigationData.activeLvl2 === 'extraction' ? 'active' : false aria-current=sideNavigationData.activeLvl2 === 'extraction' ? 'page' : false - ) Uvoz iz luščilnika + )= t('Uvoz iz luščilnika') else li.admin-nav-item a(href=`/slovarji/${dictionary.id}/uvoz/datoteka`) img(src="/images/fi_upload.svg" alt="") - p Uvoz + p= t('Uvoz') //- Enako za ostale //- li //- a(href="urlDoStrani") Lastnosti diff --git a/express/views/components/admin/admin-navigation.pug b/express/views/components/admin/admin-navigation.pug index 32884ae..cd4cfd3 100644 --- a/express/views/components/admin/admin-navigation.pug +++ b/express/views/components/admin/admin-navigation.pug @@ -4,37 +4,37 @@ extends ../../common/side-navigation-common .admin-nav-mobile button#nav-button.nav-button img#burger-menu-img(src="/images/burger-menu-button-icon.svg" alt="Meni") - span.nav-title Administrator + span.nav-title= t('Administracija') nav - ul.admin-nav-content.slidable + ul.admin-nav-content.slidable.scroller-style li.admin-nav-item a(href="#") img(src="/images/cog.svg" alt="") - p Osnovne nastavitve + p= t('Osnovne nastavitve') li.admin-nav-item.focused-menu a.menu-selected(href="#") img(src="/images/link.svg" alt="") - p Povezave + p= t('Povezave') ul.sub-links li - a.menu-selected(href="#") Portali + a.menu-selected(href="#")= t('Portali') li - a(href="#") Domači slovarji + a(href="#")= t('Domači slovarji') li - a(href="#") Tuji slovarji + a(href="#")= t('Tuji slovarji') li.admin-nav-item a(href="#") img(src="/images/book.svg" alt="") - p Slovarji + p= t('Slovarji') li.admin-nav-item a(href="#") img(src="/images/users.svg" alt="") - p Uporabniki + p= t('Uporabniki') li.admin-nav-item a(href="#") img(src="/images/areas.svg" alt="") - p Področja + p= t('Področja') li.admin-nav-item a(href="#") img(src="/images/statistics.svg" alt="") - p Statistika + p= t('Statistika') diff --git a/express/views/components/consultancy/admin/consultancy-item-admin.pug b/express/views/components/consultancy/admin/consultancy-item-admin.pug index fd5e59b..019caeb 100644 --- a/express/views/components/consultancy/admin/consultancy-item-admin.pug +++ b/express/views/components/consultancy/admin/consultancy-item-admin.pug @@ -41,7 +41,7 @@ mixin consultancyItem(data, section) else .row.mt-2.mb-2 span.consultancy-description-text - b.text-p875rem= 'Opis terminološkega problema: ' + b.text-p875rem= t('Opis terminološkega problema: ') span.text-p875rem= data.description .row.mt-3.btn-group if section.inProgress @@ -51,57 +51,57 @@ mixin consultancyItem(data, section) type="button" ) img.i1p5rx1p5r(src="/images/u_edit-alt.svg" alt="") - span.ps-1 DODELI + span.ps-1 #{ t('DODELI') } .col-md-2 button.btn.btn-secondary.w-100.consultancy-modal-share( type="button" ) img.i1p5rx1p5r(src="/images/users.svg" alt="") - span.ps-1 DELI + span.ps-1 #{ t('DELI') } .col-md-2 button.btn.btn-secondary.w-100.reject-item img.i1p5rx1p5r(src="/images/x-circle-red.svg" alt="") - span.ps-1 ZAVRNI + span.ps-1 #{ t('ZAVRNI') } .col-md-2 button.edit-button.btn.btn-secondary.w-100 img.i1p5rx1p5r(src="/images/u_edit-alt.svg" alt="") - span.ps-1 UREDI + span.ps-1 #{ t('UREDI') } .col-md-2 button.btn.btn-primary.review-btn.w-100(disabled=!data.title) img.i1p5rx1p5r(src="/images/book-open-white.svg" alt="") - span.ps-1 OBJAVI + span.text-white.ps-1 #{ t('OBJAVI') } else if section.new .col-md-2 button.btn.btn-secondary.w-100.consultancy-modal-assign( type="button" ) img.i1p5rx1p5r(src="/images/u_edit-alt.svg" alt="") - span.ps-1 DODELI + span.ps-1 #{ t('DODELI') } .col-md-2 button.btn.btn-secondary.w-100.delete-item img.i1p5rx1p5r(src="/images/trash-2.svg" alt="") - span.ps-1 BRIŠI + span.ps-1 #{ t('BRIŠI') } else if section.rejected .col-md-2 button.btn.btn-secondary.w-100.consultancy-modal-assign( type="button" ) img.i1p5rx1p5r(src="/images/u_edit-alt.svg" alt="") - span.ps-1 DODELI + span.ps-1 #{ t('DODELI') } .col-md-2 button.btn.btn-secondary.w-100.delete-item img.i1p5rx1p5r(src="/images/trash-2.svg" alt="") - span.ps-1 BRIŠI + span.ps-1 #{ t('BRIŠI') } else if section.prepared .col-md-2 button.edit-button.btn.btn-secondary.w-100 img.i1p5rx1p5r(src="/images/u_edit-alt.svg" alt="") - span.ps-1 UREDI + span.ps-1 #{ t('UREDI') } if user.hasRole('consultancy admin') .col-md-2 button.btn.btn-primary.publish-btn.w-100(disabled=!data.title) img.i1p5rx1p5r(src="/images/book-open-white.svg" alt="") - span.ps-1 OBJAVI + span.text-white.ps-1 #{ t('OBJAVI') } else if section.published if user.hasRole('consultancy admin') .col-md-2 @@ -109,15 +109,15 @@ mixin consultancyItem(data, section) type="button" ) img.i1p5rx1p5r(src="/images/u_edit-alt.svg" alt="") - span.ps-1 DODELI + span.ps-1 #{ t('DODELI') } .col-md-2 button.edit-button.btn.btn-secondary.w-100 img.i1p5rx1p5r(src="/images/u_edit-alt.svg" alt="") - span.ps-1 UREDI + span.ps-1 #{ t('UREDI') } if user.hasRole('consultancy admin') .col-md-2 button.btn.btn-secondary.w-100.delete-item img.i1p5rx1p5r(src="/images/trash-2.svg" alt="") - span.ps-1 BRIŠI + span.ps-1 #{ t('BRIŠI') } .hovered.position-absolute diff --git a/express/views/components/consultancy/api/consultancy-item-rendered.pug b/express/views/components/consultancy/api/consultancy-item-rendered.pug new file mode 100644 index 0000000..dea1698 --- /dev/null +++ b/express/views/components/consultancy/api/consultancy-item-rendered.pug @@ -0,0 +1,13 @@ +if isAdminPage + include /components/consultancy/admin/consultancy-item-admin +else + include /components/consultancy/consultancy-item + +.consultancy-container + // To remove duplicates + //- - const moderatorList = entryList.filter(a => a.isModerator) // All published questions have a moderator... + each entry in entries + if isAdminPage + +consultancyItem(entry, section) + else + +consultancyItem(entry) diff --git a/express/views/components/consultancy/consultancy-item.pug b/express/views/components/consultancy/consultancy-item.pug index a479f7c..79a7da7 100644 --- a/express/views/components/consultancy/consultancy-item.pug +++ b/express/views/components/consultancy/consultancy-item.pug @@ -4,14 +4,16 @@ mixin consultancyItem(entry) .row.mt-4.mb-2 .col.d-flex.justify-content-start //- 9. 11. 2021 --> questionDateDMYformat - span.question-asked-time.text-p875rem= `Vprašanje poslano: ${entry.formattedTimeCreated}` + //- span.question-asked-time.text-p875rem= `Vprašanje poslano: ${entry.formattedTimeCreated}` + span.question-asked-time.text-p875rem= t('Vprašanje poslano:') + `${entry.formattedTimeCreated}` .row.mb-2 .col - h5-pb-0.navigation-text-color + h5.pb-0.title-cons b= entry.title .row //- span.consultancy-description-text - span.text-p875rem!= `Opis terminološkega problema: ${entry.question}` + span.text-p875rem.clipx!= `${t('Opis terminološkega problema')}: ${entry.question}` //- span.consultancy-description-text - span.text-p875rem!= `Odgovor: ${entry.answerSummary}` + //- span.text-p875rem.clipx!= `Odgovor: ${entry.answerSummary}` + span.text-p875rem.clipx!= t('Odgovor:') + `${entry.answerSummary}` .hovered.position-absolute diff --git a/express/views/components/dictionary/dictionary-list.pug b/express/views/components/dictionary/dictionary-list.pug index c872d90..1eb7a9e 100644 --- a/express/views/components/dictionary/dictionary-list.pug +++ b/express/views/components/dictionary/dictionary-list.pug @@ -6,26 +6,32 @@ mixin table(lstt= [{name: "Terminološki slovar fizike in astronomije", area: "B .row.g-0 .col-8.d-flex.justify-content-start.ps-4 button#sortByDictName.no-bg.no-border.text-decoration-none.navigation-text-color - span Ime + span= t('Ime') img#dictNameImg.invisible(src="images/arrow_drop_down.svg") .col-4.d-flex.justify-content-start button#sortByDomainName.no-bg.no-border.text-decoration-none.navigation-text-color - span Področje + span= t('Področje') img#domainNameImg.invisible(src="images/arrow_drop_down.svg") .col-1.row.d-flex.justify-content-end.g-0 //- .col-4.d-flex.justify-content-center Vir //- .col-4.d-flex.justify-content-center Opis .col.d-flex.justify-content-center - .no-bg.no-border.text-decoration-none.navigation-text-color.fw-400 Iskanje + .no-bg.no-border.text-decoration-none.navigation-text-color.fw-400= t('Iskanje') each val in lstt .row.table-dict-content.py-2.g-0(id=val.id) .col-11.row.g-0 .col-sm-8.d-flex.justify-content-start.text-p875rem.ps-4.fw-500 - a.d-flex.flex-row.text-decoration-none( - href=`/slovarji/${val.id}/o-slovarju?sentFromEntryId=dictsList` - ) - img.mt-1.d-flex.i16x16(src="/images/book.svg" alt="") - span.px-3.d-flex.flex-grow-1= val.dictionarysl + if val.count_comments != null + a.d-flex.flex-row.text-decoration-none.hover-opacity-blue( + href=`/slovarji/${val.id}/o-slovarju?sentFromEntryId=dictsList` + ) + img.mt-1.d-flex.i16x16(src="/images/book.svg" alt="") + span.px-3.d-flex.flex-grow-1= val.dictionarysl + else + .d-flex.flex-row.text-decoration-none + img.mt-1.d-flex.i16x16(src="/images/book.svg" alt="") + span.px-3.d-flex.flex-grow-1= val.dictionarysl + .col-sm-4.pe-3.d-flex.justify-content-start.text-p875rem.dpr= val.domain_primary .col-1.row.d-flex.justify-content-end.g-0 //- .col-4.d-flex.justify-content-center.align-items-center diff --git a/express/views/components/login-signup/login-signup.pug b/express/views/components/login-signup/login-signup.pug index c166e12..46ab42e 100644 --- a/express/views/components/login-signup/login-signup.pug +++ b/express/views/components/login-signup/login-signup.pug @@ -1,6 +1,6 @@ - - const registration_login_label = "registracija" - const registration_suggestion_label = "Še niste registriran uporabnik, registrirajte se." + const registration_login_label = t("registracija") + const registration_suggestion_label = t("Še niste registriran uporabnik, registrirajte se.") @@ -14,8 +14,8 @@ .modal-dialog.modal-dialog-centered.modal-fullscreen-sm-down.modal-lg .modal-content.ps-4.pe-4 .modal-header.mt-3 - h2.logintitle.modal-title.color-primary-text.fs-2 Prijava - h2.regtitle.d-none.modal-title.color-primary-text.fs-2 Registracija + h2.logintitle.modal-title.color-primary-text.fs-2= t('Prijava') + h2.regtitle.d-none.modal-title.color-primary-text.fs-2= t('Registracija') button.no-bg-and-borders( type="button" data-bs-dismiss="modal" @@ -23,8 +23,8 @@ ) img(src="/images/x.svg" alt="Zapri") #logreg-mb.modal-body.pt-0.pe-5 - p.logindesc.text-header-description-gray S prijavo pridobite možnost uporabe vseh funkcij terminološkega portala. - p.regdesc.d-none.text-header-description-gray Z registracijo pridobite možnost uporabe vseh funkcij terminološkega portala. + p.logindesc.text-header-description-gray= t('S prijavo pridobite možnost uporabe vseh funkcij terminološkega portala.') + p.regdesc.d-none.text-header-description-gray= t('Z registracijo pridobite možnost uporabe vseh funkcij terminološkega portala.') .pspelogsig-5 form#login-form( action="/api/v1/users/login" @@ -35,7 +35,7 @@ .col-2.d-flex.align-items-center.justify-content-center.pe-4 img(src="/images/user.svg" alt="") .col-8.input-floor.ps-0 - label.loginregisterlabel(for="login-username-or-email") UPORABNIK + label.loginregisterlabel(for="login-username-or-email")= t('UPORABNIK') input#login-username-or-email.d-block.w-100.input-lr( name="usernameOrEmail" ) @@ -48,7 +48,7 @@ .col-2.d-flex.align-items-center.justify-content-center.pe-4 img(src="/images/lock.svg" alt="") .col-8.input-floor.ps-0 - label.loginregisterlabel(for="login-password") GESLO + label.loginregisterlabel(for="login-password")= t('GESLO') input#login-password.d-block.w-100.input-lr( name="password" type="password" @@ -71,17 +71,17 @@ ) label.ps-2.align-middle.rememberloginlabel.text-header-description-gray.pe-2( for="login-remember-me" - ) Zapomni si prijavo + )= t('Zapomni si prijavo') .col-5.pt-2.d-flex-and-align-end.pe-0 - a.ms-auto.forgotten-password.gray1-text.no-text-decoration.align-middle( + a#forgotten-password.ms-auto.forgotten-password.gray1-text.no-text-decoration.align-middle( href="#" - ) Pozabljeno geslo + )= t('Pozabljeno geslo') .row.pt-0 .col - p#error-description.error-text.pt-3.mb-1 Nepravilno uporabniško ime, elektronski naslov ali geslo. + p#error-description.error-text.pt-3.mb-1= t('Nepravilno uporabniško ime, elektronski naslov ali geslo.') .row.pt-3 .col - button.btn.btn-primary.h41.w151 PRIJAVA + button.btn.btn-primary.h41.w151= t('PRIJAVA') .row.pt-3 .col p#login-message-container @@ -95,7 +95,7 @@ .col-2.d-flex.align-items-center.justify-content-center.pe-4 img(src="/images/user.svg" alt="") .col-8.input-floor.ps-0 - label.loginregisterlabel(for="register-username") UPORABNIK + label.loginregisterlabel(for="register-username")= t('UPORABNIK') input#register-username.w-100.input-lr(name="username") .col-2.input-floor.d-flex.justify-content-end.pe-0 @@ -105,12 +105,12 @@ ) .row.pt-1 .col.pe-0 - p#error-username.mb-0.error-text.text-end Uporabniško ime že obstaja + p#error-username.mb-0.error-text.text-end= t('Uporabniško ime že obstaja') .row.pt-0 .col-2.d-flex.align-items-center.justify-content-center.pe-4 img(src="/images/user.svg" alt="") .col-8.input-floor.ps-0 - label.loginregisterlabel(for="register-name") IME + label.loginregisterlabel(for="register-name")= t('IME') input#register-name.w-100.input-lr(name="firstName") .col-2.input-floor.d-flex.justify-content-end.pe-0 img.error-icon.error-name.align-self-end.d-flex( @@ -119,12 +119,12 @@ ) .row.pt-1 .col.pe-0 - p#error-name.mb-0.error-text.text-end Prazno obvezno polje + p#error-name.mb-0.error-text.text-end= t('Prazno obvezno polje') .row.pt-1 .col-2.d-flex.align-items-center.justify-content-center.pe-4 img(src="/images/user.svg" alt="") .col-8.input-floor.ps-0 - label.loginregisterlabel(for="register-surname") PRIIMEK + label.loginregisterlabel(for="register-surname")= t('PRIIMEK') input#register-surname.w-100.input-lr(name="lastName") .col-2.input-floor.d-flex.justify-content-end.pe-0 img.error-icon.error-surname.align-self-end.d-flex( @@ -133,12 +133,12 @@ ) .row.pt-1 .col.pe-0 - p#error-surname.mb-0.error-text.text-end Prazno obvezno polje + p#error-surname.mb-0.error-text.text-end= t('Prazno obvezno polje') .row.pt-1 .col-2.d-flex.align-items-center.justify-content-center.pe-4 img(src="/images/user.svg" alt="") .col-8.input-floor.ps-0 - label.loginregisterlabel(for="register-email") ELEKTRONSKI NASLOV + label.loginregisterlabel(for="register-email")= t('ELEKTRONSKI NASLOV') input#register-email.w-100.input-lr(name="email") .col-2.input-floor.d-flex.justify-content-end.pe-0 img.error-icon.error-email.align-self-end.d-flex( @@ -147,12 +147,12 @@ ) .row.pt-1 .col.pe-0 - p#error-email.mb-0.error-text.text-end Elektronski naslov že obstaja + p#error-email.mb-0.error-text.text-end= t('Elektronski naslov že obstaja') .row.pt-1 .col-2.d-flex.align-items-center.justify-content-center.pe-4 img(src="/images/lock.svg" alt="") .col-8.input-floor.ps-0 - label.loginregisterlabel(for="register-password") GESLO + label.loginregisterlabel(for="register-password")= t('GESLO') input#register-password.w-100.input-lr( name="password" type="password" @@ -164,12 +164,12 @@ ) .row.pt-1 .col.pe-0 - p#error-password.mb-0.error-text.text-end Geslo je prekratko + p#error-password.mb-0.error-text.text-end= t('Geslo je prekratko') .row.pt-1 .col-2.d-flex.align-items-center.justify-content-center.pe-4 img(src="/images/lock.svg" alt="") .col-8.input-floor.ps-0 - label.loginregisterlabel(for="register-password-repeat") PONOVI GESLO + label.loginregisterlabel(for="register-password-repeat")= t('PONOVI GESLO') input#register-password-repeat.w-100.input-lr( name="passwordRepeat" type="password" @@ -181,21 +181,21 @@ ) .row.pt-1 .col.pe-0 - p#error-password-repeat.mb-0.repeat.error-text.text-end Geslo se ne ujema + p#error-password-repeat.mb-0.repeat.error-text.text-end= t('Geslo se ne ujema') .row.mt-1.pt-0 .col-2.d-flex.align-items-center.justify-content-center.pe-4 .col-8.ps-0 .mb-2 input#terms-of-use.me-3(type="checkbox") - span Strinjam se s pogoji uporabe + span!= t('Strinjam se s pogoji uporabe') .mb-2 input#privacy-policy.me-3(type="checkbox") - span Soglašam s politiko zasebnosti + span!= t('Soglašam s politiko zasebnosti') .col-2.d-flex.justify-content-end.pe-0 .row .col-2.d-flex.ps-4.align-items-center.pe-4 .col-10.ps-0 - button.btn.btn-primary.h41.w151 Registracija + button#regbtnmain.btn.btn-primary.h41.w151(disabled)= ('Registracija') .row .col-sm-2.d-flex.ps-4.align-items-center .col-sm-10 @@ -214,8 +214,8 @@ //- img(src="/images/icon-user-x.svg" alt="") span.form-data - h6 Registracija - p Še niste registriran uporabnik, registrirajte se. + h6= t('Registracija') + p= t('Še niste registriran uporabnik, registrirajte se.') #loginbtn.registration.register-login-form.login.d-none button( style="border: none; background: none; text-decoration: none" @@ -225,7 +225,7 @@ //- img(src="/images/icon-user-x.svg" alt="") span.form-data - h6 Prijava - p Nazaj na prijavo + h6= t('Prijava') + p= t('Nazaj na prijavo') script(nonce=cspNonce src="/javascripts/login-and-register.js") diff --git a/express/views/components/main-panel/generic-main-panel-header.pug b/express/views/components/main-panel/generic-main-panel-header.pug index a48c474..d42442e 100644 --- a/express/views/components/main-panel/generic-main-panel-header.pug +++ b/express/views/components/main-panel/generic-main-panel-header.pug @@ -4,10 +4,7 @@ #chevrons-left.d-flex.align-items-start.justify-content-start .header-section-root .header-container-divider-left.pe-5 - h2 Naslov - h1 Podnaslov + h2= t('Naslov') + h1= t('Podnaslov') span.d-block.pe-5 - | Splošen opis področja. Anim duis u llamco Lorem reprehenderit. - | Reprehenderit aliqua quis ut velit eu irure non ad sunt sunt - | ipsum sunt esse. block right-section diff --git a/express/views/components/main-panel/implementations/one-button.pug b/express/views/components/main-panel/implementations/one-button.pug index d9e6eeb..0413972 100644 --- a/express/views/components/main-panel/implementations/one-button.pug +++ b/express/views/components/main-panel/implementations/one-button.pug @@ -1,6 +1,8 @@ +// TODO MARK FOR DELETION + extends ../generic-main-panel-header block right-section .header-container-divider-right button.optional-floating-action-button - | Besedilo gre tukaj + = t('Besedilo gre tukaj') diff --git a/express/views/components/main-panel/implementations/two-buttons.pug b/express/views/components/main-panel/implementations/two-buttons.pug index 478f2ff..087e10b 100644 --- a/express/views/components/main-panel/implementations/two-buttons.pug +++ b/express/views/components/main-panel/implementations/two-buttons.pug @@ -1,8 +1,10 @@ +// TODO MARK FOR DELETION + extends ../generic-main-panel-header block right-section .header-container-divider-right button.optional-floating-action-button - | Besedilo gre tukaj + = t('Besedilo gre tukaj') button.optional-floating-action-button - | Besedilo gre tukaj + = t('Besedilo gre tukaj') diff --git a/express/views/components/navigation/main-navigation-right-mixin.pug b/express/views/components/navigation/main-navigation-right-mixin.pug index 1878e3f..420e3c0 100644 --- a/express/views/components/navigation/main-navigation-right-mixin.pug +++ b/express/views/components/navigation/main-navigation-right-mixin.pug @@ -2,39 +2,41 @@ mixin main-navigation-right(languageToolbarPresent=false, menuTextPresent=false, li.nav-item .container //- - //- if languageToolbarPresent - a.col-sm.nav-entry.language-desktop-entry(href="#") + if languageToolbarPresent + a.col-sm.nav-entry.language-desktop-entry( + href=`/spremeni-jezik/${determinedLanguage === 'sl' ? 'en' : 'sl'}` + ) img(src="/images/globe.svg" alt="") - span#langtoggle.text-white SL + span#langtoggle.text-white= determinedLanguage === 'sl' ? 'EN' : 'SL' .col-sm.nav-entry.menu-link button#dropdownMenuButton1( type="button" data-bs-toggle="dropdown" aria-expanded="false" ) - img.threedots(src="/images/threedots.svg" alt="") + img.threedots.mx-0(src="/images/threedots.svg" alt="") if menuTextPresent - span.text-white.menutxt MENI + span.text-white.menutxt= t('MENI') ul.dropdown-menu(aria-labelledby="dropdownMenuButton1") li - a.dropdown-item(href="/") Iskanje + a.dropdown-item(href="/")= t('Iskanje') if extractionEnabled li - a.dropdown-item(href="/luscenje") Luščenje + a.dropdown-item(href="/luscenje")= t('Luščenje') if dictionariesEnabled li - a.dropdown-item(href="/slovarji/moji") Urejanje + a.dropdown-item(href="/slovarji/moji")= t('Urejanje') if consultancyEnabled li - a.dropdown-item(href="/svetovanje") Svetovanje + a.dropdown-item(href="/svetovanje")= t('Svetovanje') if user && (user.hasRole('portal admin') || user.hasRole('dictionaries admin')) li - a.dropdown-item(href="/admin") Administracija + a.dropdown-item(href="/admin")= t('Administracija') li - a.dropdown-item(href="/pomoc") Pomoč + a.dropdown-item(href="/pomoc")= t('Pomoč') if user - button#dropdownAccountButton.col-sm.nav-entry.no-border.no-bg.pe-3( + button#dropdownAccountButton.col-sm.nav-entry.no-border.no-bg.me-2rem( type="button" data-bs-toggle="dropdown" aria-expanded="false" @@ -43,7 +45,7 @@ mixin main-navigation-right(languageToolbarPresent=false, menuTextPresent=false, // .position-absolute // .green-dot if userTextPresent - span.text-white.signintxt.position-relative.left-2 RAČUN + span.text-white.signintxt.position-relative.left-2= t('RAČUN') ul.dropdown-menu.dropdown-menu-end.py-3.children-pspe-4.children-pt-pb-1.ul-children-hover( aria-labelledby="dropdownAccountButton" ) @@ -60,7 +62,7 @@ mixin main-navigation-right(languageToolbarPresent=false, menuTextPresent=false, .col-3 img(src="/images/cog.svg" alt="") .col-9 - span.text-header-description-gray.d-block Nastavitve + span.text-header-description-gray.d-block= t('Nastavitve') li.pb-0.mb-0.ps-3 form(action="/users/logout" method="post") button.profile-menu-button @@ -68,7 +70,7 @@ mixin main-navigation-right(languageToolbarPresent=false, menuTextPresent=false, .col-3 img(src="/images/log-out.svg" alt="") .col-9 - span.text-header-description-gray.d-block Odjava + span.text-header-description-gray.d-block= t('Odjava') else button#login-static-backdrop-button.col-sm.nav-entry.no-border.no-bg( href="#" @@ -78,4 +80,4 @@ mixin main-navigation-right(languageToolbarPresent=false, menuTextPresent=false, ) img(src="/images/profile.svg" alt="") if userTextPresent - span.text-white.signintxt.ps-2 PRIJAVA + span.text-white.signintxt.ps-2= t('PRIJAVA') diff --git a/express/views/components/search-and-filter/advanced-search-modal-area-only.pug b/express/views/components/search-and-filter/advanced-search-modal-area-only.pug index b97e68f..5414223 100644 --- a/express/views/components/search-and-filter/advanced-search-modal-area-only.pug +++ b/express/views/components/search-and-filter/advanced-search-modal-area-only.pug @@ -15,12 +15,13 @@ mixin consultancySM(adminFields=false) ) //- option.d-none(value="-1")= ' ' each domain in allPrimaryDomains - option(value=domain.id) #{ domain.nameSl } + option(value=domain.id selected=domain.selected) #{ domain.nameSl } span.advanced-input-label //- span.advanced-input-label Področje //- img#area-img(src="/images/chevron-down-darker.svg" alt="") span#area-suggestions.suggs + //- TODO Miha Stele to himself - good job! You've done it! You've made it! if adminFields span#consultant .d-flex.w-100 @@ -39,5 +40,5 @@ mixin consultancySM(adminFields=false) .advanced-search-final-options //- button Počisti filtre span - button#clear-in-filter-modal.btn.clear-f Počisti filtre - button#search-in-filter-modal.btn.search-btn-a Najdi + button#clear-in-filter-modal.btn.clear-f #{ t('Počisti filtre') } + button#search-in-filter-modal.btn.search-btn-a #{ t('Najdi') } diff --git a/express/views/components/search-and-filter/advanced-search-modal.pug b/express/views/components/search-and-filter/advanced-search-modal.pug index 9e30bf9..cc06272 100644 --- a/express/views/components/search-and-filter/advanced-search-modal.pug +++ b/express/views/components/search-and-filter/advanced-search-modal.pug @@ -23,9 +23,9 @@ //- input#src-lang-input.box-sizing-content.bg-transparent select#src-lang-select.select-src-lang-field(multiple="multiple") each language in sourceLanguages - option(value=language.id) #{ language.nameSl } + option(value=language.id selected=language.selected) #{ language.nameSl } span.advanced-input-label - //- span.advanced-input-label Izvorni jezik + //- span.advanced-input-label Jezik iskanja //- img#src-lang-img(src="/images/chevron-down-darker.svg" alt="") span#src-lang-suggestions.suggs span#dest-lang @@ -35,14 +35,14 @@ //- input#dest-lang-input.box-sizing-content.bg-transparent select#dest-lang-select.select-dest-lang-field(multiple="multiple") each language in targetLanguages - option(value=language.id) #{ language.nameSl } + option(value=language.id selected=language.selected) #{ language.nameSl } span.advanced-input-label //- span.advanced-input-label Ciljni jezik //- img#dest-lang-img(src="/images/chevron-down-darker.svg" alt="") span#dest-lang-suggestions.suggs //- span#src-lang //- span.sf-label.position-relative - //- span.position-absolute Izvorni jezik + //- span.position-absolute Jezik iskanja //- .resizable //- input(placeholder="" type="text") //- img(src="/images/chevron-down-darker.svg" alt="") @@ -62,7 +62,7 @@ multiple="multiple" ) each domain in allPrimaryDomains - option(value=domain.id) #{ domain.nameSl } + option(value=domain.id selected=domain.selected) #{ domain.nameSl } span.advanced-input-label //- span.advanced-input-label Področje //- img#area-img(src="/images/chevron-down-darker.svg" alt="") @@ -78,7 +78,7 @@ multiple="multiple" ) each dictionary in allDictionaryNames - option(value=dictionary.id) #{ dictionary.nameSl } + option(value=dictionary.id selected=dictionary.selected) #{ dictionary.nameSl } span.advanced-input-label //- span.advanced-input-label Slovar //- img#dict-img(src="/images/chevron-down-darker.svg" alt="") @@ -91,7 +91,7 @@ //- input#source-input.box-sizing-content.bg-transparent select#src-select.select-source-field(multiple="multiple") each portal in portals - option(value=portal.code) #{ portal.name } + option(value=portal.code selected=portal.selected) #{ portal.name } span.advanced-input-label //- span.advanced-input-label Vir //- img#source-img(src="/images/chevron-down-darker.svg" alt="") @@ -100,5 +100,5 @@ .advanced-search-final-options //- button Počisti filtre span - button#clear-in-filter-modal.btn.clear-f Počisti filtre - button#search-in-filter-modal.btn.search-btn-a Najdi + button#clear-in-filter-modal.btn.clear-f #{ t('Počisti filtre') } + button#search-in-filter-modal.btn.search-btn-a #{ t('Najdi') } diff --git a/express/views/components/search-and-filter/consultancy-results-content.pug b/express/views/components/search-and-filter/consultancy-results-content.pug new file mode 100644 index 0000000..39fe8c7 --- /dev/null +++ b/express/views/components/search-and-filter/consultancy-results-content.pug @@ -0,0 +1,10 @@ +- + let resultString = t("rezultatov") + if( consultancyHits == 3 || consultancyHits == 4 ) resultString = t("rezultati") + if( consultancyHits == 2) resultString = t("rezultata") + if( consultancyHits == 1) resultString = t("rezultat") +span.text-header-title-gray + span!= `${t('Svetovalnica')} ` + span= `- ${consultancyHits} ${resultString}` +a.mt-minus-p3(href=consultancyURL) + img.ms-2.px-1.mt-minus-p3(src="/images/fi_copy.svg") diff --git a/express/views/components/search-and-filter/inline-search.pug b/express/views/components/search-and-filter/inline-search.pug index bc8177d..ae844be 100644 --- a/express/views/components/search-and-filter/inline-search.pug +++ b/express/views/components/search-and-filter/inline-search.pug @@ -1,6 +1,6 @@ .classic-search-container .classic-search.border1px-gray-2 - input.input-search(type="text" placeholder="Išči") + input#input-search.input-search(type="text" placeholder=t('Išči')) .position-relative button#inline-search-btn.border-0.bg-transparent img(src="/images/search.svg" alt="") diff --git a/express/views/components/search-and-filter/result-item.pug b/express/views/components/search-and-filter/result-item.pug index 67fbeb8..6b65a94 100644 --- a/express/views/components/search-and-filter/result-item.pug +++ b/express/views/components/search-and-filter/result-item.pug @@ -16,9 +16,9 @@ mixin result-item(headword, entry, isLast=false) a.rl.py-3(href=`/termin/${entry.id}`) .anchor(id='e-' + entry.id) .result-item.d-flex.flex-row.w-100 - .row.w-100.result-items-row - .col-lg-4 - .row.w-100.ls + .row.w-100.result-items-row.g-0 + .col-lg-4.px-0 + .row.w-100.ls.g-0 .col-10.word-constraint-container .results-item-headword.d-flex.flex-grow-1.word-constraint.d-flex.flex-direction-column.w-100.flex-nowrap span.rihw.word-breakable.ps-2.long-text-wrap-r!= headword @@ -33,9 +33,25 @@ mixin result-item(headword, entry, isLast=false) .col-2.flex-column.row.rsc .results-comments.d-inline.me-4.align-top if slovenianDefinitionExists - img.bgmsg.mb-1(src="/images/message-square-black.svg") + //- tabindex="0" + img.bgmsg.mb-1( + data-bs-toggle="tooltip" + data-bs-title=t('Definicija') + data-bs-container="body" + data-bs-placement="right" + data-tooltip-content=t('Definicija') + src="/images/message-square-black.svg" + ) else if checkAllForeignDefinitions - img.bgmsg.mb-1(src="/images/message-square.svg") + //- tabindex="0" + img.bgmsg.mb-1( + data-bs-toggle="tooltip" + data-bs-title=t('Definicija') + data-bs-container="body" + data-bs-placement="right" + data-tooltip-content=t('Definicija') + src="/images/message-square.svg" + ) .col-lg-4.d-flex.px-0 //- .results-foreign-languages.d-inline-block.align-top ul.mt-1.px-0 @@ -67,8 +83,8 @@ mixin result-item(headword, entry, isLast=false) li.results-foreign-term.align-items-top.d-flex p.word-breakable.text-truncate.pb-0.mb-0.lh-18px.syn-h.long-text-wrap-r!= synonym - .col-lg-4 - .row + .col-lg-4.px-0 + .row.g-0.px-2 .col .results-keywords.d-flex.flex-grow-1.ms-2 span.res-domain= domain diff --git a/express/views/components/search-and-filter/result-panel-decision-mixin.pug b/express/views/components/search-and-filter/result-panel-decision-mixin.pug index 226de82..8283d8a 100644 --- a/express/views/components/search-and-filter/result-panel-decision-mixin.pug +++ b/express/views/components/search-and-filter/result-panel-decision-mixin.pug @@ -5,12 +5,12 @@ mixin decision(sel) a#headword.m-auto.d-inline-block.text-header-title-gray( href="#" class=sel == 0 ? 'selected' : '' - ) TERMIN + )= t('TERMIN') a#translation.m-auto.ps-3.d-inline-block.text-header-title-gray( href="#" class=sel == 1 ? 'selected' : '' - ) TUJI TERMIN + )= t('TUJI TERMIN') a#other.m-auto.ps-3.d-inline-block.text-header-title-gray( href="#" class=sel == 2 ? 'selected' : '' - ) DRUGO + )= t('DRUGO') diff --git a/express/views/components/search-and-filter/result-panel.pug b/express/views/components/search-and-filter/result-panel.pug index 0d6930e..b2590ce 100644 --- a/express/views/components/search-and-filter/result-panel.pug +++ b/express/views/components/search-and-filter/result-panel.pug @@ -8,27 +8,38 @@ mixin result-section(list, title) mixin result-sections +result-section(entriesByCategory.uncategorized, "") - +result-section(entriesByCategory.byTerm, "ISKANI NIZ JE BIL NAJDEN V IZTOČNICAH SLOVARSKIH SESTAVKOV") - +result-section(entriesByCategory.byForeignTerm, "ISKANI NIZ JE BIL NAJDEN V TUJEJEZIČNIH USTREZNIKIH SLOVARSKIH SESTAVKOV") - +result-section(entriesByCategory.byOther, "ISKANI NIZ JE BIL NAJDEN V DRUGI VSEBINI SLOVARSKIH SESTAVKOV") + +result-section(entriesByCategory.byTerm, `${t('ISKANI NIZ JE BIL NAJDEN')} ${t('V IZTOČNICAH')} ${t('SLOVARSKIH SESTAVKOV')}`) + +result-section(entriesByCategory.byForeignTerm, `${t('ISKANI NIZ JE BIL NAJDEN')} ${t('V TUJEJEZIČNIH USTREZNIKIH')} ${t('SLOVARSKIH SESTAVKOV')}`) + +result-section(entriesByCategory.byOther, `${t('ISKANI NIZ JE BIL NAJDEN')} ${t('V DRUGI VSEBINI')} ${t('SLOVARSKIH SESTAVKOV')}`) mixin result-panel(rpList) #res-panel.results-root.w-100.ms-0 #chevrons-left.d-flex .header-container-divider-left - h1#site-heading Seznam zadetkov + h1#site-heading= t('Seznam zadetkov') hr#disposable-break.mt-0 //- include ./result-panel-decision-mixin //- +decision(0) + include /utilities/pager .results-quickstats.row.align-items-center .row.pe-0.align-items-center - .col-md-6 - span.text-header-title-gray Rezultati: #{ numberOfAllHits } - .col-md-6.mx-auto.text-center.d-flex.justify-content-end.pe-0 - include /utilities/pager - +pager(page) + .col-md-3 + span.text-header-title-gray= t('Rezultati: ') + numberOfAllHits + .col-md-5 + if (consultancyHits > 0) + #consulancy-results-container + include /components/search-and-filter/consultancy-results-content + .col-md-4.mx-auto.text-center.d-flex.justify-content-end.pe-0 + +pager(page, 'pagination-top') #results-data.results-data.mt-4 include ./result-item +result-sections + + .results-quickstats.row.align-items-center.mt-4 + .row.pe-0.align-items-center + .col-md-6 + span.text-header-title-gray= t('Rezultati: ') + numberOfAllHits + .col-md-6.mx-auto.text-center.d-flex.justify-content-end.pe-0 + +pager(page, 'pagination-bottom') diff --git a/express/views/components/search-and-filter/search-filter-modal.pug b/express/views/components/search-and-filter/search-filter-modal.pug index 2c8d316..d67580a 100644 --- a/express/views/components/search-and-filter/search-filter-modal.pug +++ b/express/views/components/search-and-filter/search-filter-modal.pug @@ -8,7 +8,7 @@ section.filter-modal-root .modal-content .modal-header.flex-wrap.pb-2 h5#modal-filter-label.modal-title.maincolor-text - | #{ "PLACEHOLDER" } + | #{ "Filter" } button.btn-close( type="button" data-bs-dismiss="modal" @@ -16,13 +16,13 @@ section.filter-modal-root ) .flex.w-100.search-modal-header-root span.search-modal-filter - input(type="text") - span + input#filterSfmText.rm-outline.onEnterListener(type="text") + button#filter-sfm.no-border.no-bg img(src="/images/search_blue.svg" alt="") span.d-flex.flex-grow-1.align-content-center a#ccf.clear-filter-modal-section(href="#") img(src="/images/square-minus.svg" alt="") - span POČISTI FILTRE + span.ps-2 POČISTI FILTRE .modal-body.pb-2 ul#modal-list.nav-modal-section-content.mb-0.pb-0 // Added dynamically... diff --git a/express/views/components/search-and-filter/search-filter-navigation-mixin.pug b/express/views/components/search-and-filter/search-filter-navigation-mixin.pug index 7dff52d..ca1bb78 100644 --- a/express/views/components/search-and-filter/search-filter-navigation-mixin.pug +++ b/express/views/components/search-and-filter/search-filter-navigation-mixin.pug @@ -4,15 +4,15 @@ mixin sideMenuEntry(id, selectMoreId , title, content, contentRoot, disabledSect .nav-section-header span(class=disabledSection ? 'disabled-opacity' : '')= title span.btn-area - button.btn.btn-primary( + button.border-0( id=selectMoreId type="button" data-bs-toggle="modal" data-bs-target="#modal-filter" disabled=disabledSection ) - span.select-more-text IZBERI VEČ - div + span.select-more-text= t('IZBERI VEČ') + .ps-2 img(src="/images/fi_copy.svg" alt="") ul.nav-section-content(id=id) //- root struct check in case of empty results @@ -35,7 +35,7 @@ mixin sideMenuEntry(id, selectMoreId , title, content, contentRoot, disabledSect if isAtLeastOneChecked a.showall(href="#" id='show-all-' + id) img(src="/images/chevron-left-blue.svg" alt="") - span PRIKAŽI VSE + span= t('PRIKAŽI VSE') mixin sfSideNavigation(sideNavigationData) .admin-nav @@ -46,17 +46,17 @@ mixin sfSideNavigation(sideNavigationData) src="/images/burger-menu-button-icon.svg" alt="Meni" ) - span#nav-title.nav-title Iskanje po slovarjih + span#nav-title.nav-title= t('Iskanje po slovarjih') #mobile-right-holder nav .admin-nav-content.slidable.sf-min-h.nav-bg.ps-2rem.sf-correct - .search-filter-nav-content.w24rem.me-0.ps-2rem + .search-filter-nav-content.w24rem.me-0.ps-2rem.pe-0 .slidable-list.scroller-style a#clear-filters.clear-filter-section.d-flex(href="#") img.align-self-center(src="/images/square-minus.svg" alt="") - span.fs-075rem.fw-400 POČISTI FILTRE - +sideMenuEntry("src-lang-side", "select-src-langs", "Izvorni jeziki", sideNavigationData.sourceLanguages, sideNavigationData, disabledSideMenuFilters.sourceLanguages) - +sideMenuEntry("dest-lang-side", "select-dest-langs", "Ciljni jeziki", sideNavigationData.targetLanguages, sideNavigationData, disabledSideMenuFilters.targetLanguages) - +sideMenuEntry("domain-side", "select-domains", "Področja", sideNavigationData.primaryDomains, sideNavigationData, disabledSideMenuFilters.primaryDomains) - +sideMenuEntry("dict-side", "select-dicts", "Slovarji", sideNavigationData.dictionaries, sideNavigationData, disabledSideMenuFilters.dictionaries) - +sideMenuEntry("source-side", "select-sources", "Viri", sideNavigationData.sources, sideNavigationData, disabledSideMenuFilters.sources) + span.ps-2.fs-075rem.fw-400= t('POČISTI FILTRE') + +sideMenuEntry("src-lang-side", "select-src-langs", t("Jeziki iskanja"), sideNavigationData.sourceLanguages, sideNavigationData, disabledSideMenuFilters.sourceLanguages) + +sideMenuEntry("dest-lang-side", "select-dest-langs", t("Ciljni jeziki"), sideNavigationData.targetLanguages, sideNavigationData, disabledSideMenuFilters.targetLanguages) + +sideMenuEntry("domain-side", "select-domains", t("Področja"), sideNavigationData.primaryDomains, sideNavigationData, disabledSideMenuFilters.primaryDomains) + +sideMenuEntry("dict-side", "select-dicts", t("Slovarji"), sideNavigationData.dictionaries, sideNavigationData, disabledSideMenuFilters.dictionaries) + +sideMenuEntry("source-side", "select-sources", t("Viri"), sideNavigationData.sources, sideNavigationData, disabledSideMenuFilters.sources) diff --git a/express/views/components/search-and-filter/search-filter-navigation.pug b/express/views/components/search-and-filter/search-filter-navigation.pug index 310c460..66ae402 100644 --- a/express/views/components/search-and-filter/search-filter-navigation.pug +++ b/express/views/components/search-and-filter/search-filter-navigation.pug @@ -2,24 +2,24 @@ .admin-nav-mobile button#nav-button.nav-button img#burger-menu-img(src="/images/burger-menu-button-icon.svg" alt="Meni") - span.nav-title Filtri + span.nav-title= t('Filtri') nav - .admin-nav-content.slidable + .admin-nav-content.slidable.scroller-style .search-filter-nav-content a.clear-filter-section(href="#") img(src="/images/square-minus.svg" alt="") - span POČISTI FILTRE + span= t('POČISTI FILTRE') .nav-section hr .nav-section-header - span Izvorni jeziki + span= t('Jeziki iskanja') span.btn-area button.btn.btn-primary( type="button" data-bs-toggle="modal" data-bs-target="#modal-filter" ) - span.select-more-text IZBERI VEČ + span.select-more-text= t('IZBERI VEČ') div img(src="/images/fi_copy.svg" alt="") ul.nav-section-content @@ -28,37 +28,37 @@ a#sl(href="#") div img(src="/images/chevron-right.svg" alt="") - span.nav-section-content-desc Slovenščina + span.nav-section-content-desc= t('Slovenščina') span 461 li a#hr(href="#") div img(src="/images/chevron-right.svg" alt="") - span.nav-section-content-desc Hrvaščina + span.nav-section-content-desc= t('Hrvaščina') span 461 li a#it(href="#") div img(src="/images/chevron-right.svg" alt="") - span.nav-section-content-desc Italijanščina + span.nav-section-content-desc= t('Italijanščina') span 461 li a(href="#") div img(src="/images/chevron-right.svg" alt="") - span.nav-section-content-desc Slovenščina + span.nav-section-content-desc= t('Slovenščina') span 461 .nav-section hr .nav-section-header - span Slovarji + span= t('Slovarji') span.btn-area button.btn.btn-primary( type="button" data-bs-toggle="modal" data-bs-target="#modal-filter" ) - span.select-more-text IZBERI VEČ + span.select-more-text= t('IZBERI VEČ') div img(src="/images/fi_copy.svg" alt="") ul.nav-section-content @@ -67,7 +67,7 @@ a(href="#") div img(src="/images/chevron-right.svg" alt="") - span.nav-section-content-desc Nemško-slovenski slovar tehnike in naravoslovja + span.nav-section-content-desc= t('Nemško-slovenski slovar tehnike in naravoslovja') span 111 li a(href="#") diff --git a/express/views/components/search-and-filter/search-with-primary-domain-filter.pug b/express/views/components/search-and-filter/search-with-primary-domain-filter.pug index 0cbcab9..f95143c 100644 --- a/express/views/components/search-and-filter/search-with-primary-domain-filter.pug +++ b/express/views/components/search-and-filter/search-with-primary-domain-filter.pug @@ -1,4 +1,4 @@ -mixin searchWithPrimaryDomainFilter(formId, searchId, searchBtnId, initialMarginClass="mt-3") +mixin searchWithPrimaryDomainFilter(formId, searchId, searchBtnId, initialMarginClass="mt-3", filledText="") form.needs-validation.w-100(id=formId class=initialMarginClass) - let checkrightSection = headerContent && headerContent.buttons && headerContent.buttons.length .header-consultancy-divider.d-flex.d-column.w-100.row.g-0.ms-0 @@ -12,9 +12,10 @@ mixin searchWithPrimaryDomainFilter(formId, searchId, searchBtnId, initialMargin .search-bar.height-46p(tabindex="0") button.searchbar-icon-container(id=searchBtnId type="submit") img.search_icon(src="/images/search.svg" alt="OK") - input#search-query.search-input( + input#search-query.search-input.pdf( type="text" - placeholder=isDictionaryListPage ? 'Ime slovarja' : 'Vpišite iskalni niz' + placeholder=isDictionaryListPage ? t('Ime slovarja') : t('Vpišite iskalni niz') + value=filledText ) button#keyboard.search-button-keyboard span diff --git a/express/views/components/search-and-filter/small-device-search.pug b/express/views/components/search-and-filter/small-device-search.pug index c7fe7e7..6049380 100644 --- a/express/views/components/search-and-filter/small-device-search.pug +++ b/express/views/components/search-and-filter/small-device-search.pug @@ -12,7 +12,7 @@ mixin mini-searchbar img.search_icon(src="/images/search.svg" alt="OK") input#search-query.search-input.focused-for-enter.fw-300.placeholder-gray( type="text" - placeholder="Vpišite iskalni niz" + placeholder=t('Vpišite iskalni niz') ) button#keyboard.search-button-keyboard span diff --git a/express/views/email/consultancy-creation-notify.pug b/express/views/email/consultancy-creation-notify.pug index 0b540c0..476fb07 100644 --- a/express/views/email/consultancy-creation-notify.pug +++ b/express/views/email/consultancy-creation-notify.pug @@ -1 +1,23 @@ -p #{ propertyToPassGoesHere } 1234567891011121314151617181920 +p Ime in primek: #{ nameAndSurname } + br + span E-naslov: #{ email } + br + span Institucija: #{ institution } + br + br + span Področje: #{ domain } + +p + b Opis teminološkega problema: + br + span #{ question } + +p + b Morebitne že obstoječe poimenovalne rešitve: + br + span #{ existingSolutions } + +p + b Morebitni primeri rabe termina v besedilih ali povezave do njih: + br + span #{ examplesOfUse } diff --git a/express/views/email/consultancy-publish-notify_en.pug b/express/views/email/consultancy-publish-notify_en.pug new file mode 100644 index 0000000..88e2319 --- /dev/null +++ b/express/views/email/consultancy-publish-notify_en.pug @@ -0,0 +1,2 @@ +p Hello, +p The answer to your terminology question has been published on the "#{ portalName }". diff --git a/express/views/email/consultancy-publish-notify_sl.pug b/express/views/email/consultancy-publish-notify_sl.pug new file mode 100644 index 0000000..7917629 --- /dev/null +++ b/express/views/email/consultancy-publish-notify_sl.pug @@ -0,0 +1,2 @@ +p Pozdravljeni, +p na portalu "#{ portalName }" je objavljen odgovor na vaše terminološko vprašanje. diff --git a/express/views/email/dictionary-status-change.pug b/express/views/email/dictionary-status-change.pug index de5fd7e..f28ed98 100644 --- a/express/views/email/dictionary-status-change.pug +++ b/express/views/email/dictionary-status-change.pug @@ -1,13 +1,12 @@ -//- TODO implement real text p Pozdravljeni, case type when 'status' p oseba: #[b #{ changerEmail }] je spremenila stanje vašega slovarja: #[b #{ nameSl }]. when 'unpublish' - p tralala hopsasa oseba: #[b #{ changerEmail }] se je igrala in zato vaš slovar: #[b #{ nameSl }] ni več objavljen. + p oseba: #[b #{ changerEmail }] je spremenila status slovarja #[b #{ nameSl }], ki ni več objavljen. when 'delete' - p tralala hopsasa slovar: #[b #{ nameSl }] ima manjše število gesel kot je nastavljeno. + p slovar: #[b #{ nameSl }] ima manjše število gesel kot je nastavljeno. when 'publish-approval' - p slovar #[b #{ nameSl }] je bil dan v stanje preverjanja slovarja... Prosimo, da ga odprete/zaprete. + p slovar #[b #{ nameSl }] je bil dan v stanje preverjanja. Prosimo, da ga odprete/zaprete. when 'publish-no-approval' p slovar #[b #{ nameSl }] je bil objavljen. diff --git a/express/views/email/extraction-done_en.pug b/express/views/email/extraction-done_en.pug new file mode 100644 index 0000000..cd5c76e --- /dev/null +++ b/express/views/email/extraction-done_en.pug @@ -0,0 +1,2 @@ +p Hello, +p You extraction: #[a(href=extractionLink)= extractionName] on the Slovenian Terminology Portal has been completed. diff --git a/express/views/email/extraction-done.pug b/express/views/email/extraction-done_sl.pug similarity index 100% rename from express/views/email/extraction-done.pug rename to express/views/email/extraction-done_sl.pug diff --git a/express/views/email/user-activation_en.pug b/express/views/email/user-activation_en.pug new file mode 100644 index 0000000..804b396 --- /dev/null +++ b/express/views/email/user-activation_en.pug @@ -0,0 +1,4 @@ +p Dear #{ username }, +p You have received this massage because a new user account has been created on the Slovenian Terminology Portal using this e-mail address. +p To activate your account, you must confirm that this is the correct e-mail address. The link will be active for 7 days. +a(href=activationLink) I confirm my e-mail address. diff --git a/express/views/email/user-activation.pug b/express/views/email/user-activation_sl.pug similarity index 100% rename from express/views/email/user-activation.pug rename to express/views/email/user-activation_sl.pug diff --git a/express/views/extraction-poc/docs-edit.pug b/express/views/extraction-poc/docs-edit.pug index dbc3281..19e999d 100644 --- a/express/views/extraction-poc/docs-edit.pug +++ b/express/views/extraction-poc/docs-edit.pug @@ -1,3 +1,5 @@ +//- TODO MARK FOR DELETION + h1 Dokumenti za luščenje #{ id } h2 Seznam datotek diff --git a/express/views/extraction-poc/list.pug b/express/views/extraction-poc/list.pug index 58a3955..9a65ff2 100644 --- a/express/views/extraction-poc/list.pug +++ b/express/views/extraction-poc/list.pug @@ -31,7 +31,7 @@ ul#extraction-list a(href=`poc/korpus/${e.corpusId}` target="_blank") Uporabniški korpus if e.status === 'finished' && e.ossParams a( - href="https://www.clarin.si/noske/run.cgi/corp_info?corpname=oss&struct_attr_stats=1" + href="https://www.clarin.si/ske/#dashboard?corpname=oss" target="_blank" ) Korpus KAS+ | ) (ZAČETEK: #[span.extraction-time-started= e.timeStarted ? e.timeStarted.toLocaleString('sl-SI') : 'ni še bilo začeto'], diff --git a/express/views/layout.pug b/express/views/layout.pug index 551dfa0..e6cb624 100644 --- a/express/views/layout.pug +++ b/express/views/layout.pug @@ -1,5 +1,5 @@ doctype html -html +html(lang=language) head meta(charset="utf-8") meta(name="viewport" content="width=device-width,initial-scale=1") @@ -74,6 +74,8 @@ html title= title ? `${portalCode} | ${title}` : 'Terminološki portal' body include /components/login-signup/login-signup + include /utilities/modal-reset-password + include /utilities/modal-reset-password-info block body script( nonce=cspNonce @@ -85,6 +87,21 @@ html integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous" ) + script( + nonce=cspNonce + src="https://unpkg.com/i18next@22.4.10/dist/umd/i18next.min.js" + integrity="sha512-hqu1oKvqYx/BBOipLeaeoeXhUTWhQHKWWLHKD+ewvzxWKD0jk6Z1NAYvvT/pHVvCl8Ccpxdt9Er2v5cj7xJ1Rw==" + crossorigin="anonymous" + ) + script( + nonce=cspNonce + src="https://cdn.jsdelivr.net/npm/i18next-http-backend@2.1.1/i18nextHttpBackend.min.js" + integrity="sha512-cL7Wvz326H2GY4YHfTEHtpy21oPBNdJVPgBdmJgVQW0/KkJhZh1hP8hioaJ4CMiHArq5jwecwZ+s/5Y6Mx3QEg==" + crossorigin="anonymous" + ) + + if inDevEnv + script(nonce=cspNonce) window.inDevEnv = true script(nonce=cspNonce src="/javascripts/globals.js") script(nonce=cspNonce src="/javascripts/scripts.js") script(nonce=cspNonce src="/javascripts/main-sreach-utils.js") diff --git a/express/views/pages/admin-dict-structure-demo.pug b/express/views/pages/admin-dict-structure-demo.pug index 3ff9b2c..c493236 100644 --- a/express/views/pages/admin-dict-structure-demo.pug +++ b/express/views/pages/admin-dict-structure-demo.pug @@ -1,5 +1,6 @@ extends ../layout +//- TODO MARK FOR DELETION block body section include ../common/main-navigation diff --git a/express/views/pages/admin.pug b/express/views/pages/admin.pug index f598609..38c0719 100644 --- a/express/views/pages/admin.pug +++ b/express/views/pages/admin.pug @@ -1,5 +1,6 @@ extends ../layout +//- TODO MARK FOR DELETION //- TODO check if deprecated block body section diff --git a/express/views/pages/admin/areas.pug b/express/views/pages/admin/areas.pug index 5b16010..ffce32b 100644 --- a/express/views/pages/admin/areas.pug +++ b/express/views/pages/admin/areas.pug @@ -12,15 +12,13 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Podpodročja', h1: 'Podpodročja', description: 'Podpodročja so namenjena podrobnejšemu razvrščanju terminoloških slovarjev. Administrator mora vsa novo predlagana podpodročja potrditi, preden jih lahko na seznamu vidijo tudi drugi uporabniki portala.', buttons: [{ form: 'dictionary-domain-labels', type: 'disabled', content: 'Shrani' }] } + - const c = { sideMenu: true, h2: t('Podpodročja'), h1: t('Podpodročja'), description: t('Podpodročja so namenjena podrobnejšemu razvrščanju terminoloških slovarjev. Administrator mora vsa novo predlagana podpodročja potrditi, preden jih lahko na seznamu vidijo tudi drugi uporabniki portala.'), buttons: [{ form: 'dictionary-domain-labels', type: 'disabled', content: t('Shrani') }] } +mainHeader(c) .content-hold-prerequisites - #offset-main.main-container.mt-2 + #offset-main.main-container if results.length - .d-flex.justify-content-between.mt-4 - .d-flex.flex-row.mb-4 - include /components/search-and-filter/inline-search + .d-flex.justify-content-end.mt-4 .d-flex include /utilities/pager +pager @@ -31,9 +29,9 @@ block body table#all-areas-table.table.areas-table.table-responsive thead tr - th(scope="col") Vidno - th(scope="col") Podpodročje - th(scope="col") Prevod + th(scope="col")= t('Vidno') + th(scope="col")= t('Podpodročje') + th(scope="col")= t('Prevod') th(scope="col") tbody#page-results each result in results @@ -53,6 +51,7 @@ block body name="isApproved" disabled ) + //- TODO I18n td.tdata-area= result.nameSl td.tdata-translation= result.nameEn td.buttons-group @@ -66,21 +65,21 @@ block body data-bs-toggle="modal" type="button" ) - img(src="/images/red-trash-icon.svg" alt="") + img(src="/images/red-trash-icon.svg" alt=t('Izbriši')) #input-row.row .col-sm-3 .subject-name - label.input-name-txt PODPODROČJE + label.input-name-txt= ('PODPODROČJE') input#area-input.form-control(type="text") .col-sm-3 .subject-name - label.input-name-txt ANGLEŠKI PREVOD PODPODROČJA + label.input-name-txt= ('ANGLEŠKI PREVOD') input#translation-input.form-control(type="text") .col.d-flex.align-items-end.mt-2 - button#add-area.btn.btn-primary(type="submit" disabled) Dodaj + button#add-area.btn.btn-primary(type="submit" disabled)= ('Dodaj') + include /common/footer include /utilities/modal-alert include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/admin/comments.pug b/express/views/pages/admin/comments.pug index f4bc724..69b1bb1 100644 --- a/express/views/pages/admin/comments.pug +++ b/express/views/pages/admin/comments.pug @@ -12,10 +12,11 @@ block body - const d = { activeLvl1: 'comments', activeLvl2: '' } +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Urejanje', h1: 'Komentarji', description: 'Na tem mestu so zbrani vsi komentarji, povezani s terminološkim portalom.' } + //- TODO I18n + - const c = { sideMenu: true, h2: t('Urejanje'), h1: t('Komentarji'), description: t('Na tem mestu so zbrani vsi komentarji, povezani s terminološkim portalom.') } +mainHeader(c) - .comments-content.col-12.col-md-12.py-md-3.bd-content.content-hold-prerequisites + .comments-content.col-12.col-md-12.pt-md-3.bd-content.content-hold-prerequisites #offset-main.main-container include /utilities/comments-with-pager diff --git a/express/views/pages/admin/connections-list.pug b/express/views/pages/admin/connections-list.pug index 18d5d23..de47f07 100644 --- a/express/views/pages/admin/connections-list.pug +++ b/express/views/pages/admin/connections-list.pug @@ -13,7 +13,7 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Povezave', h1: 'Povezave s portali', description: 'Svoj terminološki portal lahko povežete še z drugimi terminološkimi portali in med iskalnimi prikazujete tudi njihove zadetke.', buttons: [{ type: 'link', content: 'Dodaj povezavo', url: '/admin/povezave/nova' }] } + - const c = { sideMenu: true, h2: t('Povezave'), h1: t('Povezave s portali'), description: t('Svoj terminološki portal lahko povežete še z drugimi terminološkimi portali in med iskalnimi prikazujete tudi njihove zadetke.'), buttons: [{ type: 'link', content: t('Dodaj povezavo'), url: '/admin/povezave/nova' }] } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-2 @@ -34,23 +34,23 @@ block body data-link-id=link.id ) img(src="/images/external-link.svg") - span.ms-1 Pošlji + span.ms-1= t('Pošlji') hr.mt-2.mb-3 .d-sm-flex.justify-content-between .d-sm-flex.align-items-center.mb-0 img(src="/images/x-circle-red.svg") - span.normal-gray.ms-1 ZAVRJEN + span.normal-gray.ms-1= t('ZAVRJEN') .d-flex.align-content-center.mb-0.me-3.mt-2.mt-sm-0 a.btn.p-0.image-link( type="link" href=`/admin/povezava/${link.id}/urejanje` ) img(src="/images/u_edit-alt.svg" alt="Uredi") - span.normal-gray.ms-1 Uredi + span.normal-gray.ms-1= t('Uredi') .ms-3 button.p-0.btn.delete-task(data-link-id=link.id) img(src="/images/red-trash-icon.svg" alt="") - span.normal-gray.ms-2 Odstrani + span.normal-gray.ms-2= t('Odstrani') else #synced-terms-div .container-fluid.task.task-completed.p-3.mb-4(id='code' + link) @@ -67,12 +67,12 @@ block body data-link-id=link.id ) img(src="/images/refresh-ccw.svg") - span.ms-1 Sinhroniziraj + span.ms-1= t('Sinhroniziraj') a.ms-2.btn.btn-secondary.align-items-center.d-flex( href=`/admin/povezave/seznam/${link.id}` ) img(src="/images/book-colorized.svg") - span.ms-1 Slovarji + span.ms-1= t('Slovarji') hr.mt-2.mb-3 .d-sm-flex.justify-content-between .d-sm-flex.align-items-center.mb-0.form-check.form-switch.portal-enable-switch( @@ -91,22 +91,22 @@ block body name="isEnabled" id='checkbox' + link.id ) - label.normal-gray.ms-2.mt-1(for='checkbox' + link.id) Omogočeno + label.normal-gray.ms-2.mt-1(for='checkbox' + link.id)= t('Omogočeno') .d-flex.align-content-center.mb-0.me-3.mt-2.mt-sm-0 a.btn.p-0.image-link( type="link" href=`/admin/povezava/${link.id}/urejanje` ) img(src="/images/u_edit-alt.svg" alt="Uredi") - span.normal-gray.ms-1 Uredi + span.normal-gray.ms-1= t('Uredi') .ms-3 button.p-0.btn.delete-task(data-link-id=link.id) img(src="/images/red-trash-icon.svg" alt="") - span.normal-gray.ms-2 Odstrani + span.normal-gray.ms-2= t('Odstrani') else .d-flex.justify-content-center.mt-5 - p Nimate povezav za urejanje. + p= t('Nimate povezav za urejanje.') + include /common/footer include /utilities/modal-alert include /utilities/modal-response +responseModal - include /common/footer diff --git a/express/views/pages/admin/dictionaries-list.pug b/express/views/pages/admin/dictionaries-list.pug index c38aa45..5f437b5 100644 --- a/express/views/pages/admin/dictionaries-list.pug +++ b/express/views/pages/admin/dictionaries-list.pug @@ -12,13 +12,11 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Slovarji', h1: 'Seznam slovarjev', description: 'Seznam vseh slovarjev, ki jih uredniki, sicer registrirani uporabniki, urejajo na tem portalu.', buttons: [{ type: 'link', content: 'Dodaj slovar', url: '/slovarji/nov' }] } + - const c = { sideMenu: true, h2: t('Slovarji'), h1: t('Seznam slovarjev'), description: t('Seznam vseh slovarjev, ki jih uredniki, sicer registrirani uporabniki, urejajo na tem portalu.'), buttons: [{ type: 'link', content: t('Dodaj slovar'), url: '/slovarji/nov' }] } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-3 - .d-flex.justify-content-between - .d-flex.flex-row.mb-4 - include /components/search-and-filter/inline-search + .d-flex.justify-content-end .d-flex include /utilities/pager +pager @@ -34,28 +32,34 @@ block body .table-responsive table.styled-table thead - tr + tr#thead th= 'ID' - th= 'NASLOV' - th= 'STATUS' - th= 'USTVARJEN' - th= 'SPREMENJEN' + th= t('NASLOV') + th= t('STATUS') + th= t('USTVARJEN') + th= t('SPREMENJEN') th= '' tbody#page-results each result in results tr - each el in result - if result.timeCreated===el || result.timeModified===el - - const date = new Date(el).toLocaleDateString('sl-SL', localeOptions) - td= date - else - td= el + td= result.id + td= result.name + if result.status === 'closed' + td= t('zaprt') + else if result.status ==='reviewed' + td= t('v predogledu') + else + td= t('odprt') + - const dateC = new Date(result.timeCreated).toLocaleDateString('sl-SL', localeOptions) + - const dateM = new Date(result.timeModified).toLocaleDateString('sl-SL', localeOptions) + td= dateC + td= dateM td a.image-link( type="link" href=`/admin/slovarji/${result.id}/podatki` ) - img(src="/images/u_edit-alt.svg" alt="Uredi") - span.normal-gray.ms-1 Uredi + img(src="/images/u_edit-alt.svg" alt=t('Uredi')) + span.normal-gray.ms-1= t('Uredi') - include /common/footer + include /common/footer diff --git a/express/views/pages/admin/dictionary-advanced.pug b/express/views/pages/admin/dictionary-advanced.pug index 40b0490..ece50b4 100644 --- a/express/views/pages/admin/dictionary-advanced.pug +++ b/express/views/pages/admin/dictionary-advanced.pug @@ -12,7 +12,8 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Napredno', description: 'Napredno urejanje omogoča spreminjanje večjega števila podatkov v terminološkem slovarju.' } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Napredno'), description: t('Napredno urejanje omogoča spreminjanje večjega števila podatkov v terminološkem slovarju.') } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-3 @@ -21,44 +22,50 @@ block body name="dictionaryId" value=dictionary.id ) - .row + .row.g-0 .col-md-6.white-border-background.p-4.d-flex.align-items-center - span.info-text-for-button.me-auto Indeksiraj ... - button#index-entries-btn.btn.btn-primary.ps-5.pe-5.advanced-blue-btn UREDI + span.info-text-for-button.me-auto= t('Indeksiranje slovarja') + button#index-entries-btn.btn.btn-primary.ps-5.pe-5.advanced-blue-btn= t('IDEKSIRAJ') .col-sm.align-items-center.ms-xxl-3.ms-md-3.d-flex.ps-1 - span.name-info-txt Navodilo k posamičnemu polju. + span.name-info-txt= t('Tukaj lahko ponovno ideksirate slovar.') .file-type.mt-4 - .row.mt-5 + .row.mt-5.g-0 .col-md-6.white-border-background.p-4.d-flex.align-items-center - span.info-text-for-button.me-auto Brisanje slovarskih sestavkov - button#adv-delete-all-entries.btn.btn-primary.ps-5.pe-5.advanced-blue-btn BRIŠI + span.info-text-for-button.me-auto= t('Brisanje slovarskih sestavkov') + button#adv-delete-all-entries.btn.btn-primary.ps-5.pe-5.advanced-blue-btn= t('BRIŠI') .col-sm.align-items-center.ms-xxl-3.ms-md-3.d-flex.ps-1 - span.name-info-txt Izbrišete lahko vse doslej obdelane slovarske sestavke in ohranite vse metapodatke o slovarju. Dejanja ni mogoče preklicati. - a.name-info-txt.ms-1(href="/pomoc") Več … + span.name-info-txt= t('Izbrišete lahko vse doslej obdelane slovarske sestavke in ohranite vse metapodatke o slovarju. Dejanja ni mogoče preklicati.') + a.name-info-txt.ms-1( + href="/pomoc#help-edit-dict" + target="_blank" + )= t('Več …') .file-type.mt-5 - .row.mt-5 + .row.mt-5.g-0 .col-md-6.white-border-background.p-4.d-flex.align-items-center - span.info-text-for-button.me-auto Brisanje slovarja - button#adv-delete-dictionary.btn.btn-primary.ps-5.pe-5.advanced-blue-btn BRIŠI + span.info-text-for-button.me-auto= t('Brisanje slovarja') + button#adv-delete-dictionary.btn.btn-primary.ps-5.pe-5.advanced-blue-btn= t('BRIŠI') .col-sm.align-items-center.ms-xxl-3.ms-md-3.d-flex.ps-1 - span.name-info-txt Izbrišete lahko celoten slovar z vsemi metapodatki. Dejanja ni mogoče preklicati. - a.name-info-txt.ms-2(href="/pomoc") Več … + span.name-info-txt= t('Izbrišete lahko celoten slovar z vsemi metapodatki. Dejanja ni mogoče preklicati.') + a.name-info-txt.ms-2( + href="/pomoc#help-edit-dict" + target="_blank" + )= t('Več …') .file-type.mt-5 - .row.mt-5 + .row.mt-5.g-0 .col-md-6.white-border-background.p-4.d-flex.align-items-center - span.info-text-for-button.me-auto Objava vseh slovarskih sestavkov - button#adv-publish-all-entries.btn.btn-primary.ps-5.pe-5.advanced-blue-btn OBJAVI + span.info-text-for-button.me-auto= t('Objava vseh slovarskih sestavkov') + button#adv-publish-all-entries.btn.btn-primary.ps-5.pe-5.advanced-blue-btn= t('OBJAVI') .col-sm.align-items-center.ms-xxl-3.ms-md-3.d-flex.ps-1 - span.name-info-txt Po koncu urejanja vseh slovarskih sestavkov lahko slovar objavite in ga tako prikažete na javnem delu terminološkega portala. + span.name-info-txt= t('Po koncu urejanja vseh slovarskih sestavkov lahko slovar objavite in ga tako prikažete na javnem delu terminološkega portala.') + include /common/footer include /utilities/modal-spinner include /utilities/modal-alert-mixin - +alertModal("index-entries", "Uporabi", "Prekliči", "modal-use-btn", "cancel-btn", 'Ali želite indeksirati?') - +alertModal("delete-entries", "Izbriši", "Prekliči", "modal-use-btn", "cancel-btn", 'S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju. Metapodatki bodo ostali. Dejanja ni mogoče razveljaviti.') - +alertModal("delete-dictionary", "Izbriši", "Prekliči", "modal-use-btn", "cancel-btn", 'S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju in vse metapodatke. Dejanja ni mogoče razveljaviti.') - +alertModal("publish-entries", "Objavi", "Prekliči", "modal-use-btn", "cancel-btn", 'Ali želite objaviti vsa gesla?') + +alertModal("index-entries", t("Uporabi"), t("Prekliči"), "modal-use-btn", "cancel-btn", t('Ali želite indeksirati?')) + +alertModal("delete-entries", t("Izbriši"), t("Prekliči"), "modal-use-btn", "cancel-btn", t('S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju. Metapodatki bodo ostali. Dejanja ni mogoče razveljaviti.')) + +alertModal("delete-dictionary", t("Izbriši"), t("Prekliči"), "modal-use-btn", "cancel-btn", t('S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju in vse metapodatke. Dejanja ni mogoče razveljaviti.')) + +alertModal("publish-entries", t("Objavi"), t("Prekliči"), "modal-use-btn", "cancel-btn", t('Ali želite objaviti vsa gesla?')) include /utilities/modal-response - +responseModal("delete-entries-res", "Razumem", "understand-btn", "Slovarski sestavki so bili izbrisani.") - +responseModal("delete-dict-res", "Razumem", "understand-btn", "Slovar je bil izbrisan.") - +responseModal("publish-entries-res", "Razumem", "understand-btn", "Slovarski sestavki so bili objavljeni.") - include /common/footer + +responseModal("delete-entries-res", t("Razumem"), "understand-btn", t("Slovarski sestavki so bili izbrisani.")) + +responseModal("delete-dict-res", t("Razumem"), "understand-btn", t("Slovar je bil izbrisan.")) + +responseModal("publish-entries-res", t("Razumem"), "understand-btn", t("Slovarski sestavki so bili objavljeni.")) diff --git a/express/views/pages/admin/dictionary-comments.pug b/express/views/pages/admin/dictionary-comments.pug index f78227d..25787d3 100644 --- a/express/views/pages/admin/dictionary-comments.pug +++ b/express/views/pages/admin/dictionary-comments.pug @@ -13,11 +13,12 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Komentarji', description: 'Na tem mestu so zbrani vsi komentarji, povezani z izbranim terminološkim slovarjem.' } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Komentarji'), description: t('Na tem mestu so zbrani vsi komentarji, povezani z izbranim terminološkim slovarjem.') } +mainHeader(c) - .comments-content.col-12.col-md-12.py-md-3.bd-content.content-hold-prerequisites + .comments-content.col-12.col-md-12.pt-md-3.bd-content.content-hold-prerequisites #offset-main.main-container include /utilities/comments-with-pager - include /common/footer + include /common/footer diff --git a/express/views/pages/admin/dictionary-description.pug b/express/views/pages/admin/dictionary-description.pug index 39c4a43..924615f 100644 --- a/express/views/pages/admin/dictionary-description.pug +++ b/express/views/pages/admin/dictionary-description.pug @@ -12,235 +12,11 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionary.nameSl, h1: 'Osnovni podatki', description: 'Izpolnite polja in na kratko opišite vsebino terminološkega slovarja.', helpLink: { linkHref: '/pomoc', linkText: 'Več ...' }, buttons: [{ type: 'disabled', content: 'Shrani', form: 'admin-description' }] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionary.nameSl, h1: t('Osnovni podatki'), description: t('Izpolnite polja in na kratko opišite vsebino terminološkega slovarja.'), helpLink: { linkHref: '/pomoc#help-edit-dict', linkText: t('Več ...') }, buttons: [{ type: 'disabled', content: t('Shrani'), form: 'admin-description' }] } +mainHeader(c) - .content-hold-prerequisites - #offset-main.main-container.mt-1 - .title - .subject-name - label.smaller-gray-uppercase(for="") ID Slovarja - .row - .col-sm-6 - span.page-title-header= dictionary.id - - form#admin-description.needs-validation.mt-3(method="post" novalidate) - .title - .subject-name - label.smaller-gray-uppercase(for="dictionary-title") NASLOV SLOVARJA * - .row - .col-sm-6 - input#dictionary-title.name-input.form-control( - type="text" - name="nameSl" - required - maxlength="120" - value=dictionary.nameSl - ) - .invalid-feedback Niste vpisali naslova slovarja. - - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Celotni naslov slovarja, ki bo zabeležen v bibliografskih podatkih. - - .english-title.mt-4 - .subject-name - label.smaller-gray-uppercase(for="dictionary-title-en") ANGLEŠKI NASLOV SLOVARJA * - .row - .col-sm-6 - input#dictionary-title-en.name-input.form-control( - type="text" - name="nameEn" - maxlength="120" - value=dictionary.nameEn ? dictionary.nameEn : '' - required - ) - .invalid-feedback Niste vpisali angleškega naslova slovarja. - - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Celotni naslov slovarja v angleščini. - - .short-title.mt-4 - .subject-name - label.smaller-gray-uppercase(for="short-dictionary-title") SKRAJŠANI NASLOV SLOVARJA - .row - .col-sm-6 - input#short-dictionary-title.name-input.d-inline.form-control( - type="text" - name="nameSlShort" - maxlength="15" - value=dictionary.nameSlShort ? dictionary.nameSlShort : '' - ) - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesede nje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki. - //- TODO enable field when slug is in DB - //- .mt-4 - //- .subject-name - //- label.smaller-gray-uppercase(for="slug") Slug - //- .row - //- .col-sm-6 - //- input#slug.name-input.d-inline.form-control( - //- type="text" - //- placeholder="V razvoju ..." - //- disabled - //- ) - //- .col-sm.d-flex.align-items-center - //- span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Slug - - if dictionary.author - each ele, index in dictionary.author - .author.mt-4.added-field - .subject-name - label.smaller-gray-uppercase(for="author") AVTOR SLOVARJA - .row - .col-sm-6 - .input-group - input( - class=index != 0 ? 'name-input d-inline form-control icon-trash' : 'name-input d-inline form-control' - type="text" - name="author" - maxlength="64" - value=ele ? ele : '' - ) - if (index != 0) - button.input-group-text.delete-author-btn(type="button") - img.delete-author.p-0( - src="/images/red-trash-icon.svg" - alt="Delete" - ) - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime. - else - #first-author.author.mt-4 - .subject-name - label.smaller-gray-uppercase(for="author") AVTOR SLOVARJA - .row - .col-sm-6 - input#author.name-input.d-inline.form-control( - type="text" - name="author" - maxlength="64" - ) - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime. - - #add-new-author.author.mt-4 - .subject-name - label.smaller-gray-uppercase(for="input-new-author") NOV AVTOR - .row - .col-sm-6 - button#input-new-author.form-control(type="button") - span.new-author-text-btn Nov avtor - - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Dodajte ime in priimek naslednjega avtorja slovarja.. - //- TODO enable field when weight is in DB - //- .mt-4 - //- .subject-name - //- label.smaller-gray-uppercase(for="weight") TEŽA - //- .row - //- .col-sm-6.d-flex.align-items-center - //- input#weight.name-input.form-control.autocomplete.d-inline( - //- disabled - //- placeholder="V razvoju ..." - //- ) - - //- .col-sm.d-flex.align-items-center - //- span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Statična teža. - - .mt-4 - .subject-name - label.smaller-gray-uppercase(for="status") STATUS - .row - .col-sm-6.d-flex.align-items-center - select#status.name-input.form-select.d-inline(name="status") - option( - value="closed" - selected=status === 'closed' ? true : false - ) Zaprt - option( - value="reviewed" - selected=status === 'reviewed' ? true : false - disabled=status === 'reviewed' ? false : true - ) V odpiranju - option( - value="published" - selected=status === 'published' ? true : false - ) Odprt - - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Terminološkemu slovarju določite status. Izbirate lahko med zaprt, v urejanju in odprt. - - .cerif-area.mt-4 - .subject-name - label.smaller-gray-uppercase(for="select-cerif") PODROČJE * - .row - .col-sm-6 - select.name-input.d-inline.form-select( - name="domainPrimary" - required - ) - each domain in allPrimaryDomains - option( - value=domain.id - selected=domain.id === dictionary.domainPrimary - )= domain.nameSl - #invalid-section.invalid-feedback-selection.hidden-section.mt-3 Nimate izbranega področja CERIF. - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Izberite področje svojega terminološkega slovarja na seznamu področij. - - .small-name-area.mt-4 - .subject-name - label.smaller-gray-uppercase(for="domain-secondary") PODPODROČJE - .row - .col-sm-6 - select#domain-secondary.name-input.d-inline.form-control.without-addition( - name="domainSecondary" - multiple - ) - each domain in allSecondaryDomains - if associatedSecondaryDomains.some(associatedDomain => associatedDomain.id === domain.id) - option(value=domain.id selected)= domain.nameSl - else - option(value=domain.id)= domain.nameSl - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Podpodročje. - - #add-new-area.author.mt-4 - .subject-name - label.smaller-gray-uppercase NOVO PODPODROČJE - .row - .col-sm-6 - button#input-new-area.form-control(type="button") - span.new-author-text-btn Novo podpodročje - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Vpišite novo podpodročje. Na seznamu podpodročij bo vidno takoj po potrditvi administratorja portala. - - #text-editor.mt-4 - .subject-name - span.smaller-gray-uppercase OPIS SLOVARJA - .container-xxl.ps-0.ms-0 - textarea.summernote( - name="description" - value=dictionary.description - ) - if dictionary.description - p= dictionary.description - .col-sm-6.smaller-gray-info.mt-2 - | - #issn-field.mt-4 - .subject-name - label.smaller-gray-uppercase(for="issn") ISSN - .row - .col-sm-6 - input#issn.name-input.d-inline.form-control( - type="text" - name="issn" - maxlength="20" - value=dictionary.issn ? dictionary.issn : '' - ) - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 ISSN oznaka. - a.ms-1.smaller-gray-info(href="/pomoc" target="_blank") Več ... + include /utilities/dictionary-description-mixin + +dictionary-description(dictionary.id) include /utilities/modal-alert include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/admin/dictionary-domain-labels.pug b/express/views/pages/admin/dictionary-domain-labels.pug index 1063e06..a4fc68b 100644 --- a/express/views/pages/admin/dictionary-domain-labels.pug +++ b/express/views/pages/admin/dictionary-domain-labels.pug @@ -12,99 +12,8 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Področne oznake', description: 'Na tem mestu lahko določite področne oznake, če želite posamezne termine v svojem terminološkem slovarju razvrstiti še podrobneje.', helpLink: { linkHref: '/pomoc#help-content-domains', linkText: 'Več ...' }, buttons: [{ form: 'dictionary-domain-labels', type: 'disabled', content: 'Shrani' }] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Področne oznake'), description: t('Na tem mestu lahko določite področne oznake, če želite posamezne termine v svojem terminološkem slovarju razvrstiti še podrobneje.'), helpLink: { linkHref: '/pomoc#help-content-domains', linkText: t('Več ...') }, buttons: [{ form: 'dictionary-domain-labels', type: 'disabled', content: t('Shrani') }] } +mainHeader(c) - - .content-hold-prerequisites - #offset-main.main-container.mt-2 - if results.length - .d-flex.justify-content-between.mt-2 - .d-flex.flex-row.mb-4 - include /components/search-and-filter/inline-search - .d-flex - include /utilities/pager - +pager - - form#dictionary-domain-labels( - method="post" - action="/api/v1/dictionaries/update-domain-labels" - ) - input#subareas-dict-id( - type="hidden" - name="dictionaryId" - value=dictionary.id - ) - table#all-areas-table.table.areas-table.table-responsive - thead - tr - th.visible-th(scope="col") Vidno - th(scope="col") Področna oznaka - th(scope="col") - tbody#page-results - each result in results - tr - input(type="hidden" name="domainLabelId" value=result.id) - th(scope="row") - if result.isVisible - input.form-check.checkbox-table( - type="checkbox" - name="isVisible" - checked - disabled - ) - else - input.form-check.checkbox-table( - type="checkbox" - name="isVisible" - disabled - ) - td.tdata-area= result.name - td.buttons-group - .table-buttons - button.p-0.table-button-grp.me-3.edit-row-btn( - type="button" - ) - img(src="/images/u_edit-alt.svg" alt="") - button.p-0.table-button-grp.delete-row-btn( - type="button" - data-bs-target="#alert-modal" - data-bs-toggle="modal" - ) - img(src="/images/red-trash-icon.svg" alt="") - else - form#dictionary-domain-labels( - method="post" - action="/api/v1/dictionaries/update-domain-labels" - ) - input#subareas-dict-id( - type="hidden" - name="dictionaryId" - value=dictionary.id - ) - #subareas-info.d-flex.justify-content-center.mt-5 - p Niste vnesli področnih oznak. - #no-subareas-section.d-none - .d-flex.justify-content-between.mt-4 - .d-flex.flex-row.mb-4 - include /components/search-and-filter/inline-search - .d-flex - include /utilities/pager - +pager - - table#all-areas-table.table.areas-table.table-responsive - thead - tr - th.visible-th(scope="col") Vidno - th(scope="col") Področna oznaka - th(scope="col") - tbody - tr.hidden(hidden) - .row - .col-sm-3 - .subject-name - label.input-name-txt PODROČNA OZNAKA - input#subarea-input.form-control(type="text") - .col.d-flex.align-items-end.mt-2 - button#add-area.btn.btn-primary(type="submit" disabled) Dodaj - include /utilities/modal-alert - include /common/footer + include /utilities/dictionary-domain-labels-mixin + +dictionary-domain-labels diff --git a/express/views/pages/admin/dictionary-export.pug b/express/views/pages/admin/dictionary-export.pug index a896ffd..d281b46 100644 --- a/express/views/pages/admin/dictionary-export.pug +++ b/express/views/pages/admin/dictionary-export.pug @@ -1,8 +1,7 @@ extends /layout block pageSpecificScipts - script(nonce=cspNonce src="/javascripts/admin.js") -block include-styles - link(rel="stylesheet" href="/stylesheets/pages/admin.css") + script(nonce=cspNonce src="/javascripts/dictionaries.js") + script(nonce=cspNonce src="/javascripts/dictionary-export.js") block body section#fixed-top-section @@ -13,241 +12,9 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Izvoz', description: 'Terminološke slovarje lahko v celoti ali po izbranih kriterijih izvozite v različnih formatih in shranite na svojem računalniku.', buttons: [] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Izvoz'), description: t('Terminološke slovarje lahko v celoti ali po izbranih kriterijih izvozite v različnih formatih in shranite na svojem računalniku.'), buttons: [] } +mainHeader(c) - .content-hold-prerequisites - #offset-main.main-container.mt-2 - .row.validity.align-items-center - .col-xxl-6.col-md-7 - span.header-table-wrapper Veljavnost - ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 - .options-width-box - li.form-check.radio-button.ms-0.ps-3 - input#allKeys.form-check-input( - type="radio" - name="flexRadioDefault" - checked - ) - label.radio-button-labels.form-check-label.ms-0.text-nowrap( - for="allKeys" - ) - | Vsi slovarski sestavki - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#validKeys.form-check-input( - type="radio" - name="flexRadioDefault" - ) - label.radio-button-labels.form-check-label.ms-0( - for="validKeys" - ) - | Veljavni - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#notValidKeys.form-check-input( - type="radio" - name="flexRadioDefault" - ) - label.radio-button-labels.form-check-label.ms-0( - for="notValidKeys" - ) - | Neveljavni - .col.mb-3.mb-md-0 - span.name-info-txt Izberite, katere slovarske sestavke želite izpisati glede na njihovo veljavnost. - .row.posted.align-items-center - .col-xxl-6.col-md-7 - span.header-table-wrapper Objavljeno - ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 - .options-width-box - li.form-check.radio-button.ms-0.ps-3 - input#allPostedKeys.form-check-input( - type="radio" - name="posted" - checked - ) - label.radio-button-labels.form-check-label.ms-0.text-nowrap( - for="allPostedKeys" - ) - | Vsi slovarski sestavki - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#onlyPosted.form-check-input(type="radio" name="posted") - label.radio-button-labels.form-check-label.ms-0( - for="onlyPosted" - ) - | Objavljeni - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#notPosted.form-check-input(type="radio" name="posted") - label.radio-button-labels.form-check-label.ms-0( - for="notPosted" - ) - | Neobjavljeni - .col.mb-3.mb-md-0 - span.name-info-txt Izberite, ali želite izpisati samo objavljene ali tudi neobjavljene slovarske sestavke. - .row.phases.align-items-center - .col-xxl-6.col-md-7 - span.header-table-wrapper Faze urejanja - ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 - .options-width-box - li.form-check.radio-button.ms-0.ps-3 - input#allEditedKeys.form-check-input( - type="radio" - name="edited" - checked - ) - label.radio-button-labels.form-check-label.ms-0.text-nowrap( - for="allEditedKeys" - ) - | Vsi slovarski sestavki - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#editedKeys.form-check-input(type="radio" name="edited") - label.radio-button-labels.form-check-label.ms-0( - for="editedKeys" - ) - | Urejeni - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#inEditing.form-check-input(type="radio" name="edited") - label.radio-button-labels.form-check-label.ms-0( - for="inEditing" - ) - | V urejanju - .col.mb-3.mb-md-0 - span.name-info-txt Izberite, ali želite izpisati vse slovarske sestavke ali samo tiste, ki so v določeni fazi urejanja. - .row.proffesional-check.align-items-center - .col-xxl-6.col-md-7 - span.header-table-wrapper Strokovni pregled - ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 - .options-width-box - li.form-check.radio-button.ms-0.ps-3 - input#allChecked.form-check-input( - type="radio" - name="professionallyChecked" - checked - ) - label.radio-button-labels.form-check-label.ms-0( - for="allChecked" - ) - | Vsi slovarski sestavki - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#onlyProffesionallyChecked.form-check-input( - type="radio" - name="professionallyChecked" - ) - label.radio-button-labels.form-check-label.ms-0( - for="onlyProffesionallyChecked" - ) - | Pregledani - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#notProffesionallyChecked.form-check-input( - type="radio" - name="professionallyChecked" - ) - label.radio-button-labels.form-check-label.ms-0( - for="notProffesionallyChecked" - ) - | Nepregledani - .col.mb-3.mb-md-0 - span.name-info-txt Izberite, ali želite izpisati samo strokovno pregledane slovarske sestavke. - .row.terminology-check.align-items-center - .col-xxl-6.col-md-7 - span.header-table-wrapper Jezikovni pregled - ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 - .options-width-box - li.form-check.radio-button.ms-0.ps-3 - input#allGramaticallyChecked.form-check-input( - type="radio" - name="gramaticallyChecked" - checked - ) - label.radio-button-labels.form-check-label.ms-0( - for="allGramaticallyChecked" - ) - | Vsi slovarski sestavki - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#onlyGramaticallyChecked.form-check-input( - type="radio" - name="gramaticallyChecked" - ) - label.radio-button-labels.form-check-label.ms-0( - for="onlyGramaticallyChecked" - ) - | Pregledani - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#notGramaticallyChecked.form-check-input( - type="radio" - name="gramaticallyChecked" - ) - label.radio-button-labels.form-check-label.ms-0( - for="notGramaticallyChecked" - ) - | Nepregledani - .col.mb-3.mb-md-0 - span.name-info-txt Izberite, ali želite izpisati samo jezikovno pregledane slovarske sestavke. - .file-type - span.new-user-info FORMAT ZAPISA - .row.align-items-center - .col-xxl-6.col-md-7 - .form-check.radio-button.ms-2.ps-3 - input#file-format-xml.form-check-input( - type="radio" - name="importFileFormat" - value="xml" - checked - ) - label.form-check-label.ms-0(for="file-format-xml") - | XML - .form-check.radio-button.ms-3 - input#file-format-csv.form-check-input( - type="radio" - name="importFileFormat" - value="csv" - ) - label.form-check-label.ms-0(for="file-format-csv") - | CSV - .form-check.radio-button.ms-3 - input#file-format-tsv.form-check-input( - type="radio" - name="importFileFormat" - value="tsv" - ) - label.form-check-label.ms-0(for="file-format-tsv") - | TSV - .form-check.radio-button.ms-3 - input#file-format-txt.form-check-input( - type="radio" - name="importFileFormat" - value="txt" - ) - label.form-check-label.ms-0(for="file-format-txt") - | TXT - .col.mb-3.mb-md-0 - span.name-info-txt Izberite format izpisa izbranega terminološkega slovarja. - button.btn.btn-primary.mt-4 IZVOZI - .latest-exports.mt-4 - .d-flex.w-100.justify-content-between - .d-flex - span.info-text-for-button Zadnji izvozi - .d-flex - include /utilities/pager - +pager(1,2,3,1,3,true) - .d-block.w-100 - include /utilities/table-mixin - - - const headerRow = ['Datum', 'Vrsta', 'Št. gesel', 'Status'] - const dataRows = [ - ['12.12.2012', 'Oznaka', 'Št. gesel', 'Oznaka', ""], - ['12.12.2012', 'Oznaka', 'Št. gesel', 'Oznaka', {button: {content:"Shrani"}}], - ['12.12.2012', 'Oznaka', 'Št. gesel', 'Oznaka', {button: {content:"Shrani"}}], - ['12.12.2012', 'Oznaka', 'Št. gesel', 'Oznaka', {button: {content:"Shrani"}}], - ['12.12.2012', 'Oznaka', 'Št. gesel', 'Oznaka', {button: {content:"Shrani"}}] - ] - +tableHeader(headerRow, dataRows) - - include /common/footer + include /utilities/dictionary-export-content-mixin + +dictionary-export-content(dictionary.id) + include /common/footer diff --git a/express/views/pages/admin/dictionary-extraction-import.pug b/express/views/pages/admin/dictionary-extraction-import.pug index 3b13065..f12faf7 100644 --- a/express/views/pages/admin/dictionary-extraction-import.pug +++ b/express/views/pages/admin/dictionary-extraction-import.pug @@ -11,56 +11,9 @@ block body include /common/side-menu-mixin-admin - const d = { activeLvl1: 'dictionaries', activeLvl2: 'extractionImport' } +sideNavigation(d) - + //- TODO I18n include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Uvoz iz luščilnika', description: 'Rezultat luščenja so terminološki kandidati. Z uvozom rezultatov posameznega luščenja lahko dopolnite geslovnik terminološkega slovarja, ki ga urejate.', buttons: [] } + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Uvoz iz luščilnika'), description: t('Rezultat luščenja so terminološki kandidati. Z uvozom rezultatov posameznega luščenja lahko dopolnite geslovnik terminološkega slovarja, ki ga urejate.'), buttons: [] } +mainHeader(c) - .content-hold-prerequisites - #offset-main.main-container.mt-3 - .small-name-area - .subject-name - span.input-name-txt IME LUŠČENJA - .row - .col-lg-6 - select#select-extraction-name.name-input.d-inline.form-control - option(selected value="" disabled hidden)= 'Izberite luščenje' - each el in extractions - option(value=el.id)= el.name - .col-sm.d-flex.align-items-center - span.name-info-txt.ms-lg-3.mt-2.mt-lg-0 Izberite enega od rezultatov luščenja s seznama. - .list-terminology-candidates.mt-4.me-2 - .d-flex.w-100.justify-content-between - .d-flex.align-items-center - span.info-text-for-button Seznam terminoloških kandidatov - .d-flex - include /utilities/pager - +pager - .table-responsive.mt-2.me-2 - table.styled-table - thead - tr - th= '#' - th= 'KANONIČNA OBLIKA' - th= 'RANKING' - th= 'POGOSTOST OBJAVLJANJA' - tbody#page-results - .row.mt-4 - .me-0.pe-0.d-flex.align-items-center - span.radio-button-labels Uvozi termine od številke - input.without-arrows.form-control.terminology-input.ms-1( - type="number" - maxlength="5" - name="from" - min="0" - ) - span.radio-button-labels.ms-1 do številke - input.without-arrows.form-control.terminology-input.ms-1( - type="number" - maxlength="5" - name="to" - min="0" - ) - - button.btn.btn-primary.mt-4(disabled) UVOZI - - include /common/footer + include /utilities/dictionary-extraction-import-mixin + +dictionary-extraction-import diff --git a/express/views/pages/admin/dictionary-import.pug b/express/views/pages/admin/dictionary-import.pug index ad94718..c160aa8 100644 --- a/express/views/pages/admin/dictionary-import.pug +++ b/express/views/pages/admin/dictionary-import.pug @@ -1,8 +1,6 @@ extends /layout block pageSpecificScipts script(nonce=cspNonce src="/javascripts/dictionaries.js") -block include-styles - link(rel="stylesheet" href="/stylesheets/pages/admin.css") block body section#fixed-top-section @@ -13,144 +11,8 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Uvoz iz datoteke', description: 'Slovar lahko uvozite s svojega računalnika in nadaljujete z urejanjem na terminološkem portalu.', buttons: [] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Uvoz iz datoteke'), description: t('Če imate pripravljen slovar v enem od formatov, ki so navedeni spodaj, lahko svoje podatke uvozite.'), buttons: [] } +mainHeader(c) - .content-hold-prerequisites - #offset-main.main-container - form#file-import-form.container.mt-3.ms-2.ps-0( - method="post" - enctype="multipart/form-data" - ) - .file-type - span.new-user-info DATOTEKA - .row - .col-md-6.ms-2.white-border-background.p-4.d-flex.align-items-center.justify-content-between - .align-items-center.d-flex - span#chosen-file.info-text-for-button Izberi datoteko - div - label(for="upload") - button#button-import.btn.btn-primary(type="button") IZBERI - input#upload( - type="file" - name="dictionaryImportFile" - accept=".xml" - ) - - .col-md.d-flex.align-items-center - span.name-info-txt Izberite slovarske podatke, ki ste jih shranili na svojem računalniku. - .file-type.mt-4 - span.new-user-info NAČIN UVOZA - .row - .col-sm-6.ms-2 - .checkbox - input#flexCheckDefault.form-check-input( - type="checkbox" - name="deleteExistingEntries" - ) - label.form-check-label(for="flexCheckDefault") - span.user-email Izbriši obstoječe slovarske sestavke. - .col - span.name-info-txt Če izberete to možnost, se bodo vsi doslejšnji slovarski sestavki ob uvozu nove datoteke izbrisali. - .file-type.mt-4 - span.new-user-info FAZA UREJANJA - #file-type-selection.file-types - .row - .col-sm-6.ms-2 - .form-check.radio-button.ms-0.ps-3 - input#in-edit-radio.form-check-input( - type="radio" - name="entryStatus" - value="inEdit" - checked - ) - label.form-check-label.ms-0(for="in-edit-radio") - | V urejanju - #complete-radio.form-check.radio-button.ms-3 - input#complete-radio.form-check-input( - type="radio" - name="entryStatus" - value="complete" - ) - label.form-check-label.ms-0(for="complete-radio") - | Urejeno - .col - span.name-info-txt Z izbiro te možnosti boste pobrisali samo slovarske sestavke, ki so v določeni fazi urejanja. - .file-type.mt-4 - span.new-user-info FORMAT ZAPISA - #file-type-selection.file-types - .row - .col-sm-6.ms-2.text-nowrap - .form-check.radio-button.ms-0.ps-3 - input#file-format-xml.form-check-input( - type="radio" - name="importFileFormat" - value="xml" - checked - ) - label.form-check-label.ms-0(for="file-format-xml") - | XML - .form-check.radio-button.ms-3 - input#file-format-csv.form-check-input( - type="radio" - name="importFileFormat" - value="csv" - ) - label.form-check-label.ms-0(for="file-format-csv") - | CSV - .form-check.radio-button.ms-3 - input#file-format-tsv.form-check-input( - type="radio" - name="importFileFormat" - value="tsv" - ) - label.form-check-label.ms-0(for="file-format-tsv") - | TSV - .form-check.radio-button.ms-3 - input#file-format-txt.form-check-input( - type="radio" - name="importFileFormat" - value="txt" - ) - label.form-check-label.ms-0(for="file-format-txt") - | TXT - .col - span.name-info-txt Izberite format datoteke, v kateri je slovar shranjen na vašem računalniku. - .file-type.mt-4 - button.btn.btn-primary.mt-4 UVOZI - .latest-uploads.mt-4 - .d-flex.w-100.justify-content-between - .d-flex - span.info-text-for-button Zadnji uvozi - .d-flex - include /utilities/pager - +pager - - if imports.length - .table-responsive - table.styled-table - thead - tr - th= 'DATUM' - th= 'VRSTA' - th= 'ŠT. GESEL' - th= 'STATUS' - th= '' - - - const localeOptions = { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - } - each element in imports - tr - td= element.timeStarted.toLocaleDateString('sl-SL', localeOptions) - td= element.fileFormat - td= element.countValidEntries - td= element.status - else - p Ni še dodanih uvozov. - - include /common/footer + include /utilities/dictionary-import-mixin + +dictionary-import(dictionary.id) diff --git a/express/views/pages/admin/dictionary-structure.pug b/express/views/pages/admin/dictionary-structure.pug index 7a49b4a..2956c17 100644 --- a/express/views/pages/admin/dictionary-structure.pug +++ b/express/views/pages/admin/dictionary-structure.pug @@ -1,7 +1,7 @@ extends /layout block pageSpecificScipts - script(nonce=cspNonce src="/javascripts/admin.js") + script(nonce=cspNonce src="/javascripts/dictionaries.js") block body section#fixed-top-section @@ -12,12 +12,13 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionary.nameSl, h1: 'Struktura slovarskega sestavka', description: 'V tem razdelku lahko določite elemente slovarskega sestavka. Z izbiro elementov se vam prikazuje podoba slovarskega sestavka. Izbiro lahko tudi med urejanjem vsebine kadarkoli spremenite.', helpLink: { linkHref: '/pomoc', linkText: 'Več ...' }, buttons: [{ type: 'disabled', content: 'Shrani', form: 'form-dictionary-structure' }] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionary.nameSl, h1: t('Struktura slovarskega sestavka'), description: t('V tem razdelku lahko določite elemente slovarskega sestavka. Z izbiro elementov se vam prikazuje podoba slovarskega sestavka. Izbiro lahko tudi med urejanjem vsebine kadarkoli spremenite.'), helpLink: { linkHref: '/pomoc#help-structure-dict', linkText: t('Več ...') }, buttons: [{ type: 'disabled', content: t('Shrani'), form: 'form-dictionary-structure' }] } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container form#form-dictionary-structure(method="post") include /utilities/dictionary-structure-input + include /common/footer include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/admin/dictionary-users.pug b/express/views/pages/admin/dictionary-users.pug index da206fd..1e257b7 100644 --- a/express/views/pages/admin/dictionary-users.pug +++ b/express/views/pages/admin/dictionary-users.pug @@ -12,12 +12,12 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionary.nameSl, h1: 'Uporabniki', description: 'V tem razdelku lahko določite fazo urejanja slovarja, dodajate uporabnike in jim določate uporabniške pravice pri urejanju slovarja.', buttons: [{ type: 'disabled', content: 'Shrani', form: 'admin-dictionary-users' }] } + - const c = { sideMenu: true, h2: dictionary.nameSl, h1: t('Uporabniki'), description: t('V tem razdelku lahko določite uporabniške vloge posameznega uporabnika in urejate njegove podatke.'), buttons: [{ type: 'disabled', content: t('Shrani'), form: 'admin-dictionary-users' }] } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-1 form#admin-dictionary-users.container-xxl.ms-0.ps-0(method="post") - span.users-subtitle-txt.ms-0 Faze urejanja + span.users-subtitle-txt.ms-0= t('Faze urejanja') .row.align-items-center.mt-2 .col-sm-6 .switch-forms-and-key-word.d-md-flex.mt-2 @@ -28,10 +28,10 @@ block body disabled checked ) - label.form-check-label(for="preposition") Predlog + label.form-check-label(for="preposition")= t('Predlog') .col-sm - span.name-info-txt V slovarju so samo terminološki kandidati. Slovarja ne morete objaviti. + span.name-info-txt= t('V slovarju so samo terminološki kandidati. Slovarja ne morete objaviti.') .row.align-items-center .col-sm-6 @@ -43,11 +43,11 @@ block body disabled checked ) - label.form-check-label(for="editing") V urejanju + label.form-check-label(for="editing")= t('V urejanju') .col-sm - span.name-info-txt Slovarski sestavek je v fazi, ko se dodajajo in oblikujejo elementi, ki ste jih izbrali v strukturi slovarskega sestavka. - a.ms-1.name-info-txt(href="/pomoc#help-users-dict" target="_blank") Več... + span.name-info-txt= t('Slovarski sestavek je v fazi, ko se dodajajo in oblikujejo elementi, ki ste jih izbrali v strukturi slovarskega sestavka.') + a.ms-1.name-info-txt(href="/pomoc#help-users-dict" target="_blank")= t('Več...') .row.align-items-center .col-sm-6 @@ -59,9 +59,9 @@ block body disabled checked ) - label.form-check-label(for="edited") Urejeno + label.form-check-label(for="edited")= t('Urejeno') .col-sm - span.name-info-txt Slovarski sestavek je pregledan in dokončan. + span.name-info-txt= t('Slovarski sestavek je pregledan in dokončan.') .row.align-items-center.mt-2 .col-sm-6 @@ -74,16 +74,16 @@ block body name="hasTerminologyReview" checked ) - label.form-check-label(for="terminology_review") Strokovno pregledano + label.form-check-label(for="terminology_review")= t('Strokovno pregledano') else .form-check.form-switch.d-flex input#terminology_review.form-check-input( type="checkbox" name="hasTerminologyReview" ) - label.form-check-label(for="terminology_review") Strokovno pregledano + label.form-check-label(for="terminology_review")= t('Strokovno pregledano') .col-sm - span.name-info-txt Slovar je pregledal področni strokovnjak. + span.name-info-txt= t('Slovar je pregledal področni strokovnjak.') .row.align-items-center.mt-2 .col-sm-6 @@ -96,35 +96,35 @@ block body name="hasLanguageReview" checked ) - label.form-check-label(for="language_review") Jezikovno pregledano + label.form-check-label(for="language_review")= t('Jezikovno pregledano') else .form-check.form-switch.d-flex input#language_review.form-check-input( type="checkbox" name="hasLanguageReview" ) - label.form-check-label(for="language_review") Jezikovno pregledano + label.form-check-label(for="language_review")= t('Jezikovno pregledano') .col-sm - span.name-info-txt Slovar je pregledal jezikoslovec. + span.name-info-txt= t('Slovar je pregledal jezikoslovec.') .container-xl.mt-4.ms-0.ps-0 - span.users-subtitle-txt.ms-0.mt-3 Uporabniške pravice/vloge + span.users-subtitle-txt.ms-0.mt-3= t('Uporabniške pravice/vloge') table#user-roles-table.table-users.table-borderless.align-middle.mt-3 thead tr th.col-2 - span.user-rights-column-title Uporabnik + span.user-rights-column-title= t('Uporabnik') th.col-2 - span.user-rights-column-title Administracija + span.user-rights-column-title= t('Administracija') th.col-2.text-center - span.user-rights-column-title Urejanje + span.user-rights-column-title= t('Urejanje') th.col-2 - span#txt-term-rev.user-rights-column-title Strokovni pregled + span#txt-term-rev.user-rights-column-title= t('Strokovni pregled') th.col-2 - span#txt-lang-rev.user-rights-column-title Jezikovni pregled + span#txt-lang-rev.user-rights-column-title= t('Jezikovni pregled') th.col-1.justify-content-center - span.hidden-text Izbriši polje + span.hidden-text= t('Izbriši polje') if userRights each el in userRights @@ -207,7 +207,7 @@ block body form#form-add-user.container-xl.new-user-input.mt-4.ps-0.ms-0( action="/api/v1/users/addUser" ) - span.new-user-info NOV UPORABNIK + span.new-user-info= t('NOV UPORABNIK') .row.d-flex.justify-content-lg-start.align-items-center.mt-2.ms-0.ps-0 .col-lg-6.ms-0.ps-0.me-2 input.name-input.d-inline.form-control.ms-0.ps-0( @@ -216,13 +216,13 @@ block body autocomplete="off" ) .col.ms-xl-4.ms-0.ps-0.d-flex.justify-content-start.mt-2.mt-lg-0 - button.btn.btn-primary Dodaj + button.btn.btn-primary= t('Dodaj') - const minEntriesNum = parseInt(minEntries) - const countEntries = parseInt(entriesCount) .container-xxl.ms-0.ps-0 - .row.align-items-center - .col-sm-6.mt-4 + .row.align-items-center.mt-4 + .col-sm-6 .switch-forms-and-key-word.d-md-flex .form-check.form-switch.d-flex if publishApproval === 'F' @@ -263,20 +263,20 @@ block body form="admin-dictionary-users" checked ) - label.form-check-label(for="publish-switch") Slovar odprt + label.form-check-label(for="publish-switch")= t('Slovar odprt') .col-sm - span.name-info-txt Slovar je pregledal jezikoslovec. + span.name-info-txt= t('Slovar je odprt.') if dictionary.status === 'published' && countEntries < minEntriesNum .col-sm-6 - span.mt-3.normal-red-note Število slovarskih sestavkov je manjše od zahtevanega števila - povečajte število slovarskih sestavkov, sicer ga skrbnik slovarjev lahko zapre + span.mt-3.normal-red-note= t('Število slovarskih sestavkov je manjše od zahtevanega števila - povečajte število slovarskih sestavkov, sicer ga skrbnik slovarjev lahko zapre') if publishApproval === 'T' && dictionary.status === 'reviewed' .col-sm-6 - span.mt-3.normal-red-note Slovar je v odpiranju - čaka na potrditev skrbnika slovarjev + span.mt-3.normal-red-note= t('Slovar je v odpiranju - čaka na potrditev skrbnika slovarjev') + include /common/footer include /utilities/modal-alert include /utilities/modal-response +responseModal include /utilities/modal-alert-mixin +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer diff --git a/express/views/pages/admin/new-connection.pug b/express/views/pages/admin/new-connection.pug index fe520e5..e1f5b80 100644 --- a/express/views/pages/admin/new-connection.pug +++ b/express/views/pages/admin/new-connection.pug @@ -12,18 +12,18 @@ block body - const d = { activeLvl1: 'connections', activeLvl2: 'list' } +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Povezave', h1: 'Povezave s portali', description: 'Vnesite podatke terminološkega portala, s katerim želite povezati svoj portal.' } + - const c = { sideMenu: true, h2: t('Povezave'), h1: t('Povezave s portali'), description: t('Vnesite podatke terminološkega portala, s katerim želite povezati svoj portal.') } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-2 .row ul.col-sm-5 - span.smaller-blue-subheading Dodaj povezavo + span.smaller-blue-subheading= t('Dodaj povezavo') form#form-connection-new(method="post") .mt-3 .subject-name - label.smaller-black-uppercase(for="portal-name") IME PORTALA * + label.smaller-black-uppercase(for="portal-name")= t('IME PORTALA *') .row .col-sm-5 input#portal-name.name-input.form-control.autocomplete.d-inline( @@ -32,11 +32,11 @@ block body required ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Vnesite polno ime terminološkega portala, s katerim se povezujete. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Vnesite polno ime terminološkega portala, s katerim se povezujete.') .mt-5 .subject-name - label.smaller-black-uppercase(for="portal-label") OZNAKA PORTALA * + label.smaller-black-uppercase(for="portal-label")= t('OZNAKA PORTALA *') .row .col-sm-5 input#portal-label.name-input.form-control.autocomplete.d-inline( @@ -46,11 +46,11 @@ block body required ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Unikatna oznaka povezanega terminološkega portala za prikaz na vašem portalu, sestavljena iz dveh črk ali črke in številke. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Unikatna oznaka povezanega terminološkega portala za prikaz na vašem portalu, sestavljena iz dveh črk ali črke in številke.') .mt-5 .subject-name - label.smaller-black-uppercase(for="index-url") URL za sinhronizacijo slovarjev * + label.smaller-black-uppercase(for="index-url")= t('URL za sinhronizacijo slovarjev *') .row .col-sm-5 input#index-url.name-input.form-control.autocomplete.d-inline( @@ -59,11 +59,11 @@ block body required ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 URL naslov terminološkega portala, s katerim želite povezati svoj portal. (API klic) + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('URL naslov terminološkega portala, s katerim želite povezati svoj portal. (API klic)') .mt-5 .subject-name - label.smaller-black-uppercase(for="update-url") URL za sinhronizacijo slovarskih sestavkov * + label.smaller-black-uppercase(for="update-url")= t('URL za sinhronizacijo slovarskih sestavkov *') .row .col-sm-5 input#update-url.name-input.form-control.autocomplete.d-inline( @@ -72,16 +72,16 @@ block body required ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 URL naslov terminološkega portala, s katerim boste sinhronizirali podatke iz terminoloških virov. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('URL naslov terminološkega portala, s katerim boste sinhronizirali podatke iz terminoloških virov.') .mt-5 .row .col-sm-5 .col-sm.d-flex.flex-column.flex-sm-row.align-items-center.justify-content-center - a.btn.btn-secondary.w-sm-100(href="/admin/povezave/seznam") Prekliči + a.btn.btn-secondary.w-sm-100(href="/admin/povezave/seznam")= t('Prekliči') button.create-portal-btn.btn.btn-primary.ms-sm-4.mt-2.mt-sm-0( type="submit" form="form-connection-new" - ) Dodaj povezavo + )= t('Dodaj povezavo') - include /common/footer + include /common/footer diff --git a/express/views/pages/admin/portal-consultancy-settings.pug b/express/views/pages/admin/portal-consultancy-settings.pug index 0fb2f4a..624090a 100644 --- a/express/views/pages/admin/portal-consultancy-settings.pug +++ b/express/views/pages/admin/portal-consultancy-settings.pug @@ -12,7 +12,7 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Osnovne nastavitve', h1: 'Svetovalnica', description: 'Portal lahko povežete s Terminološko svetovalnico ZRC SAZU in tako prikazujete terminološke odgovore na svojem portalu, lahko pa vklopite lastno svetovalnico. V tem primeru morate med registriranimi uporabniki izbrati svetovalce in urednika svetovalnice, ki bodo odgovarjali na terminološka vprašanja uporabnikov.', buttons: [{ form: 'admin-settings-consult', type: 'disabled', content: 'Shrani' }] } + - const c = { sideMenu: true, h2: t('Osnovne nastavitve'), h1: t('Svetovalnica'), description: t('Portal lahko povežete s Terminološko svetovalnico ZRC SAZU in tako prikazujete terminološke odgovore na svojem portalu, lahko pa vklopite lastno svetovalnico. V tem primeru morate med registriranimi uporabniki izbrati svetovalce in urednika svetovalnice, ki bodo odgovarjali na terminološka vprašanja uporabnikov.'), buttons: [{ form: 'admin-settings-consult', type: 'disabled', content: t('Shrani') }] } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-2 @@ -26,14 +26,14 @@ block body value="own" checked ) - label.normal-gray.ms-2(for="own-consultancy") Lastna svetovalnica + label.normal-gray.ms-2(for="own-consultancy")= t('Lastna svetovalnica') .mt-3 input#zrc-consultancy.form-check-input( type="radio" name="consultancyType" value="ZRC" ) - label.normal-gray.ms-2(for="zrc-consultancy") Terminološka svetovalnica ZRC SAZU + label.normal-gray.ms-2(for="zrc-consultancy")= t('Terminološka svetovalnica ZRC SAZU') else .button-type input#own-consultancy.form-check-input( @@ -41,7 +41,7 @@ block body name="consultancyType" value="own" ) - label.normal-gray.ms-2(for="own-consultancy") Lastna svetovalnica + label.normal-gray.ms-2(for="own-consultancy")= t('Lastna svetovalnica') .mt-3 input#zrc-consultancy.form-check-input( type="radio" @@ -49,11 +49,11 @@ block body value="ZRC" checked ) - label.normal-gray.ms-2(for="zrc-consultancy") Terminološka svetovalnica ZRC SAZU + label.normal-gray.ms-2(for="zrc-consultancy")= t('Terminološka svetovalnica ZRC SAZU') .mt-4 .subject-name - label.smaller-black-uppercase(for="zrc-email") E-NASLOV + label.smaller-black-uppercase(for="zrc-email")= t('E-NASLOV') .row .col-sm-5 input#zrc-email.name-input.form-control.autocomplete.d-inline( @@ -62,11 +62,11 @@ block body value=consultancy.zrcEmail ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Vnesite e-naslov, ki ga za obveščanje o novih terminoloških vprašanjih uporabljajo terminološki svetovalci. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Vnesite e-naslov, ki ga za obveščanje o novih terminoloških vprašanjih uporabljajo terminološki svetovalci.') .mt-3 .subject-name - label.smaller-black-uppercase(for="zrc-url") POVEZAVA + label.smaller-black-uppercase(for="zrc-url")= t('POVEZAVA') .row .col-sm-5 input#zrc-url.name-input.form-control.autocomplete.d-inline( @@ -75,7 +75,7 @@ block body value=consultancy.zrcURL ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Vnesite naslov spletnega mesta, kjer so zbrani vsi odgovori terminološke svetovalnice. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Vnesite naslov spletnega mesta, kjer so zbrani vsi odgovori terminološke svetovalnice.') + include /common/footer include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/admin/portal-edit.pug b/express/views/pages/admin/portal-edit.pug index 4ec8bb7..4f26013 100644 --- a/express/views/pages/admin/portal-edit.pug +++ b/express/views/pages/admin/portal-edit.pug @@ -11,18 +11,18 @@ block body - const d = { activeLvl1: 'connections', activeLvl2: 'list' } +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Povezave', h1: 'Povezave s portali', description: 'Vnesite podatke terminološkega portala, s katerim želite povezati svoj portal.' } + - const c = { sideMenu: true, h2: t('Povezave'), h1: t('Povezave s portali'), description: t('Vnesite podatke terminološkega portala, s katerim želite povezati svoj portal.') } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-2 .row ul.col-sm-5 - span.smaller-blue-subheading Uredi povezavo + span.smaller-blue-subheading= t('Uredi povezavo') form#form-connection-update(method="post") .mt-3 .subject-name - label.smaller-black-uppercase(for="portal-name") IME PORTALA * + label.smaller-black-uppercase(for="portal-name")= t('IME PORTALA *') .row .col-sm-5 input#portal-name.name-input.form-control.autocomplete.d-inline( @@ -32,11 +32,11 @@ block body value=portal.name ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Ime terminološkega portala ... (potrebujem text) + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Vnesite polno ime terminološkega portala, s katerim se povezujete.') .mt-5 .subject-name - label.smaller-black-uppercase(for="portal-label") OZNAKA PORTALA * + label.smaller-black-uppercase(for="portal-label")= t('OZNAKA PORTALA *') .row .col-sm-5 input#portal-label.name-input.form-control.autocomplete.d-inline( @@ -47,11 +47,11 @@ block body required ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Unikatna dvočrkovna oznaka povezanega terminološkega portala za prikaz na tem portalu. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Unikatna oznaka povezanega terminološkega portala za prikaz na vašem portalu, sestavljena iz dveh črk ali črke in številke.') .mt-5 .subject-name - label.smaller-black-uppercase(for="index-url") URL za sinhronizacijo slovarjev * + label.smaller-black-uppercase(for="index-url")= t('URL za sinhronizacijo slovarjev *') .row .col-sm-5 input#index-url.name-input.form-control.autocomplete.d-inline( @@ -61,11 +61,11 @@ block body value=portal.indexURL ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 URL naslov terminološkega portala, s katerim želimo povezati svoj portal. (API klic) + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('URL naslov terminološkega portala, s katerim želimo povezati svoj portal. (API klic)') .mt-5 .subject-name - label.smaller-black-uppercase(for="update-url") URL za sinhronizacijo slovarskih sestavkov * + label.smaller-black-uppercase(for="update-url")= t('URL za sinhronizacijo slovarskih sestavkov *') .row .col-sm-5 input#update-url.name-input.form-control.autocomplete.d-inline( @@ -75,16 +75,16 @@ block body value=portal.URLupdate ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 URL naslov terminološkega portala, s katerim želimo povezati svoj portal. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('URL naslov terminološkega portala, s katerim želimo povezati svoj portal.') .mt-5 .row .col-sm-5 .col-sm.d-flex.flex-column.flex-sm-row.align-items-center.justify-content-center - a.btn.btn-secondary.w-sm-100(href="/admin/povezave/seznam") Prekliči + a.btn.btn-secondary.w-sm-100(href="/admin/povezave/seznam")= t('Prekliči') button.btn.btn-primary.ms-sm-4.mt-2.mt-sm-0( type="submit" form="form-connection-update" - ) Shrani spremembe + )= t('Shrani') - include /common/footer + include /common/footer diff --git a/express/views/pages/admin/portal-list-dict.pug b/express/views/pages/admin/portal-list-dict.pug index e7a6863..39eb62b 100644 --- a/express/views/pages/admin/portal-list-dict.pug +++ b/express/views/pages/admin/portal-list-dict.pug @@ -12,13 +12,11 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Povezave', h1: 'Povezani slovarji', description: 'Seznam slovarjev s povezanega portala.', buttons: [{ type: 'button', form: 'all-linked-dictionaries', content: 'Shrani' }] } + - const c = { sideMenu: true, h2: t('Povezave'), h1: t('Povezani slovarji'), description: t('Seznam slovarjev s povezanega portala.'), buttons: [{ type: 'button', form: 'all-linked-dictionaries', content: t('Shrani') }] } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container - .d-flex.justify-content-between - .d-flex.flex-row.mb-4 - include /components/search-and-filter/inline-search + .d-flex.justify-content-end .d-flex.me-3 include /utilities/pager +pager @@ -27,10 +25,10 @@ block body .table-responsive table.styled-table thead - tr + tr#thead th= '' - th= 'Naslov' - th= 'Oznaka portala' + th= t('Naslov') + th= t('Oznaka portala') tbody#page-results if results.length each result in results @@ -48,6 +46,6 @@ block body if result.code td= result.code else - p Ni še povezanih portalov + p= ('Ni še povezanih portalov') include /common/footer diff --git a/express/views/pages/admin/portal.pug b/express/views/pages/admin/portal.pug index 5cbe007..73b5d1c 100644 --- a/express/views/pages/admin/portal.pug +++ b/express/views/pages/admin/portal.pug @@ -11,7 +11,7 @@ block body - const d = { activeLvl1: 'settings', activeLvl2: 'portals' } +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Osnovne nastavitve', h1: 'Portal', description: 'Administratorski del portala je namenjen skupini oseb, ki vsebinsko in tehnično ureja portal, odpira slovarske vire, daje pooblastila posameznim uporabnikom in skrbi za vsebinsko in tehnično urejenost portala. Izberite module, ki jih boste ponudili na terminološkem portalu in na kratko opišite, kaj ponuja vaš portal.', buttons: [{ type: 'disabled', form: 'admin-portal-settings', content: 'Shrani' }] } + - const c = { sideMenu: true, h2: t('Osnovne nastavitve'), h1: t('Portal'), description: t('Administratorski del portala je namenjen skupini oseb, ki vsebinsko in tehnično ureja portal, odpira slovarske vire, daje pooblastila posameznim uporabnikom in skrbi za vsebinsko in tehnično urejenost portala. Izberite module, ki jih boste ponudili na terminološkem portalu in na kratko opišite, kaj ponuja vaš portal.'), buttons: [{ type: 'disabled', form: 'admin-portal-settings', content: t('Shrani') }] } +mainHeader(c) .content-hold-prerequisites @@ -19,20 +19,33 @@ block body form#admin-portal-settings(method="post") .port-name .subject-name - label.smaller-black-uppercase(for="portal-name") IME PORTALA + label.smaller-black-uppercase(for="portal-name-sl")= t('IME PORTALA') .row .col-sm-5 - input#portal-name.name-input.form-control.autocomplete.d-inline( + input#portal-name-sl.name-input.form-control.autocomplete.d-inline( type="text" - name="portalName" - value=portal.name + name="portalNameSl" + value=portal.nameSl ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Polno ime terminološkega portala. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Polno ime terminološkega portala.') .mt-3 .subject-name - label.smaller-black-uppercase(for="dictionary-label") OZNAKA PORTALA * + label.smaller-black-uppercase(for="portal-name-en")= t('ANGLEŠKO IME PORTALA') + .row + .col-sm-5 + input#portal-name-en.name-input.form-control.autocomplete.d-inline( + type="text" + name="portalNameEn" + value=portal.nameEn + ) + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Angleško ime terminološkega portala.') + + .mt-3 + .subject-name + label.smaller-black-uppercase(for="dictionary-label")= t('OZNAKA PORTALA *') .row .col-sm-5 input#dictionary-label.name-input.form-control.autocomplete.d-inline.w-25( @@ -43,21 +56,31 @@ block body required ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Dvočrkovna oznaka terminološkega portala. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Dvočrkovna oznaka terminološkega portala.') #text-editor.mt-sm-3 .subject-name - span.smaller-black-uppercase OPIS PORTALA + span.smaller-black-uppercase= t('OPIS PORTALA') div textarea.summernote( - name="portalDescription" - value=portal.description + name="portalDescriptionSl" + value=portal.descriptionSl ) - p= portal.description + p= portal.descriptionSl + + //- TODO: render values once eng columns are added in db + .subject-name.mt-3 + span.smaller-black-uppercase= t('ANGLEŠKI OPIS PORTALA') + div + textarea.summernote( + name="portalDescriptionEn" + value=portal.descriptionEn + ) + p= portal.descriptionEn .moduls.mt-3 .subject-name - label.smaller-black-uppercase(for="dictionary-label") MODULI + label.smaller-black-uppercase(for="dictionary-label")= t('MODULI') .d-grid .ms-1.d-sm-flex.mt-2.row .form-check.form-switch.d-flex.align-items-center.col-sm-5 @@ -73,9 +96,9 @@ block body type="checkbox" name="isExtractionEnabled" ) - label.form-check-label.normal-gray-label(for="extraction") Luščenje + label.form-check-label.normal-gray-label(for="extraction")= t('Luščenje') .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info Modul za luščenje terminoloških kandidatov iz besedil. + span.d-md-inline.d-block.smaller-gray-info= t('Modul za luščenje terminoloških kandidatov iz besedil.') .ms-1.d-sm-flex.mt-4.row .form-check.form-switch.d-flex.align-items-center.col-sm-5 @@ -91,9 +114,9 @@ block body type="checkbox" name="isDictionariesEnabled" ) - label.form-check-label.normal-gray-label(for="edit") Urejanje + label.form-check-label.normal-gray-label(for="edit")= t('Urejanje') .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info Modul za urejanje terminoloških slovarjev. + span.d-md-inline.d-block.smaller-gray-info= t('Modul za urejanje terminoloških slovarjev.') .ms-1.d-sm-flex.mt-4.row .form-check.form-switch.d-flex.align-items-center.col-sm-5 @@ -109,9 +132,9 @@ block body type="checkbox" name="isConsultancyEnabled" ) - label.form-check-label.normal-gray-label(for="consulting") Svetovanje + label.form-check-label.normal-gray-label(for="consulting")= t('Svetovanje') .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info Modul za svetovanje pri terminoloških zagatah. + span.d-md-inline.d-block.smaller-gray-info= t('Modul za svetovanje pri terminoloških zagatah.') + include /common/footer include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/admin/portals-all-linked-dictionaries.pug b/express/views/pages/admin/portals-all-linked-dictionaries.pug index 871fb7c..b198311 100644 --- a/express/views/pages/admin/portals-all-linked-dictionaries.pug +++ b/express/views/pages/admin/portals-all-linked-dictionaries.pug @@ -13,14 +13,12 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Povezave', h1: 'Vsi povezani slovarji', description: 'Poiščite naslove terminoloških slovarjev, s katerimi želite dopolniti iskalne zadetke na svojem portalu. Lahko izberete vse slovarje ali le nekatere. Svoje izbire morate shraniti. Vse vaše izbire mora potrditi administrator povezanega portala.', buttons: [{ type: 'button', form: 'all-linked-dictionaries', content: 'Shrani' }] } + - const c = { sideMenu: true, h2: t('Povezave'), h1: t('Vsi povezani slovarji'), description: t('Poiščite naslove terminoloških slovarjev, s katerimi želite dopolniti iskalne zadetke na svojem portalu. Lahko izberete vse slovarje ali le nekatere. Svoje izbire morate shraniti. Vse vaše izbire mora potrditi administrator povezanega portala.'), buttons: [{ type: 'button', form: 'all-linked-dictionaries', content: t('Shrani') }] } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-2 if results.length - .d-flex.justify-content-between - .d-flex.flex-row.mb-4 - include /components/search-and-filter/inline-search + .d-flex.justify-content-end .d-flex.me-3 include /utilities/pager +pager @@ -29,10 +27,10 @@ block body .table-responsive table.styled-table thead - tr + tr#thead th= '' - th= 'Naslov' - th= 'Oznaka portala' + th= t('Naslov') + th= t('Oznaka portala') th= '' tbody#page-results each result in results @@ -51,6 +49,6 @@ block body td= result.code else .d-flex.justify-content-center - p Ni še povezanih portalov + p= t('Ni še povezanih portalov') - include /common/footer + include /common/footer diff --git a/express/views/pages/admin/settings-dictionaries.pug b/express/views/pages/admin/settings-dictionaries.pug index e62bdeb..ccae398 100644 --- a/express/views/pages/admin/settings-dictionaries.pug +++ b/express/views/pages/admin/settings-dictionaries.pug @@ -12,14 +12,14 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Osnovne nastavitve', h1: 'Slovarji', description: 'Določite lastnosti terminoloških virov na portalu, zlasti minimalno število sestavkov, ki so pogoj za objavo, možnosti potrjevanja objave novih slovarjev, število različic slovarja, ki jih lahko hrani posamezni uporabnik. Te nastavitve veljajo za vse terminološke vire na portalu.', buttons: [{ form: 'admin-settings-dict', type: 'disabled', content: 'Shrani' }] } + - const c = { sideMenu: true, h2: t('Osnovne nastavitve'), h1: t('Slovarji'), description: t('Določite lastnosti terminoloških virov na portalu, zlasti minimalno število sestavkov, ki so pogoj za objavo, možnosti potrjevanja objave novih slovarjev, število različic slovarja, ki jih lahko hrani posamezni uporabnik. Te nastavitve veljajo za vse terminološke vire na portalu.'), buttons: [{ form: 'admin-settings-dict', type: 'disabled', content: t('Shrani') }] } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-2 form#admin-settings-dict(method="post") .min-composition .subject-name - label.smaller-black-uppercase(for="min-composition") MINIMALNO ŠTEVILO SLOVARSKIH SESTAVKOV + label.smaller-black-uppercase(for="min-composition")= t('MINIMALNO ŠTEVILO SLOVARSKIH SESTAVKOV') .row .col-sm-5 select#min-composition.name-input.form-control.autocomplete.d-inline.w-25( @@ -43,11 +43,11 @@ block body selected=dictionary.minEntriesPerDictionary === '100' ) 100 .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Število slovarskih sestavkov, ki jih mora vsebovati slovar, da je omogočena objava slovarja na portalu. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Število slovarskih sestavkov, ki jih mora vsebovati slovar, da je omogočena objava slovarja na portalu.') .mt-4 .subject-name - label.smaller-black-uppercase(for="publish-control") KONTROLA OBJAVE + label.smaller-black-uppercase(for="publish-control")= t('KONTROLA OBJAVE') .ms-1.row .form-check.form-switch.d-flex.align-items-center.col-sm-5 if dictionary.dictionaryPublishApproval @@ -64,50 +64,13 @@ block body ) label.form-check-label.normal-gray-label.ms-3( for="publish-control" - ) Kontrola objave + )= t('Kontrola objave') .col-sm.d-flex.align-items-center.ps-0 - span.smaller-gray-info.ms-xxl-3.ms-md-3 Za prvo objavo slovarja je potrebno dovoljenje skrbnika slovarjev. - - //- .mt-4 - //- .subject-name - //- label.smaller-black-uppercase(for="max-copy-count") MAKSIMALNO ŠTEVILO KOPIJ SLOVARJEV - //- .row - //- .col-sm-5 - //- input#max-copy-count.name-input.form-control.autocomplete.d-inline.w-25( - //- type="text" - //- maxlength="6" - //- name="keepNumOfExportsPerDict" - //- value=dictionary.keepNumOfExportsPerDict - //- ) - //- .col-sm.d-flex.align-items-center - //- span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Število zadnjih kopij spremenjenih slovarjev, ki se hranijo. Če vrednost ni določena, omejitve ni. - - //- .mt-4 - //- .subject-name - //- label.smaller-black-uppercase(for="autosave") SAMODEJNO SHRANJEVANJE - //- .row - //- .col-sm-5 - //- select#autosave.name-input.form-select.autocomplete.d-inline.w-50( - //- name="dictionaryAutoSaveFrequency" - //- ) - //- if dictionary.dictionaryAutoSaveFrequency === 'disabled' - //- option(selected value="disabled") Onemogočeno - //- option(value="monthly") Mesečno - //- option(value="yearly") Letno - //- if dictionary.dictionaryAutoSaveFrequency === 'monthly' - //- option(selected value="monthly") Mesečno - //- option(value="disabled") Onemogočeno - //- option(value="yearly") Letno - //- if dictionary.dictionaryAutoSaveFrequency === 'yearly' - //- option(selected value="yearly") Letno - //- option(value="disabled") Onemogočeno - //- option(value="monthly") Mesečno - //- .col-sm.d-flex.align-items-center - //- span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Časovno obdobje, v katerem se slovar samodejno shranjuje. + span.smaller-gray-info.ms-xxl-3.ms-md-3= t('Za prvo objavo slovarja je potrebno dovoljenje skrbnika slovarjev.') .mt-4 .subject-name - label.smaller-black-uppercase(for="max-composition-count") MAKSIMALNO ŠTEVILO KOPIJ SLOVARSKIH SESTAVKOV + label.smaller-black-uppercase(for="max-composition-count")= t('MAKSIMALNO ŠTEVILO KOPIJ SLOVARSKIH SESTAVKOV') .row .col-sm-5 input#max-composition-count.name-input.form-control.autocomplete.d-inline.w-25( @@ -117,12 +80,12 @@ block body value=dictionary.numOfHistoryEntriesPerEntry ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Število zadnjih kopij spremenjenih slovarskih sestavkov, ki se hranijo. Če vrednost ni določena, omejitve ni. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Število zadnjih kopij spremenjenih slovarskih sestavkov, ki se hranijo. Če vrednost ni določena, omejitve ni.') .mt-4 .row .subject-name.col-sm-5 - label.smaller-black-uppercase MOŽNOST OBJAVE SLOVARSKEGA SESTAVKA MED UREJANJEM + label.smaller-black-uppercase= t('MOŽNOST OBJAVE SLOVARSKEGA SESTAVKA MED UREJANJEM') .d-grid .ms-1.d-sm-flex.row .form-check.form-switch.d-flex.align-items-center.col-sm-5 @@ -133,75 +96,15 @@ block body name="canPublishEntriesInEdit" checked ) - label.form-check-label.normal-gray-label(for="in-editing") Objava med urejanjem + label.form-check-label.normal-gray-label(for="in-editing")= t('Objava med urejanjem') else input#in-editing.form-check-input( type="checkbox" name="canPublishEntriesInEdit" ) - label.form-check-label.normal-gray-label(for="in-editing") Objava med urejanjem + label.form-check-label.normal-gray-label(for="in-editing")= t('Objava med urejanjem') .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-md-2 Poleg objave slovarskega sestavka v fazi "Urejeno" je omogočena tudi objava v fazi "V urejanju". - //- .ms-1.d-sm-flex.mt-2.row - //- .form-check.form-switch.d-flex.align-items-center.col-sm-2 - //- input#suggestion.form-check-input( - //- type="checkbox" - //- disabled - //- checked - //- ) - //- label.form-check-label.normal-gray-label(for="suggestion") Predlog - //- .col-sm.d-flex.align-items-center - //- span.d-md-inline.d-block.smaller-gray-info Modul za luščenje terminoloških kandidatov iz besedil. - //- span.d-md-inline.d-block.smaller-gray-info Modul za urejanje terminoloških slovarjev. - - //- .ms-1.d-sm-flex.mt-4.row - //- .form-check.form-switch.d-flex.align-items-center.col-sm-2 - //- if dictionary.enabledTerminologyReview - //- if dictionary.enabledTerminologyReview === 'T' - //- input#pro-checked.form-check-input( - //- type="checkbox" - //- checked - //- name="enabledTerminologyReview" - //- ) - //- else - //- input#pro-checked.form-check-input( - //- type="checkbox" - //- name="enabledTerminologyReview" - //- ) - //- label.form-check-label.normal-gray-label(for="pro-checked") Strokovno pregledano - //- .col-sm.d-flex.align-items-center - //- span.d-md-inline.d-block.smaller-gray-info Modul za urejanje terminoloških slovarjev. - - //- .ms-1.d-sm-flex.mt-4.row - //- .form-check.form-switch.d-flex.align-items-center.col-sm-2 - //- if dictionary.enabledLanguageReview - //- if dictionary.enabledLanguageReview === 'T' - //- input#linguistically-checked.form-check-input( - //- type="checkbox" - //- checked - //- name="enabledLanguageReview" - //- ) - //- else - //- input#linguistically-checked.form-check-input( - //- type="checkbox" - //- name="enabledLanguageReview" - //- ) - //- label.form-check-label.normal-gray-label( - //- for="linguistically-checked" - //- ) Jezikovno pregledano - //- .col-sm.d-flex.align-items-center - //- span.d-md-inline.d-block.smaller-gray-info Modul za urejanje terminoloških slovarjev. - - //- .ms-1.d-sm-flex.mt-4.row - //- .form-check.form-switch.d-flex.align-items-center.col-sm-2 - //- input#consulting.form-check-input( - //- type="checkbox" - //- checked - //- disabled - //- ) - //- label.form-check-label.normal-gray-label(for="consulting") Urejeno - //- .col-sm.d-flex.align-items-center - //- span.d-md-inline.d-block.smaller-gray-info Modul za svetovanje pri terminoloških zagatah. + span.d-md-inline.d-block.smaller-gray-info.ms-md-2= t('Poleg objave slovarskega sestavka v fazi "Urejeno" je omogočena tudi objava v fazi "V urejanju".') + include /common/footer include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/admin/user-edit.pug b/express/views/pages/admin/user-edit.pug index fec745e..95b9cf6 100644 --- a/express/views/pages/admin/user-edit.pug +++ b/express/views/pages/admin/user-edit.pug @@ -10,7 +10,7 @@ block body include /common/side-menu-fake +sideNavigationFake include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: false, h2: userData.firstName + ' ' + userData.lastName, h1: 'Uporabniki', description: 'V tem razdelku lahko določite fazo urejanja slovarja, dodajate uporabnike in jim določate uporabniške pravice pri urejanju slovarja.', buttons: [{ type: 'cancel', content: 'Prekliči', url:"/admin/uporabniki/seznam", classAtr: "btn btn-secondary header-btn-secondary small-header-btn"}, { type: 'disabled', content: 'Shrani', form: 'form-edit-user' }] } + - const c = { sideMenu: false, h2: userData.firstName + ' ' + userData.lastName, h1: t('Uporabniki'), description: t('V tem razdelku lahko določite uporabniške vloge posameznega uporabnika in urejate njegove podatke.'), buttons: [{ type: 'cancel', content: t('Prekliči'), url:"/admin/uporabniki/seznam", classAtr: "btn btn-secondary header-btn-secondary small-header-btn"}, { type: 'disabled', content: t('Shrani'), form: 'form-edit-user' }] } +mainHeader(c) .content-hold-prerequisites.mt-4 #offset-main @@ -24,7 +24,7 @@ block body form#form-edit-user.needs-validation(method="post" novalidate) .mt-2 .subject-name - label.smaller-black-uppercase(for="user-name") Uporabniško ime + label.smaller-black-uppercase(for="user-name")= t('Uporabniško ime') .row .col-sm-5 input#user-name.name-input.form-control( @@ -33,11 +33,11 @@ block body name="username" ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Ime, s katerim se uporabnik predstavlja na terminološkem portalu. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Ime, s katerim se uporabnik predstavlja na terminološkem portalu.') .mt-2 .subject-name - label.smaller-black-uppercase(for="user-fname") Ime + label.smaller-black-uppercase(for="user-fname")= t('Ime') .row .col-sm-5 input#user-fname.name-input.form-control( @@ -46,11 +46,11 @@ block body name="firstName" ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Ime registriranega uporabnika. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Ime registriranega uporabnika.') .mt-2 .subject-name - label.smaller-black-uppercase(for="user-lastname") Priimek + label.smaller-black-uppercase(for="user-lastname")= t('Priimek') .row .col-sm-5 input#user-lastname.name-input.form-control( @@ -59,11 +59,11 @@ block body name="lastName" ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Priimek registriranega uporabnika. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Priimek registriranega uporabnika.') .mt-2 .subject-name - label.smaller-black-uppercase(for="user-mail") E-naslov + label.smaller-black-uppercase(for="user-mail")= t('E-naslov') .row .col-sm-5 input#user-mail.name-input.form-control( @@ -72,7 +72,7 @@ block body name="email" ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Veljavni elektronski naslov uporabnika, na katerega uporabnik prejema sporočila, povezana s portalom. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Veljavni elektronski naslov uporabnika, na katerega uporabnik prejema sporočila, povezana s portalom.') //- .mt-2 //- .subject-name @@ -101,45 +101,45 @@ block body .mt-2.mb-4 .subject-name - label.smaller-black-uppercase(for="user-is-confirmed") Potrjen + label.smaller-black-uppercase(for="user-is-confirmed")= t('Potrjen') .row .col-sm-5 input#user-is-confirmed.form-check-input( type="checkbox" - value="" - name="isUserConfirmed" + name="status" + checked=userData.status === 'active' ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Registracija uporabnika je potrjena. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Registracija uporabnika je potrjena.') .d-flex.justify-content-between.container-xl.ps-0.ms-0.mt-4.mb-4 button#edit-user-remove.btn.btn-secondary(type="button") img.me-2(src="/images/red-trash-icon.svg" alt="Delete") - | Briši uporabnika - span.users-subtitle-txt Uporabniške vloge + = t('Briši uporabnika') + span.users-subtitle-txt= t('Uporabniške vloge') if userRoles .container-xl.ps-0.ms-0.mt-3 table.styled-table-left-header thead if userRoles.roles.isPortalAdmin tr - th.table-left-header= 'Skrbnik portala' + th.table-left-header= t('Skrbnik portala') td= '' if userRoles.roles.isDictionariesAdmin tr - th.table-left-header= 'Skrbnik slovarjev' + th.table-left-header= t('Skrbnik slovarjev') td= '' if userRoles.roles.isConsultancyAdmin tr - th.table-left-header= 'Skrbnik svetovalnice' + th.table-left-header= t('Skrbnik svetovalnice') td= '' if userRoles.roles.isConsultant tr - th.table-left-header= 'Svetovalec' + th.table-left-header= t('Svetovalec') td= '' if userRoles.roles.isEditor tr - th.table-left-header= 'Urednik slovarja' + th.table-left-header= t('Urednik slovarja') td= '' include /common/footer diff --git a/express/views/pages/admin/user-list.pug b/express/views/pages/admin/user-list.pug index 4cb5070..3a0d480 100644 --- a/express/views/pages/admin/user-list.pug +++ b/express/views/pages/admin/user-list.pug @@ -13,14 +13,12 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Uporabniki', h1: 'Seznam', description: 'Na seznamu uporabnikov lahko določite posameznemu uporabniku dodatne vloge ali urejate njihove podatke.' } + - const c = { sideMenu: true, h2: t('Uporabniki'), h1: t('Seznam'), description: t('Na seznamu uporabnikov lahko določite posameznemu uporabniku dodatne vloge ali urejate njihove podatke.') } +mainHeader(c) .content-hold-prerequisites.mt-4 #offset-main.main-container.mt-4 .table-full - .d-flex.w-100.justify-content-between.mb-4 - .d-flex - include /components/search-and-filter/inline-search + .d-flex.w-100.justify-content-end.mb-4 .d-flex include /utilities/pager +pager @@ -28,10 +26,10 @@ block body .table-responsive table.styled-table thead - tr - th= 'Uporabniško ime' - th= 'E - naslov' - th= 'Potrjen' + tr#thead + th= t('Uporabniško ime') + th= t('E - naslov') + th= t('Potrjen') th= '' tbody#page-results each result in results @@ -44,7 +42,7 @@ block body type="link" href=`/admin/uporabniki/${result.id}/urejanje` ) - img(src="/images/u_edit-alt.svg" alt="Uredi") - span.normal-gray.ms-1 Uredi + img(src="/images/u_edit-alt.svg" alt=t('Uredi')) + span.normal-gray.ms-1= t('Uredi') - include /common/footer + include /common/footer diff --git a/express/views/pages/admin/user-portals.pug b/express/views/pages/admin/user-portals.pug index 695eaad..163485e 100644 --- a/express/views/pages/admin/user-portals.pug +++ b/express/views/pages/admin/user-portals.pug @@ -12,26 +12,26 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: 'Uporabniki', h1: 'Portal', description: 'V tem razdelku lahko določite glavne administratorske pravice na terminološkem portalu.', buttons: [{ type: 'disabled', content: 'Shrani', form: 'form-user-roles' }] } + - const c = { sideMenu: true, h2: t('Uporabniki'), h1: t('Portal'), description: t('V tem razdelku lahko določite glavne administratorske pravice na terminološkem portalu.'), buttons: [{ type: 'disabled', content: t('Shrani'), form: 'form-user-roles' }] } +mainHeader(c) .content-hold-prerequisites.mt-4 #offset-main.main-container.mt-1 - span.users-subtitle-txt.ms-0.mt-3 Uporabniške pravice/vloge + span.users-subtitle-txt.ms-0.mt-3= t('Uporabniške pravice/vloge') form#form-user-roles(method="post") table#user-roles-table.table-users.table-borderless.align-middle.mt-3 thead tr th.col-2 - span.user-rights-column-title Uporabnik + span.user-rights-column-title= t('Uporabnik') th.col-2 - span.user-rights-column-title Skrbnik portala + span.user-rights-column-title= t('Skrbnik portala') th.col-2 - span.user-rights-column-title.ms-xxl-4 Skrbnik slovarjev + span.user-rights-column-title.ms-xxl-4= t('Skrbnik slovarjev') th.col-2 - span.user-rights-column-title.ms-xxl-4 Skrbnik svetovalnice + span.user-rights-column-title.ms-xxl-4= t('Skrbnik svetovalnice') th.col-1.justify-content-center - span.hidden-text Izbriši polje + span.hidden-text= t('Izbriši polje') each el in users tbody.user-data.pb-xl-2 tr @@ -96,7 +96,7 @@ block body form#form-add-user.container-xl.new-user-input.mt-4.ps-0.ms-0( action="/api/v1/users/addUser" ) - span.new-user-info NOV UPORABNIK + span.new-user-info= t('NOV UPORABNIK') .row.d-flex.justify-content-lg-start.align-items-center.mt-2.ms-0.ps-0 .col-lg-6.ms-0.ps-0.me-2 input.name-input.d-inline.form-control.ms-0.ps-0( @@ -105,10 +105,10 @@ block body autocomplete="off" ) .col.ms-xl-4.ms-0.ps-0.d-flex.justify-content-start.mt-2.mt-lg-0 - button.btn.btn-primary Dodaj + button.btn.btn-primary= t('Dodaj') + include /common/footer include /utilities/modal-alert include /utilities/modal-response +responseModal include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/consultancy/admin/consultancy-admin-inheritable-module.pug b/express/views/pages/consultancy/admin/consultancy-admin-inheritable-module.pug index 1223c71..5093300 100644 --- a/express/views/pages/consultancy/admin/consultancy-admin-inheritable-module.pug +++ b/express/views/pages/consultancy/admin/consultancy-admin-inheritable-module.pug @@ -10,6 +10,11 @@ block pageSpecificScipts script(nonce=cspNonce src="/javascripts/consultancy-search-mechanism.js") block body + input#consultancyContext( + type="hidden" + name="consultancyContext" + value=consultancyPageType + ) - const specificUserRights = user && (user.hasRole('consultant') || user.hasRole('consultancy admin')) //- .consultancy-padding-admin.consultancy-container-unique section#fixed-top-section @@ -17,8 +22,8 @@ block body +main-navigation(false) block config - const pageTypeObject = { new: true } - - const subHeading = 'Novo' - - const description = 'Seznam vprašanj, ki so jih poslali uporabniki, in še niso bila dodeljena moderatorjem.' + - const subHeading = t('Novo') + - const description = t('Seznam vprašanj, ki so jih poslali uporabniki, in še niso bila dodeljena moderatorjem.') +sideMenu(pageTypeObject) @@ -32,33 +37,25 @@ block body include /utilities/consultancy-admin-panel-header //- const c = { h1: 'Terminološko svetovanje', description: 'Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj, pa strokovnjaki čutijo potrebo po sistemskem poenotenju oz. izbiri najprimernejše rešitve.', buttons: [{ type: 'link', content: 'Zastavi novo vprašanje', url: '/svetovanje/vprasanje/novo' }] } // - 'Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj.' - - const c = { specificRights: specificUserRights, exportButtonPresent: false, h1: subHeading, h2: 'Administratorska konzola', description: description } + - const c = { specificRights: specificUserRights, exportButtonPresent: false, h1: subHeading, h2: t('Administratorska konzola'), description: description } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container include /components/consultancy/admin/consultancy-item-admin block consultancyContainer - const section = pageTypeObject - .consultancy-container - if pageTypeObject.new - each entry in entries - +consultancyItem(entry, section) - else - //- - const moderatorList = entries.filter(a => a.isModerator) - each entry in entries - //- - - entry.sharedAuthors = entries.filter(a => { - return !a.isModerator && entry.id === a.id - }).map(a => a.name = `${a.firstName} ${a.lastName}`) - +consultancyItem(entry, section) + #results-data.consultancy-container + each entry in entries + +consultancyItem(entry, section) + include /common/footer else include /utilities/empty-main-panel-header +mainHeader .content-hold-prerequisites.ps-4.pe-4 #offset-main.ps-2.pe-2 p Nimate ustreznih pravic + include /common/footer include ../../../utilities/modal-alert-mixin - +alertModal("deleteModal","Izbriši" ,"Prekliči" , "del-btn", "cancel-btn") - include /common/footer + +alertModal("deleteModal",t("Izbriši") ,t("Prekliči") , "del-btn", "cancel-btn") diff --git a/express/views/pages/consultancy/admin/dialogs/assign.pug b/express/views/pages/consultancy/admin/dialogs/assign.pug index 50daf4f..c45f420 100644 --- a/express/views/pages/consultancy/admin/dialogs/assign.pug +++ b/express/views/pages/consultancy/admin/dialogs/assign.pug @@ -29,7 +29,7 @@ mixin consultancyAdminAssignModal .modal-dialog .modal-content .modal-header.mb-0.pb-0 - h5.pt-3.navigation-text-color Dodeli + h5.pt-3.navigation-text-color #{ t('Dodeli') } button.btn-close( type="button" data-bs-dismiss="modal" @@ -43,9 +43,9 @@ mixin consultancyAdminAssignModal button.btn.btn-secondary.height50( type="button" data-bs-dismiss="modal" - ) Zapri + ) #{ t('Zapri') } .col.d-flex.justify-content-end.mb-2 button#assign-btn.btn.btn-primary.height50( type="button" data-bs-dismiss="modal" - ) Uporabi + ) #{ t('Uporabi') } diff --git a/express/views/pages/consultancy/admin/dialogs/share.pug b/express/views/pages/consultancy/admin/dialogs/share.pug index d716dbb..04b772f 100644 --- a/express/views/pages/consultancy/admin/dialogs/share.pug +++ b/express/views/pages/consultancy/admin/dialogs/share.pug @@ -41,7 +41,7 @@ mixin consultancyAdminShareModal .modal-dialog .modal-content .modal-header.mb-0.pb-0 - h5.pt-3.navigation-text-color Deli + h5.pt-3.navigation-text-color #{ t('Deli') } button.btn-close( type="button" data-bs-dismiss="modal" @@ -55,6 +55,6 @@ mixin consultancyAdminShareModal button#close-shared.btn.btn-secondary.height50( type="button" data-bs-dismiss="modal" - ) Zapri + ) #{ t('Zapri') } #add-shared-author.col.d-flex.justify-content-end.mb-2 - button.btn.btn-primary.height50(type="button") Dodaj + button.btn.btn-primary.height50(type="button") #{ t('Dodaj') } diff --git a/express/views/pages/consultancy/admin/edit.pug b/express/views/pages/consultancy/admin/edit.pug index 870f5b5..2fafccf 100644 --- a/express/views/pages/consultancy/admin/edit.pug +++ b/express/views/pages/consultancy/admin/edit.pug @@ -19,8 +19,8 @@ block body +main-navigation(false) block config - const pageTypeObject = sentFrom - - const subHeading = 'Urejanje' - - const description = 'Dodajte terminološki odgovor in ga utemeljite.' + - const subHeading = t('Urejanje') + - const description = t('Dodajte terminološki odgovor in ga utemeljite.') +sideMenu(pageTypeObject) @@ -28,32 +28,32 @@ block body include /utilities/consultancy-admin-in-progress-panel-header //- const c = { h1: 'Terminološko svetovanje', description: 'Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj, pa strokovnjaki čutijo potrebo po sistemskem poenotenju oz. izbiri najprimernejše rešitve.', buttons: [{ type: 'link', content: 'Zastavi novo vprašanje', url: '/svetovanje/vprasanje/novo' }] } - - const c = { noSidebar: true, hrefurl: `/svetovanje/vprasanje/admin/${sentFrom}`, specificRights: specificUserRights, exportButtonPresent: false, h1: 'Urejanje terminološkega odgovora', description: 'Dodajte terminološki odgovor in ga utemeljite.', buttons: [{ form: 'edit-form', type: 'button', content: 'Shrani spremembe', contentDisabled: true }] } + - const c = { noSidebar: true, hrefurl: `/svetovanje/vprasanje/admin/${sentFrom}`, specificRights: specificUserRights, exportButtonPresent: false, h1: t('Urejanje terminološkega odgovora'), description: t('Dodajte terminološki odgovor in ga utemeljite.'), buttons: [{ form: 'edit-form', type: 'button', content: t('Shrani'), contentDisabled: true }] } +mainHeader(c) if specificUserRights .content-hold-prerequisites #offset-main.main-container.offset-correction .row.mt-4.mx-0 .col.g-0 - p.text-header-description-gray.mb-2 Datum vprašanja: #{ entry.timeCreated } - p.text-header-description-gray.mt-2.mb-0 Uporabnik: #{ author.firstName } - p.text-header-description-gray.mt-1 E-pošta: #{ author.email } + p.text-header-description-gray.mb-2 #{ t('Datum vprašanja:') } #{ entry.timeCreated } + p.text-header-description-gray.mt-2.mb-0 #{ t('Uporabnik') }: #{ author.firstName } + p.text-header-description-gray.mt-1 #{ t('E-pošta') }: #{ author.email } hr - p.text-header-description-gray.mb-2 Datum objave/spremembe: #{ entry.timePublished } - p.text-header-description-gray.mt-2.mb-0 Avtorji mnenja: + p.text-header-description-gray.mb-2 #{ t('Datum objave/spremembe') }: #{ entry.timePublished } + p.text-header-description-gray.mt-2.mb-0 #{ t('Avtorji mnenja:') } span.text-header-description-gray= ` ${entry.answerAuthors ? entry.answerAuthors.join(`, `) : ''}` - p.text-header-description-gray.mt-1 URL povezava do mnenja: #{ isPublished?`${urlPrefix}/svetovanje/vprasanje/${id}`:'' } + p.text-header-description-gray.mt-1 #{ t('URL povezava do mnenja:') } #{ isPublished?`${urlPrefix}/svetovanje/vprasanje/${id}`:'' } hr form#edit-form.row(method="put" action="/api/v1/consultancy/entry") input(type="hidden" name="_method" value="put") input#entry-id.d-none(type="text" value=id) .col - h5.navigation-text-color Urejanje + h5.navigation-text-color #{ t('Urejanje') } #question-title-field.mt-4 .title.mt-4 .subject-name.col.d-flex.justify-content-between - label.mb-1.smaller-gray-uppercase(for="address") NASLOV* + label.mb-1.smaller-gray-uppercase(for="address") #{ t('NASLOV') }* - const type = 'two' +mixedContentBtns .row @@ -67,7 +67,7 @@ block body .cerif-area.mt-4 .subject-name - label.smaller-gray-uppercase(for="select-cerif") PODROČJE + label.smaller-gray-uppercase(for="select-cerif") #{ t('PODROČJE') } .row #cerif select#select-cerif.select-cerif.name-input.d-inline.form-control( @@ -80,14 +80,14 @@ block body value=domain.id selected=domain.id === entry.domainPrimaryId )= domain.nameSl - #invalid-section.invalid-feedback-selection.hidden-section.mt-3 Nimate izbranega področja CERIF. + #invalid-section.invalid-feedback-selection.hidden-section.mt-3 #{ t('Nimate izbranega področja CERIF.') } //- .col-sm.d-flex.align-items-center span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Področje, v katero sodi opisani terminološki problem #question-field.mt-4 .title.mt-4 .subject-name.col.d-flex.justify-content-between - label.mb-1.smaller-gray-uppercase(for="questionTitle") VPRAŠANJE* + label.mb-1.smaller-gray-uppercase(for="questionTitle") #{ t('VPRAŠANJE') }* +mixedContentBtns(type) .row .col @@ -100,7 +100,7 @@ block body #text-editor.mt-4 .subject-name - span.smaller-gray-uppercase MNENJE* + span.smaller-gray-uppercase #{ t('MNENJE') }* .row .col - @@ -110,24 +110,24 @@ block body } else { if(entry.institution){ innerHTML += ` - Institucija: ${entry.institution} + ${t('Institucija')}: ${entry.institution}

` } if(entry.description){ innerHTML += ` - Opis terminološkega problema: ${entry.description} + ${t('Opis terminološkega problema')}: ${entry.description}

` } if(entry.existingSolutions){ innerHTML += ` - Obstoječe poimenovalne rešitve: ${entry.existingSolutions} + ${t('Obstoječe poimenovalne rešitve')}: ${entry.existingSolutions}

` } if(entry.examplesOfUse){ innerHTML += ` - Primeri rabe: ${entry.examplesOfUse} + ${t('Primeri rabe')}: ${entry.examplesOfUse}


` } @@ -136,14 +136,14 @@ block body #opinion.summernote !{ innerHTML } //- .col-sm.d-flex.align-items-center span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Navodilo k posamičnemu polju. Reprehenderit aliqua quis ut velit eu irure non ad sunt sunt ipsum sunt esse. - .comments-content.col-12.col-md-12.py-md-3.bd-content + .comments-content.col-12.col-md-12.pt-md-3.bd-content include /utilities/comments-with-pager + include /common/footer else include /utilities/empty-main-panel-header +mainHeader .content-hold-prerequisites.ps-4.pe-4 #offset-main.ps-2.pe-2 - p Nimate ustreznih pravic + p #{ t('Nimate ustreznih pravic') } include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/consultancy/admin/in-progress.pug b/express/views/pages/consultancy/admin/in-progress.pug index 14d06eb..bb753ef 100644 --- a/express/views/pages/consultancy/admin/in-progress.pug +++ b/express/views/pages/consultancy/admin/in-progress.pug @@ -2,5 +2,5 @@ extends /pages/consultancy/admin/consultancy-admin-inheritable-module block config - const pageTypeObject = { inProgress: true } - - const subHeading = 'V delu' - - const description = 'Seznam vprašanj, ki so še v urejanju.' + - const subHeading = t('V delu') + - const description = t('Seznam vprašanj, ki so še v urejanju.') diff --git a/express/views/pages/consultancy/admin/prepared.pug b/express/views/pages/consultancy/admin/prepared.pug index 4ce0fa1..7830b62 100644 --- a/express/views/pages/consultancy/admin/prepared.pug +++ b/express/views/pages/consultancy/admin/prepared.pug @@ -2,5 +2,5 @@ extends /pages/consultancy/admin/consultancy-admin-inheritable-module block config - const pageTypeObject = { prepared: true } - - const subHeading = 'Pripravljeno' - - const description = 'Seznam urejenih vprašanj, ki čakajo na potrditev dokončne objave.' + - const subHeading = t('Pripravljeno') + - const description = t('Seznam urejenih vprašanj, ki čakajo na potrditev dokončne objave.') diff --git a/express/views/pages/consultancy/admin/published.pug b/express/views/pages/consultancy/admin/published.pug index d50dc4e..f859348 100644 --- a/express/views/pages/consultancy/admin/published.pug +++ b/express/views/pages/consultancy/admin/published.pug @@ -2,5 +2,5 @@ extends /pages/consultancy/admin/consultancy-admin-inheritable-module block config - const pageTypeObject = { published: true } - - const subHeading = 'Objavljeno' - - const description = 'Seznam objavljenih vprašanj.' + - const subHeading = t('Objavljeno') + - const description = t('Seznam objavljenih vprašanj.') diff --git a/express/views/pages/consultancy/admin/rejected.pug b/express/views/pages/consultancy/admin/rejected.pug index ca02ef0..9d10a69 100644 --- a/express/views/pages/consultancy/admin/rejected.pug +++ b/express/views/pages/consultancy/admin/rejected.pug @@ -2,5 +2,5 @@ extends /pages/consultancy/admin/consultancy-admin-inheritable-module block config - const pageTypeObject = { rejected: true } - - const subHeading = 'Zavrnjeno' - - const description = 'Seznam vprašanj, ki so jih moderatorji zavrnili in jih je treba dodeliti nekomu drugemu.' + - const subHeading = t('Zavrnjeno') + - const description = t('Seznam vprašanj, ki so jih moderatorji zavrnili in jih je treba dodeliti nekomu drugemu.') diff --git a/express/views/pages/consultancy/admin/statistics.pug b/express/views/pages/consultancy/admin/statistics.pug index 1d573a1..ed178ca 100644 --- a/express/views/pages/consultancy/admin/statistics.pug +++ b/express/views/pages/consultancy/admin/statistics.pug @@ -23,7 +23,7 @@ block body +main-navigation(false) block config - const pageTypeObject = { stats: true } - - const subHeading = 'Statistika' + - const subHeading = t('Statistika') +sideMenu(pageTypeObject) @@ -36,7 +36,7 @@ block body if specificUserRights include /utilities/consultancy-admin-panel-header //- const c = { h1: 'Terminološko svetovanje', description: 'Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj, pa strokovnjaki čutijo potrebo po sistemskem poenotenju oz. izbiri najprimernejše rešitve.', buttons: [{ type: 'link', content: 'Zastavi novo vprašanje', url: '/svetovanje/vprasanje/novo' }] } - - const c = { specificRights: specificUserRights, exportButtonPresent: false, h1: 'Statistika', description: 'Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj.', buttons: [{ type: 'link', content: 'Zastavi novo vprašanje', url: '/svetovanje/vprasanje/novo' }] } + - const c = { specificRights: specificUserRights, exportButtonPresent: false, h1: t('Statistika'), description: 'STATISTIKA_PLACEHOLDER', buttons: [{ type: 'link', content: t('Zastavi novo vprašanje'), url: '/svetovanje/vprasanje/novo' }] } +mainHeader(c) .content-hold-prerequisites.ps-4.pe-4 #offset-main.ps-2.pe-2 @@ -50,7 +50,7 @@ block body +mainHeader .content-hold-prerequisites.ps-4.pe-4 #offset-main.ps-2.pe-2 - p Nimate ustreznih pravic + p #{ t('Nimate ustreznih pravic') } //- include ../../../utilities/modal-alert-mixin //- +alertModal("deleteModal","Izbriši" ,"Prekliči" , "del-btn", "cancel-btn") diff --git a/express/views/pages/consultancy/admin/users.pug b/express/views/pages/consultancy/admin/users.pug index c7c535a..95c2974 100644 --- a/express/views/pages/consultancy/admin/users.pug +++ b/express/views/pages/consultancy/admin/users.pug @@ -24,7 +24,7 @@ block body +main-navigation(false) block config - const pageTypeObject = { users: true } - - const subHeading = 'Uporabniki' + - const subHeading = t('Uporabniki') +sideMenu(pageTypeObject) @@ -40,9 +40,9 @@ block body .header-container-left-side.d-flex.col-sm-10 #chevrons-left.d-flex.w-100 .header-container-divider-left - h2#site-header-title Uporabniki - h1#site-heading Seznam svetovalcev - span#text-description.page-description.pe-0 Na tem mestu lahko dodajate svetovalce, ki so registrirani uporabniki terminološkega portala. Če boste pripisali področje, boste lahko svetovalcu dodeljevali samo vprašanja, ki sodijo na izbrano področje, vsem drugim pa vsa. + h2#site-header-title #{ t('Uporabniki') } + h1#site-heading #{ t('Seznam svetovalcev') } + span#text-description.page-description.pe-0 #{ t('Na tem mestu lahko dodajate svetovalce, ki so registrirani uporabniki terminološkega portala. Če boste pripisali področje, boste lahko svetovalcu dodeljevali samo vprašanja, ki sodijo na izbrano področje, vsem drugim pa vsa.') }' .header-container-divider-right.d-flex.justify-content-end.col-sm-2 //- a.btn.btn-primary.header-btn(disabled) Dodaj @@ -60,8 +60,8 @@ block body table#all-areas-table.table.areas-table.table-responsive thead tr - th(scope="col") Ime - th(scope="col") Področja + th(scope="col") #{ t('Ime') } + th(scope="col") #{ t('Področja') } th(scope="col") tbody each user in users @@ -90,14 +90,14 @@ block body .row.mb-5 .col-sm-3 .subject-name - label.input-name-txt Ime + label.input-name-txt #{ t('Ime') } input#username.form-control(type="text") .col-sm-3 .subject-name - label.input-name-txt Področja + label.input-name-txt #{ t('Področja') } input#domains.form-control(type="text") .col.d-flex.align-items-end.mt-2 - button#add-area.btn.btn-primary(type="submit") Dodaj + button#add-area.btn.btn-primary(type="submit") #{ t('Dodaj') } + include /common/footer include /utilities/modal-alert - include /common/footer diff --git a/express/views/pages/consultancy/ask.pug b/express/views/pages/consultancy/ask.pug index 2980cfa..0a60670 100644 --- a/express/views/pages/consultancy/ask.pug +++ b/express/views/pages/consultancy/ask.pug @@ -3,6 +3,7 @@ extends /layout block pageSpecificScipts //- the only reason to include the bottom script is to calibrate the padding of the content script(nonce=cspNonce src="/javascripts/consultancy.js") + script(nonce=cspNonce src="/javascripts/consultancy-search-mechanism.js") script(nonce=cspNonce src="/javascripts/utils.js") block body @@ -16,8 +17,8 @@ block body .content-hold-prerequisites.ps-4.pe-4 #offset-main.ps-2.pe-2 if user - .consultancy-container.mb-5 - .back-section.mt-4.mb-3.d-flex + .consultancy-container + //- .back-section.mt-4.mb-3.d-flex a.bg-transparent.border-0.consultancy-back-button.d-flex.align-items-center( href="/svetovanje" ) @@ -31,10 +32,10 @@ block body autocomplete="off" novalidate ) - h5.navigation-text-color.text-1p5rem.fw-light Zastavi terminološko vprašanje + h5.navigation-text-color.text-1p5rem.fw-light #{ t('Zastavi terminološko vprašanje') } #name-field.mt-4 .subject-name - label.smaller-gray-uppercase(for="name") IME IN PRIIMEK + label.smaller-gray-uppercase(for="name") #{ t('IME IN PRIIMEK') } .row .col-sm-6 input#name.name-input.d-inline.form-control( @@ -44,11 +45,11 @@ block body disabled ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Ime in priimek trenutno prijavljenega uporabnika. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 #{ t('Ime in priimek trenutno prijavljenega uporabnika.') } #email-field.mt-4 .subject-name - label.smaller-gray-uppercase(for="email") ELEKTRONSKI NASLOV + label.smaller-gray-uppercase(for="email") #{ t('ELEKTRONSKI NASLOV') } .row .col-sm-6 input#email.name-input.d-inline.form-control( @@ -58,10 +59,10 @@ block body disabled ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Elektronski naslov trenutno prijavljenega uporabnika. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 #{ t('Elektronski naslov trenutno prijavljenega uporabnika.') } #institution-field.mt-4 .subject-name - label.smaller-gray-uppercase(for="institution") INSTITUCIJA + label.smaller-gray-uppercase(for="institution") #{ t('INSTITUCIJA') } .row .col-sm-6 textarea#institution.explanation-field.form-control.noScrollbar.resizeNone.institution-input( @@ -69,11 +70,11 @@ block body rows="1" ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Institucija, kjer trenutno prijavljeni uporabnik deluje. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 #{ t('Institucija, kjer trenutno prijavljeni uporabnik deluje.') } #text-editor.mt-4 .subject-name - span.smaller-gray-uppercase OPIS TERMINOLOŠKEGA PROBLEMA * + span.smaller-gray-uppercase #{ t('OPIS TERMINOLOŠKEGA PROBLEMA') } * .row .col-sm-6 textarea#description.explanation-field.form-control.ms-0.noScrollbar.resizeNone.r-3.form-check-label( @@ -81,13 +82,13 @@ block body rows="2" required ) - .invalid-feedback Niste vpisali opisa terminološkega problema. + .invalid-feedback #{ t('Niste vpisali opisa terminološkega problema.') } .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Čim bolj natančno opišite terminološki problem, zlasti opišite vsebino pojma. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 #{ t('Čim bolj natančno opišite terminološki problem, zlasti opišite vsebino pojma.') } .cerif-area.mt-4 .subject-name - label.smaller-gray-uppercase(for="select-cerif") PODROČJE + label.smaller-gray-uppercase(for="select-cerif") #{ t('PODROČJE') } .row #cerif.col-sm-6 select#select-cerif.select-cerif.name-input.d-inline.form-control( @@ -96,13 +97,13 @@ block body option(value=-1) each domain in allPrimaryDomains option(value=domain.id)= domain.nameSl - #invalid-section.invalid-feedback-selection.hidden-section.mt-3 Nimate izbranega področja CERIF. + #invalid-section.invalid-feedback-selection.hidden-section.mt-3 #{ t('Nimate izbranega področja CERIF.') } .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Področje, v katero sodi opisani terminološki problem. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 #{ t('Področje, v katero sodi opisani terminološki problem.') } #text-editor.mt-4 .subject-name - span.smaller-gray-uppercase OBSTOJEČE POIMENOVALNE REŠITVE + span.smaller-gray-uppercase #{ t('OBSTOJEČE POIMENOVALNE REŠITVE') } .row .col-sm-6 textarea#existing-solutions.explanation-field.form-control.noScrollbar.r-3.resizeNone( @@ -110,11 +111,11 @@ block body rows="2" ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Morebitne že obstoječe poimenovalne rešitve, če obstajajo. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 #{ t('Morebitne že obstoječe poimenovalne rešitve, če obstajajo.') } #text-editor.mt-4 .subject-name - span.smaller-gray-uppercase PRIMERI RABE V BESEDILIH + span.smaller-gray-uppercase #{ t('PRIMERI RABE V BESEDILIH') } .row .col-sm-6 textarea#examples-of-use.explanation-field.form-control.noScrollbar.r-3.resizeNone( @@ -122,11 +123,15 @@ block body rows="2" ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Morebitni primeri rabe termina v besedilih ali povezave do njih, če obstajajo. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 #{ t('Morebitni primeri rabe termina v besedilih ali povezave do njih, če obstajajo.') } - .send-section.mt-5 - button.btn.btn-primary.send-cons-btn(type="submit") Pošlji + .send-section.mt-5.flex-row.d-flex + button#cancel-cons-btn.btn.btn-secondary(type="button") #{ t('Prekliči') } + .d-flex.flex-grow-1.justify-content-end + button.btn.btn-primary.send-cons-btn(type="submit") #{ t('Pošlji') } + include /utilities/modal-response + +responseModal("begin-response", t("Razumem"), "understand-btn", isOwnConsultancyEnabled?t("Hvala za poslano vprašanje, ki je bilo posredovano svetovalcem terminološkega portala. Odgovor boste prejeli na e-naslov, ki ste ga navedli ob registraciji."):t("Hvala za poslano vprašanje, ki je bilo posredovano v Terminološko svetovalnico ZRC SAZU. Odgovor boste prejeli na e-naslov, ki ste ga navedli ob registraciji.")) else - p Za zastavljanje terminoloških vprašanj morate biti prijavljeni. + p #{ t('Za zastavljanje terminoloških vprašanj morate biti prijavljeni.') } include /common/footer diff --git a/express/views/pages/consultancy/index.pug b/express/views/pages/consultancy/index.pug index 2dc93c1..47c08c8 100644 --- a/express/views/pages/consultancy/index.pug +++ b/express/views/pages/consultancy/index.pug @@ -4,13 +4,16 @@ block pageSpecificScipts script(nonce=cspNonce src="/javascripts/dictionaries.js") script(nonce=cspNonce src="/javascripts/consultancy.js") script(nonce=cspNonce src="/javascripts/consultancy-search-mechanism.js") + script(nonce=cspNonce src="/javascripts/query-focus-handler.js") block body block preDefinitions - const resultAmountDisplay = false - - const specificUserRights = user && (user.hasRole('consultant') || user.hasRole('consultancy admin')) - + - const specificUserRights = user && isOwnConsultancyEnabled && (user.hasRole('consultant') || user.hasRole('consultancy admin')) + - + let descriptionText = t('Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj.') + descriptionText = isOwnConsultancyEnabled?descriptionText:descriptionText+t(" Terminološke odgovore pripravljajo sodelavci Terminološke sekcije Inštituta za slovenski jezik Frana Ramovša ZRC SAZU (in so objavljeni tudi na spletišču Terminologišče), ki pri delu upoštevajo osnovna terminološka načela.") //- include /common/side-menu-fake //- +sideNavigationFake @@ -19,14 +22,13 @@ block body +main-navigation(false) .consultancy-padding-main.p-squeeze-lg include /utilities/consultancy-main-panel-header - //- const c = { h1: 'Terminološko svetovanje', description: 'Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj, pa strokovnjaki čutijo potrebo po sistemskem poenotenju oz. izbiri najprimernejše rešitve.', buttons: [{ type: 'link', content: 'Zastavi novo vprašanje', url: '/svetovanje/vprasanje/novo' }] } block headerConfig - - const c = { specificRights: specificUserRights, exportButtonPresent: false, h1: 'Terminološko svetovanje', description: 'Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj.', buttons: [{ type: 'link', content: 'Zastavi novo vprašanje', url: '/svetovanje/vprasanje/novo' }], show5MostRecent: true } + - const c = { specificRights: specificUserRights, exportButtonPresent: false, h1: t('Terminološko svetovanje'), description: descriptionText, buttons: [{ type: 'link', content: t('Zastavi novo vprašanje'), url: '/svetovanje/vprasanje/novo' }], show5MostRecent: true } +mainHeader(c) .content-hold-prerequisites.ps-0.pe-0 #offset-main include /components/consultancy/consultancy-item - .consultancy-container + #results-data.consultancy-container // To remove duplicates //- - const moderatorList = entryList.filter(a => a.isModerator) // All published questions have a moderator... each entry in entries diff --git a/express/views/pages/consultancy/item-details.pug b/express/views/pages/consultancy/item-details.pug index 62f74d1..8a3e1f3 100644 --- a/express/views/pages/consultancy/item-details.pug +++ b/express/views/pages/consultancy/item-details.pug @@ -4,29 +4,34 @@ block pageSpecificScipts script(nonce=cspNonce src="/javascripts/comments.js") //- script(nonce=cspNonce src="/javascripts/dictionaries.js") script(nonce=cspNonce src="/javascripts/consultancy.js") + script(nonce=cspNonce src="/javascripts/consultancy-search-mechanism.js") block body - - const specificUserRights = user && (user.hasRole('consultant') || user.hasRole('consultancy admin')) + - const specificUserRights = user && isOwnConsultancyEnabled && (user.hasRole('consultant') || user.hasRole('consultancy admin')) section#fixed-top-section include /common/main-navigation +main-navigation(false) + + - + let descriptionText = t('Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj.') + descriptionText = isOwnConsultancyEnabled?descriptionText:descriptionText+t(" Terminološke odgovore pripravljajo sodelavci Terminološke sekcije Inštituta za slovenski jezik Frana Ramovša ZRC SAZU (in so objavljeni tudi na spletišču Terminologišče), ki pri delu upoštevajo osnovna terminološka načela.") .consultancy-padding-main.p-squeeze-lg.mx-xxl-auto include /utilities/consultancy-main-panel-header //- const c = { h1: 'Terminološko svetovanje', description: 'Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj, pa strokovnjaki čutijo potrebo po sistemskem poenotenju oz. izbiri najprimernejše rešitve.', buttons: [{ type: 'link', content: 'Zastavi novo vprašanje', url: '/svetovanje/vprasanje/novo' }] } - - const c = { specificRights: specificUserRights, exportButtonPresent: false, h1: 'Terminološko svetovanje', description: 'Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj.', buttons: [{ type: 'link', content: 'Zastavi novo vprašanje', url: '/svetovanje/vprasanje/novo' }] } + - const c = { specificRights: specificUserRights, exportButtonPresent: false, h1: t('Terminološko svetovanje'), description: descriptionText, buttons: [{ type: 'link', content: t('Zastavi novo vprašanje'), url: '/svetovanje/vprasanje/novo' }] } +mainHeader(c, false) .content-hold-prerequisites.px-0 #offset-main - .back-section.mt-4.mb-2 + .back-section.mt-2.mb-2 a.bg-transparent.border-0.consultancy-back-button(href="/svetovanje") img.float-start(src="/images/chevrons-left.svg") .text-header-description-gray.ps-3.float-start - b Nazaj + b #{ t('Nazaj') } .consultancy-container.mb-2 .consultancy-item-detailed .row .col-6.d-flex.justify-content-start - span Vprašanje poslano #{ entry.timeCreated } + span Vprašanje poslano: #{ entry.timeCreated } //- .col-6.d-flex.justify-content-end button.me-3.bg-transparent.no-border img.i1p5rx1p5r(src="/images/copy.svg") @@ -39,10 +44,10 @@ block body .col h4.mt-3.navigation-text-color #{ entry.title } - .mb-3!= `Opis terminološkega problema: ${entry.question}` - .mb-3!= `Odgovor: ${entry.answer}` - if entry.domain - .mb-3!= `Področje: ${entry.domain}` + .mb-3!= `${t('Opis terminološkega problema:')} ${entry.question}` + .mb-3!= `${t('Odgovor')}: ${entry.answer}` + if entry.domain && isOwnConsultancyEnabled + .mb-3!= `${t('Področje')}: ${entry.domain}` div!= `${authorString}: ${entry.answerAuthors ? entry.answerAuthors.join(', ') : ''}` //- .mt-3 include /utilities/comments-with-pager diff --git a/express/views/pages/consultancy/search.pug b/express/views/pages/consultancy/search.pug index 3eb32d5..26bb70e 100644 --- a/express/views/pages/consultancy/search.pug +++ b/express/views/pages/consultancy/search.pug @@ -4,4 +4,4 @@ block preDefinitions - const resultAmountDisplay = true block headerConfig - - const c = { specificRights: specificUserRights, exportButtonPresent: true, h1: 'Terminološko svetovanje', description: 'Terminološka svetovalnica je namenjena širši strokovni javnosti, ki se sooča s konkretnimi poimenovalnimi problemi, pa naj gre za popolnoma nove pojme, ki jih je v slovenščini šele treba poimenovati, ali že znane pojme, za katere obstaja več poimenovanj.', buttons: [{ type: 'link', content: 'Zastavi novo vprašanje', url: '/svetovanje/vprasanje/novo' }], show5MostRecent: false } + - const c = { specificRights: specificUserRights, exportButtonPresent: true, h1: t('Terminološko svetovanje'), description: descriptionText, buttons: [{ type: 'link', content: t('Zastavi novo vprašanje'), url: '/svetovanje/vprasanje/novo' }], show5MostRecent: false } diff --git a/express/views/pages/demo-paginacija.pug b/express/views/pages/demo-paginacija.pug index 1399e9b..25a2450 100644 --- a/express/views/pages/demo-paginacija.pug +++ b/express/views/pages/demo-paginacija.pug @@ -1,3 +1,5 @@ +//- TODO MARK FOR DELETION + doctype html html(lang="sl") head @@ -9,11 +11,12 @@ html(lang="sl") h1 Demo konceptualna zasnova paginacije (muštr) //- Id krovnega elementa je v realnem primeru lahko poljuben, saj ga podaš funkciji initPagination (v demo-paginacija.js). + //- UPDATE: initPagination sedaj lahko sprejme tudi array idjev (tukaj: pagination-top in pagination-bottom) //- Interna struktura mora ostati enaka (imena elementov, njihove klase, imena, vrednosti). //- Lahko pa v gumbe dodaš slike itd., če je potrebno, ali dodatne klase na njih, če je potrebno zaradi oblikovanja. //- Oblikovanja tu namenoma nisem dodajal, da je demo čim enostavnejši. Oblikovati ga morata vidva oz. prvi, ki bo to delal. //- Če kaj ne bo jasno, me raje vprašaj. - #pagination + #pagination-top button.first-page(disabled) << button.previous-page(disabled) < form @@ -27,6 +30,16 @@ html(lang="sl") each result in results li Zanimiva vrednost: #[b= result.zanimivo]. Totalno nezanimivo: #{ result.nezanimivo1 } in #{ result.nezanimivo2 } + #pagination-bottom + button.first-page(disabled) << + button.previous-page(disabled) < + form + input(name="page" value="1") + span= ' / ' + span.pages-total= numberOfAllPages + button.next-page(disabled=numberOfAllPages === 1) > + button.last-page(disabled=numberOfAllPages === 1) >> + //- V realnem primeru: //- Ne uvoziš axiosa, ker je že v layout.pug //- Ne uvoziš skripte demo-paginacija.js, ker inicializacijsko kodo po njenem zgledu uporabiš v skripti, ki jo ta stan že uporablja. diff --git a/express/views/pages/dictionaries/advanced.pug b/express/views/pages/dictionaries/advanced.pug index c014836..8b7448c 100644 --- a/express/views/pages/dictionaries/advanced.pug +++ b/express/views/pages/dictionaries/advanced.pug @@ -12,7 +12,7 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Napredno', description: 'Napredno urejanje omogoča spreminjanje večjega števila podatkov v terminološkem slovarju.' } + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Napredno'), description: t('Napredno urejanje omogoča spreminjanje večjega števila podatkov v terminološkem slovarju.') } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-2 @@ -22,35 +22,41 @@ block body value=dictionary.id ) .file-type.mt-2 - .row + .row.g-0 .col-md-6.white-border-background.p-4.d-flex.align-items-center - span.info-text-for-button.me-auto Brisanje slovarskih sestavkov - button#adv-delete-all-entries.btn.btn-primary.ps-5.pe-5.advanced-blue-btn BRIŠI + span.info-text-for-button.me-auto= t('Brisanje slovarskih sestavkov') + button#adv-delete-all-entries.btn.btn-primary.ps-5.pe-5.advanced-blue-btn= t('BRIŠI') .col-sm.ms-xxl-3.ms-md-3.align-items-center.d-flex.ps-1 - span.name-info-txt Izbrišete lahko vse doslej obdelane slovarske sestavke in ohranite vse metapodatke o slovarju. Dejanja ni mogoče preklicati. - a.name-info-txt.ms-2(href="/pomoc") Več … + span.name-info-txt= t('Izbrišete lahko vse doslej obdelane slovarske sestavke in ohranite vse metapodatke o slovarju. Dejanja ni mogoče preklicati.') + a.name-info-txt.ms-2( + href="/pomoc#help-edit-dict" + target="_blank" + )= t('Več …') .file-type.mt-5 - .row + .row.g-0 .col-md-6.white-border-background.p-4.d-flex.align-items-center - span.info-text-for-button.me-auto Brisanje slovarja - button#adv-delete-dictionary.btn.btn-primary.ps-5.pe-5.advanced-blue-btn BRIŠI + span.info-text-for-button.me-auto= t('Brisanje slovarja') + button#adv-delete-dictionary.btn.btn-primary.ps-5.pe-5.advanced-blue-btn= t('BRIŠI') .col-sm.ms-xxl-3.ms-md-3.align-items-center.d-flex.ps-1 - span.name-info-txt Izbrišete lahko celoten slovar z vsemi metapodatki. Dejanja ni mogoče preklicati. - a.name-info-txt.ms-2(href="/pomoc") Več … + span.name-info-txt= t('Izbrišete lahko celoten slovar z vsemi metapodatki. Dejanja ni mogoče preklicati.') + a.name-info-txt.ms-2( + href="/pomoc#help-edit-dict" + target="_blank" + )= t('Več …') .file-type.mt-5 - .row + .row.g-0 .col-md-6.white-border-background.p-4.d-flex.align-items-center - span.info-text-for-button.me-auto Objava vseh slovarskih sestavkov - button#adv-publish-all-entries.btn.btn-primary.ps-5.pe-5.advanced-blue-btn OBJAVI + span.info-text-for-button.me-auto= t('Objava vseh slovarskih sestavkov') + button#adv-publish-all-entries.btn.btn-primary.ps-5.pe-5.advanced-blue-btn= t('OBJAVI') .col-sm.ms-xxl-3.ms-md-3.align-items-center.d-flex.ps-1 - span.name-info-txt Ko boste končali z urejanjem svojega terminološkega slovarja, lahko objavite vse slovarske sestavke, ki bodo postali vidni vsem uporabnikom. Vaše dejanje mora potrditi še administrator portala. + span.name-info-txt= t('Ko boste končali z urejanjem svojega terminološkega slovarja, lahko objavite vse slovarske sestavke, ki bodo postali vidni vsem uporabnikom. Vaše dejanje mora potrditi še administrator portala.') + include /common/footer include /utilities/modal-spinner include /utilities/modal-alert-mixin - +alertModal("delete-entries", "Izbriši", "Prekliči", "modal-use-btn", "cancel-btn", 'S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju. Metapodatki bodo ostali. Dejanja ni mogoče razveljaviti.') - +alertModal("delete-dictionary", "Izbriši", "Prekliči", "modal-use-btn", "cancel-btn", 'S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju in vse metapodatke. Dejanja ni mogoče razveljaviti.') - +alertModal("publish-entries", "Uporabi", "Prekliči", "modal-use-btn", "cancel-btn", 'Ali želite objaviti vsa gesla?') + +alertModal("delete-entries", t("Izbriši"), t("Prekliči"), "modal-use-btn", "cancel-btn", t('S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju. Metapodatki bodo ostali. Dejanja ni mogoče razveljaviti.')) + +alertModal("delete-dictionary", t("Izbriši"), t("Prekliči"), "modal-use-btn", "cancel-btn", t('S tem dejanjem boste izbrisali vse slovarske sestavke v slovarju in vse metapodatke. Dejanja ni mogoče razveljaviti.')) + +alertModal("publish-entries", t("Uporabi"), t("Prekliči"), "modal-use-btn", "cancel-btn", t('Ali želite objaviti vsa gesla?')) include /utilities/modal-response - +responseModal("delete-entries-res", "Razumem", "understand-btn", "Slovarski sestavki so bili izbrisani.") - +responseModal("delete-dict-res", "Razumem", "understand-btn", "Slovar je bil izbrisan.") - +responseModal("publish-entries-res", "Razumem", "understand-btn", "Slovarski sestavki so bili objavljeni.") - include /common/footer + +responseModal("delete-entries-res", t("Razumem"), "understand-btn", t("Slovarski sestavki so bili izbrisani.")) + +responseModal("delete-dict-res", t("Razumem"), "understand-btn", t("Slovar je bil izbrisan.")) + +responseModal("publish-entries-res", t("Razumem"), "understand-btn", t("Slovarski sestavki so bili objavljeni.")) diff --git a/express/views/pages/dictionaries/content.pug b/express/views/pages/dictionaries/content.pug index fae93b2..c19baa3 100644 --- a/express/views/pages/dictionaries/content.pug +++ b/express/views/pages/dictionaries/content.pug @@ -30,7 +30,7 @@ block body #content-data-section.mt-2 #no-entries-text.d-flex.justify-content-center .justify-content-center.mt-5 - p Ni slovarskih sestavkov za urejanje. + p= t('Ni slovarskih sestavkov za urejanje.') #entry-preview-section.d-none #classic-overview include /utilities/content-classic-overview @@ -42,6 +42,8 @@ block body include /utilities/comments-with-pager include /utilities/modal-alert include /utilities/modal-alert-mixin - +alertModal("delete-modal", "Izbriši", "Prekliči", "modal-del-btn", "cancel-btn", 'Ali želite izbrisati geslo?') + include /utilities/modal-response + +alertModal("delete-modal", t("Izbriši"), t("Prekliči"), "modal-del-btn", "cancel-btn", t('Ali želite izbrisati slovarski sestavek?')) + +responseModal("duplicate-modal", t("Razumem"), "understand-btn", t("Vsebina slovarskega sestavka je bila duplicirana.")) .mb-3 //- include /common/footer diff --git a/express/views/pages/dictionaries/description.pug b/express/views/pages/dictionaries/description.pug index 540bb3f..37fdac4 100644 --- a/express/views/pages/dictionaries/description.pug +++ b/express/views/pages/dictionaries/description.pug @@ -12,179 +12,11 @@ block body - const d = { activeLvl1: 'attributes', activeLvl2: 'description' } +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionary.nameSl, h1: 'Osnovni podatki', description: 'Izpolnite polja in na kratko opišite vsebino terminološkega slovarja.', helpLink: { linkHref: '/pomoc', linkText: 'Več ...' }, buttons: [{ type: 'disabled', content: 'Shrani', form: 'form-dictionary-description' }] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionary.nameSl, h1: t('Osnovni podatki'), description: t('Izpolnite polja in na kratko opišite vsebino terminološkega slovarja.'), helpLink: { linkHref: '/pomoc#help-edit-dict', linkText: t('Več ...') }, buttons: [{ type: 'disabled', content: t('Shrani'), form: 'form-dictionary-description' }] } +mainHeader(c) - .content-hold-prerequisites - #offset-main.main-container.mt-3 - form#form-dictionary-description.needs-validation( - method="post" - novalidate - ) - .title - .subject-name - label.input-name-txt(for="dictionary-title") NASLOV SLOVARJA * - .row - .col-sm-6 - input#dictionary-title.name-input.form-control.d-inline( - type="text" - name="nameSl" - required - maxlength="120" - value=dictionary.nameSl - ) - .invalid-feedback Niste vpisali naslova slovarja. - - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Celotni naslov slovarja, ki bo zabeležen v bibliografskih podatkih. - - .english-title.mt-4 - .subject-name - label.input-name-txt(for="dictionary-title-en") ANGLEŠKI NASLOV SLOVARJA * - .row - .col-sm-6.align-items-center - input#dictionary-title-en.name-input.form-control.d-inline( - type="text" - name="nameEn" - maxlength="120" - value=dictionary.nameEn ? dictionary.nameEn : '' - required - ) - .invalid-feedback Niste vpisali angleškega naslova slovarja. - - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Celotni naslov slovarja v angleščini. - - .short-title.mt-4 - .subject-name - label.input-name-txt(for="short-dictionary-title") SKRAJŠANI NASLOV SLOVARJA - .row - .col-sm-6 - input#short-dictionary-title.name-input.d-inline.form-control( - type="text" - name="nameSlShort" - maxlength="15" - value=dictionary.nameSlShort ? dictionary.nameSlShort : '' - ) - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesede nje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki. - - if dictionary.author - each ele, index in dictionary.author - .author.mt-4.added-field - .subject-name - label.input-name-txt(for="author") AVTOR SLOVARJA - .row - .col-sm-6 - .input-group - input( - class=index != 0 ? 'name-input d-inline form-control icon-trash' : 'name-input d-inline form-control' - type="text" - name="author" - maxlength="64" - value=ele ? ele : '' - ) - if (index!=0) - button.input-group-text.delete-author-btn(type="button") - img(src="/images/red-trash-icon.svg" alt="") - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime. - else - #first-author.author.mt-4 - .subject-name - label.input-name-txt(for="author") AVTOR SLOVARJA - .row - .col-sm-6 - input#author.name-input.d-inline.form-control( - type="text" - name="author" - maxlength="64" - ) - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime. - - #add-new-author.author.mt-4 - .subject-name - label.input-name-txt(for="input-new-author") NOV AVTOR SLOVARJA - .row - .col-sm-6 - button#input-new-author.form-control(type="button") - span.new-author-text-btn Nov avtor - - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Dodajte ime in priimek naslednjega avtorja slovarja.. - - .cerif-area.mt-4 - .subject-name - label.input-name-txt(for="select-cerif") PODROČJE * - .row - .col-sm-6 - select.name-input.d-inline.form-select( - name="domainPrimary" - required - ) - each domain in allPrimaryDomains - option( - value=domain.id - selected=domain.id === dictionary.domainPrimary - )= domain.nameSl - - #invalid-section.invalid-feedback-selection.hidden-section.mt-3 Nimate izbranega področja CERIF. - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Izberite področje svojega terminološkega slovarja na seznamu področij. - - .small-name-area.mt-4 - .subject-name - label.input-name-txt(for="domain-secondary") PODPODROČJE - .row - .col-sm-6 - select#domain-secondary.name-input.d-inline.form-control.without-addition( - name="domainSecondary" - multiple - ) - each domain in allSecondaryDomains - if associatedSecondaryDomains.some(associatedDomain => associatedDomain.id === domain.id) - option(value=domain.id selected)= domain.nameSl - else - option(value=domain.id)= domain.nameSl - - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Vpišite podpodročje glavnega področja, ki ste ga izbrali. Če podpodročja ni med naborom, izberite polje Novo podpodročje. - - #add-new-area.author.mt-4 - .subject-name - label.input-name-txt NOVO PODPODROČJE - .row - .col-sm-6 - button#input-new-area.form-control(type="button") - span.new-author-text-btn Novo podpodročje - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Vpišite novo podpodročje. Na seznamu podpodročij bo vidno takoj po potrditvi administratorja portala. - - #text-editor.mt-4 - .subject-name - span.input-name-txt OPIS SLOVARJA - .container-xxl.ps-0.ms-0 - textarea.summernote( - name="description" - value=dictionary.description - ) - if dictionary.description - p= dictionary.description - #issn-field.mt-4 - .subject-name - label.input-name-txt(for="issn") ISSN OZNAKA - .row - .col-sm-6 - input#issn.name-input.d-inline.form-control( - type="text" - name="issn" - maxlength="20" - value=dictionary.issn ? dictionary.issn : '' - ) - .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 ISSN oznaka. - a.ms-1.name-info-txt(href="/pomoc" target="_blank") Več ... - include /utilities/modal-alert + include /utilities/dictionary-description-mixin + +dictionary-description + include /utilities/modal-alert include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/dictionaries/dictionary-comments.pug b/express/views/pages/dictionaries/dictionary-comments.pug index 4df4c32..d4baee9 100644 --- a/express/views/pages/dictionaries/dictionary-comments.pug +++ b/express/views/pages/dictionaries/dictionary-comments.pug @@ -12,11 +12,12 @@ block body - const d = { activeLvl1: 'attributes', activeLvl2: 'comments' } +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Komentarji', description: 'Tu so zbrani vsi komentarji, povezani z vašim slovarjem.' } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Komentarji'), description: t('Tu so zbrani vsi komentarji, povezani z vašim slovarjem.') } +mainHeader(c) - .comments-content.col-12.col-md-12.py-md-3.bd-content.content-hold-prerequisites + .comments-content.col-12.col-md-12.pt-md-3.bd-content.content-hold-prerequisites #offset-main.main-container include /utilities/comments-with-pager - include /common/footer + include /common/footer diff --git a/express/views/pages/dictionaries/dictlist.pug b/express/views/pages/dictionaries/dictlist.pug index c687ae3..13dd268 100644 --- a/express/views/pages/dictionaries/dictlist.pug +++ b/express/views/pages/dictionaries/dictlist.pug @@ -4,6 +4,7 @@ block pageSpecificScipts script(nonce=cspNonce src="/javascripts/dictionaries.js") script(nonce=cspNonce src="/javascripts/pagination-extensions.js") script(nonce=cspNonce src="/javascripts/search-dictionary.js") + script(nonce=cspNonce src="/javascripts/query-focus-handler.js") block body section#fixed-top-section @@ -26,14 +27,14 @@ block body .header-container-left-side.d-flex .header-container-divider-left //- h2#site-header-title - h1#site-heading Seznam slovarjev - span#text-description.pe-xl-5.page-description Seznam vseh slovarjev, ki so na tem portalu na voljo uporabnikom. + h1#site-heading #{ t('Seznam slovarjev') } + span#text-description.pe-xl-5.page-description #{ t('Seznam vseh slovarjev, ki so na tem portalu na voljo uporabnikom.') } hr#header-row.mt-1.mb-0 //- - const c = {sideMenu:false, h2: 'Urejanje', h1: 'Nov slovar', description: 'Izpolnite polja in na kratko opišite vsebino terminološkega slovarja.', helpLink: { linkHref: '/pomoc', linkText: 'Več ...' }, buttons: [{ type: 'cancel', content: 'Prekliči', url:"." },{ type: 'button', content: 'Ustvari', form: 'form-dictionary-new' }] } //- +mainHeader(c) .content-hold-prerequisites.ps-3.pe-3 - #offset-main.container-fluid.mt-4.ps-sm-3.errc + #offset-main.container-fluid.mt-4.px-sm-3.errc .p-squeeze-lg .row .col-md-6 diff --git a/express/views/pages/dictionaries/domain-labels.pug b/express/views/pages/dictionaries/domain-labels.pug index 1e0c143..cb6bfb7 100644 --- a/express/views/pages/dictionaries/domain-labels.pug +++ b/express/views/pages/dictionaries/domain-labels.pug @@ -12,101 +12,8 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Področne oznake', description: 'Na tem mestu lahko določite področne oznake, če želite posamezne termine v svojem terminološkem slovarju razvrstiti še podrobneje.', helpLink: { linkHref: '/pomoc#help-content-domains', linkText: 'Več ...' }, buttons: [{ form: 'dictionary-domain-labels', type: 'disabled', content: 'Shrani' }] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Področne oznake'), description: t('Na tem mestu lahko določite področne oznake, če želite posamezne termine v svojem terminološkem slovarju razvrstiti še podrobneje.'), helpLink: { linkHref: '/pomoc#help-content-domains', linkText: t('Več ...') }, buttons: [{ form: 'dictionary-domain-labels', type: 'disabled', content: t('Shrani') }] } +mainHeader(c) - - .content-hold-prerequisites - #offset-main.main-container.mt-2 - if results.length - .d-flex.justify-content-between.mt-2 - .d-flex.flex-row.mb-4 - include /components/search-and-filter/inline-search - .d-flex - include /utilities/pager - +pager - - form#dictionary-domain-labels( - method="post" - action="/api/v1/dictionaries/update-domain-labels" - ) - input#subareas-dict-id( - type="hidden" - name="dictionaryId" - value=dictionary.id - ) - table#all-areas-table.table.areas-table.table-responsive - thead - tr - th.visible-th(scope="col") Vidno - th(scope="col") Področna oznaka - th(scope="col") - tbody#page-results - each result in results - tr - input(type="hidden" name="domainLabelId" value=result.id) - th(scope="row") - if result.isVisible - input.form-check.checkbox-table( - type="checkbox" - name="isVisible" - checked - disabled - ) - else - input.form-check.checkbox-table( - type="checkbox" - name="isVisible" - disabled - ) - td.tdata-area= result.name - td.buttons-group - .table-buttons - button.p-0.table-button-grp.me-3.edit-row-btn( - type="button" - ) - img(src="/images/u_edit-alt.svg" alt="") - button.p-0.table-button-grp.delete-row-btn( - type="button" - data-bs-target="#alert-modal" - data-bs-toggle="modal" - ) - img(src="/images/red-trash-icon.svg" alt="") - else - form#dictionary-domain-labels( - method="post" - action="/api/v1/dictionaries/update-domain-labels" - ) - input#subareas-dict-id( - type="hidden" - name="dictionaryId" - value=dictionary.id - ) - #subareas-info.d-flex.justify-content-center.mt-5 - p Niste vnesli področnih oznak. - #no-subareas-section.d-none - .d-flex.justify-content-between.mt-4 - .d-flex.flex-row.mb-4 - include /components/search-and-filter/inline-search - .d-flex - include /utilities/pager - +pager - - table#all-areas-table.table.areas-table.table-responsive - thead - tr - th.visible-th(scope="col") Vidno - th(scope="col") Področna oznaka - th(scope="col") - tbody - tr.hidden(hidden) - .row - .col-sm-3 - .subject-name - label.input-name-txt PODROČNA OZNAKA - input#subarea-input.form-control(type="text") - .col.d-flex.align-items-end.mt-2 - button#add-area.btn.btn-primary(type="submit" disabled) Dodaj - include /utilities/modal-alert - include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + include /utilities/dictionary-domain-labels-mixin + +dictionary-domain-labels diff --git a/express/views/pages/dictionaries/export.pug b/express/views/pages/dictionaries/export.pug index 9b41dd0..be6dbb5 100644 --- a/express/views/pages/dictionaries/export.pug +++ b/express/views/pages/dictionaries/export.pug @@ -2,6 +2,7 @@ extends /layout block pageSpecificScipts script(nonce=cspNonce src="/javascripts/dictionaries.js") + script(nonce=cspNonce src="/javascripts/dictionary-export.js") block body section#fixed-top-section @@ -12,241 +13,9 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Izvoz', description: 'Vse podatke, ki ste jih vnesli v svoj slovar, lahko izvozite na svoj računalnik ali drugo želeno mesto. Izbrati morate format, v katerem želite pridobiti podatke.', buttons: [] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Izvoz'), description: t('Terminološke slovarje lahko v celoti ali po izbranih kriterijih izvozite v različnih formatih in shranite na svojem računalniku.'), buttons: [] } +mainHeader(c) - .content-hold-prerequisites - #offset-main.main-container.mt-2 - .row.validity.align-items-center - .col-xxl-6.col-md-7 - span.header-table-wrapper Veljavnost - ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 - .options-width-box - li.form-check.radio-button.ms-0.ps-3 - input#allKeys.form-check-input( - type="radio" - name="flexRadioDefault" - checked - ) - label.radio-button-labels.form-check-label.ms-0.text-nowrap( - for="allKeys" - ) - | Vsi slovarski sestavki - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#validKeys.form-check-input( - type="radio" - name="flexRadioDefault" - ) - label.radio-button-labels.form-check-label.ms-0( - for="validKeys" - ) - | Veljavni - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#notValidKeys.form-check-input( - type="radio" - name="flexRadioDefault" - ) - label.radio-button-labels.form-check-label.ms-0( - for="notValidKeys" - ) - | Neveljavni - .col.mb-3.mb-md-0 - span.name-info-txt Izberite, katere slovarske sestavke želite izpisati glede na njihovo veljavnost. - .row.posted.align-items-center - .col-xxl-6.col-md-7 - span.header-table-wrapper Objavljeno - ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 - .options-width-box - li.form-check.radio-button.ms-0.ps-3 - input#allPostedKeys.form-check-input( - type="radio" - name="posted" - checked - ) - label.radio-button-labels.form-check-label.ms-0.text-nowrap( - for="allPostedKeys" - ) - | Vsi slovarski sestavki - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#onlyPosted.form-check-input(type="radio" name="posted") - label.radio-button-labels.form-check-label.ms-0( - for="onlyPosted" - ) - | Objavljeni - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#notPosted.form-check-input(type="radio" name="posted") - label.radio-button-labels.form-check-label.ms-0( - for="notPosted" - ) - | Neobjavljeni - .col.mb-3.mb-md-0 - span.name-info-txt Izberite, ali želite izpisati samo objavljene ali tudi neobjavljene slovarske sestavke. - .row.phases.align-items-center - .col-xxl-6.col-md-7 - span.header-table-wrapper Faze urejanja - ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 - .options-width-box - li.form-check.radio-button.ms-0.ps-3 - input#allEditedKeys.form-check-input( - type="radio" - name="edited" - checked - ) - label.radio-button-labels.form-check-label.ms-0.text-nowrap( - for="allEditedKeys" - ) - | Vsi slovarski sestavki - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#editedKeys.form-check-input(type="radio" name="edited") - label.radio-button-labels.form-check-label.ms-0( - for="editedKeys" - ) - | Urejeni - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#inEditing.form-check-input(type="radio" name="edited") - label.radio-button-labels.form-check-label.ms-0( - for="inEditing" - ) - | V urejanju - .col.mb-3.mb-md-0 - span.name-info-txt Izberite, ali želite izpisati vse slovarske sestavke ali samo tiste, ki so v določeni fazi urejanja. - .row.proffesional-check.align-items-center - .col-xxl-6.col-md-7 - span.header-table-wrapper Strokovni pregled - ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 - .options-width-box - li.form-check.radio-button.ms-0.ps-3 - input#allChecked.form-check-input( - type="radio" - name="professionallyChecked" - checked - ) - label.radio-button-labels.form-check-label.ms-0( - for="allChecked" - ) - | Vsi slovarski sestavki - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#onlyProffesionallyChecked.form-check-input( - type="radio" - name="professionallyChecked" - ) - label.radio-button-labels.form-check-label.ms-0( - for="onlyProffesionallyChecked" - ) - | Pregledani - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#notProffesionallyChecked.form-check-input( - type="radio" - name="professionallyChecked" - ) - label.radio-button-labels.form-check-label.ms-0( - for="notProffesionallyChecked" - ) - | Nepregledani - .col.mb-3.mb-md-0 - span.name-info-txt Izberite, ali želite izpisati samo strokovno pregledane slovarske sestavke. - .row.terminology-check.align-items-center - .col-xxl-6.col-md-7 - span.header-table-wrapper Jezikovni pregled - ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 - .options-width-box - li.form-check.radio-button.ms-0.ps-3 - input#allGramaticallyChecked.form-check-input( - type="radio" - name="gramaticallyChecked" - checked - ) - label.radio-button-labels.form-check-label.ms-0( - for="allGramaticallyChecked" - ) - | Vsi slovarski sestavki - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#onlyGramaticallyChecked.form-check-input( - type="radio" - name="gramaticallyChecked" - ) - label.radio-button-labels.form-check-label.ms-0( - for="onlyGramaticallyChecked" - ) - | Pregledani - .options-another-box - li.form-check.radio-button.ms-0.ps-3 - input#notGramaticallyChecked.form-check-input( - type="radio" - name="gramaticallyChecked" - ) - label.radio-button-labels.form-check-label.ms-0( - for="notGramaticallyChecked" - ) - | Nepregledani - .col.mb-3.mb-md-0 - span.name-info-txt Izberite, ali želite izpisati samo jezikovno pregledane slovarske sestavke. - .file-type - span.new-user-info FORMAT ZAPISA - .row.align-items-center - .col-xxl-6.col-md-7 - .form-check.radio-button.ms-2.ps-3 - input#file-format-xml.form-check-input( - type="radio" - name="importFileFormat" - value="xml" - checked - ) - label.form-check-label.ms-0(for="file-format-xml") - | XML - .form-check.radio-button.ms-3 - input#file-format-csv.form-check-input( - type="radio" - name="importFileFormat" - value="csv" - ) - label.form-check-label.ms-0(for="file-format-csv") - | CSV - .form-check.radio-button.ms-3 - input#file-format-tsv.form-check-input( - type="radio" - name="importFileFormat" - value="tsv" - ) - label.form-check-label.ms-0(for="file-format-tsv") - | TSV - .form-check.radio-button.ms-3 - input#file-format-txt.form-check-input( - type="radio" - name="importFileFormat" - value="txt" - ) - label.form-check-label.ms-0(for="file-format-txt") - | TXT - .col.mb-3.mb-md-0 - span.name-info-txt Izberite format izpisa vašega slovarja. - button.btn.btn-primary.mt-4 IZVOZI - .latest-exports.mt-4 - .d-flex.w-100.justify-content-between - .d-flex - span.info-text-for-button Zadnji izvozi - .d-flex - include /utilities/pager - +pager(1,2,3,1,3,true) - .d-block.w-100 - include /utilities/table-mixin - - - const headerRow = ['Datum', 'Vrsta', 'Št. gesel', 'Status'] - const dataRows = [ - ['12.12.2012', 'Oznaka', 'Št. gesel', 'Oznaka', ""], - ['12.12.2012', 'Oznaka', 'Št. gesel', 'Oznaka', {button: {content:"Shrani"}}], - ['12.12.2012', 'Oznaka', 'Št. gesel', 'Oznaka', {button: {content:"Shrani"}}], - ['12.12.2012', 'Oznaka', 'Št. gesel', 'Oznaka', {button: {content:"Shrani"}}], - ['12.12.2012', 'Oznaka', 'Št. gesel', 'Oznaka', {button: {content:"Shrani"}}] - ] - +tableHeader(headerRow, dataRows) - - include /common/footer + include /utilities/dictionary-export-content-mixin + +dictionary-export-content(dictionary.id) + include /common/footer diff --git a/express/views/pages/dictionaries/extraction-import.pug b/express/views/pages/dictionaries/extraction-import.pug index 70ec0be..c4daf71 100644 --- a/express/views/pages/dictionaries/extraction-import.pug +++ b/express/views/pages/dictionaries/extraction-import.pug @@ -13,54 +13,8 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Uvoz iz luščilnika', description: 'Rezultat luščenja so terminološki kandidati. Z uvozom rezultatov posameznega luščenja lahko dopolnite geslovnik terminološkega slovarja, ki ga urejate.', buttons: [] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Uvoz iz luščilnika'), description: t('Rezultat luščenja so terminološki kandidati. Z uvozom rezultatov posameznega luščenja lahko dopolnite geslovnik terminološkega slovarja, ki ga urejate.'), buttons: [] } +mainHeader(c) - .content-hold-prerequisites - #offset-main.main-container.mt-3 - .small-name-area - .subject-name - span.input-name-txt IME LUŠČENJA - .row - .col-lg-6 - select#select-extraction-name.name-input.d-inline.form-control - option(selected value="" disabled hidden)= 'Izberite luščenje' - each el in extractions - option(value=el.id)= el.name - .col-sm.d-flex.align-items-center - span.name-info-txt.ms-lg-3.mt-2.mt-lg-0 Izberite enega od rezultatov luščenja s seznama. - .list-terminology-candidates.mt-4.me-2 - .d-flex.w-100.justify-content-between - .d-flex.align-items-center - span.info-text-for-button Seznam terminoloških kandidatov - .d-flex - include /utilities/pager - +pager - .table-responsive.mt-2.me-2 - table.styled-table - thead - tr - th= '#' - th= 'KANONIČNA OBLIKA' - th= 'RANKING' - th= 'POGOSTOST OBJAVLJANJA' - tbody#page-results - .row.mt-4 - .me-0.pe-0.d-flex.align-items-center - span.radio-button-labels Uvozi termine od številke - input.without-arrows.form-control.terminology-input.ms-1( - type="number" - maxlength="5" - name="from" - min="0" - ) - span.radio-button-labels.ms-1 do številke - input.without-arrows.form-control.terminology-input.ms-1( - type="number" - maxlength="5" - name="to" - min="0" - ) - - button.btn.btn-primary.mt-4(disabled) UVOZI - - include /common/footer + include /utilities/dictionary-extraction-import-mixin + +dictionary-extraction-import diff --git a/express/views/pages/dictionaries/import.pug b/express/views/pages/dictionaries/import.pug index 8b8a053..c0aabee 100644 --- a/express/views/pages/dictionaries/import.pug +++ b/express/views/pages/dictionaries/import.pug @@ -1,5 +1,4 @@ extends /layout - block pageSpecificScipts script(nonce=cspNonce src="/javascripts/dictionaries.js") @@ -12,143 +11,8 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionaryName, h1: 'Uvoz iz datoteke', description: 'Če imate pripravljen slovar v enem od formatov, ki so navedeni spodaj, lahko svoje podatke uvozite.', buttons: [] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionaryName, h1: t('Uvoz iz datoteke'), description: t('Če imate pripravljen slovar v enem od formatov, ki so navedeni spodaj, lahko svoje podatke uvozite.'), buttons: [] } +mainHeader(c) - .content-hold-prerequisites - #offset-main.main-container - .import-container.mt-3.ms-1 - form#file-import-form(method="post" enctype="multipart/form-data") - .file-type - span.new-user-info DATOTEKA - .row - .col-md-6.ms-2.white-border-background.p-4.d-flex.align-items-center.justify-content-between - .align-items-center.d-flex - span#chosen-file.info-text-for-button Izberi datoteko - div - label(for="upload") - button#button-import.btn.btn-primary(type="button") IZBERI - input#upload( - type="file" - name="dictionaryImportFile" - accept=".xml" - ) - - .col-md.d-flex.align-items-center - span.name-info-txt Izberite slovarske podatke, ki ste jih shranili na svojem računalniku. - .file-type.mt-4 - span.new-user-info NAČIN UVOZA - .row - .col-sm-6.ms-2 - .checkbox - input#flexCheckDefault.form-check-input( - type="checkbox" - name="deleteExistingEntries" - ) - label.form-check-label(for="flexCheckDefault") - span.user-email Izbriši obstoječe slovarske sestavke. - .col - span.name-info-txt Če izberete to možnost, se bodo vsi doslejšnji slovarski sestavki ob uvozu nove datoteke izbrisali. - .file-type.mt-4 - span.new-user-info FAZA UREJANJA - #file-type-selection.file-types - .row - .col-sm-6.ms-2 - .form-check.radio-button.ms-0.ps-3 - input#in-edit-radio.form-check-input( - type="radio" - name="entryStatus" - value="inEdit" - checked - ) - label.form-check-label.ms-0(for="in-edit-radio") - | V urejanju - #complete-radio.form-check.radio-button.ms-3 - input#complete-radio.form-check-input( - type="radio" - name="entryStatus" - value="complete" - ) - label.form-check-label.ms-0(for="complete-radio") - | Urejeno - .col - span.name-info-txt Z izbiro te možnosti boste pobrisali samo slovarske sestavke, ki so v določeni fazi urejanja. - .file-type.mt-4 - span.new-user-info FORMAT ZAPISA - #file-type-selection.file-types - .row - .col-sm-6.ms-2.text-nowrap - .form-check.radio-button.ms-0.ps-3 - input#file-format-xml.form-check-input( - type="radio" - name="importFileFormat" - value="xml" - checked - ) - label.form-check-label.ms-0(for="file-format-xml") - | XML - .form-check.radio-button.ms-2 - input#file-format-csv.form-check-input( - type="radio" - name="importFileFormat" - value="csv" - ) - label.form-check-label.ms-0(for="file-format-csv") - | CSV - .form-check.radio-button.ms-2 - input#file-format-tsv.form-check-input( - type="radio" - name="importFileFormat" - value="tsv" - ) - label.form-check-label.ms-0(for="file-format-tsv") - | TSV - .form-check.radio-button.ms-2 - input#file-format-txt.form-check-input( - type="radio" - name="importFileFormat" - value="txt" - ) - label.form-check-label.ms-0(for="file-format-txt") - | TXT - .col - span.name-info-txt Izberite format datoteke, v kateri je slovar shranjen na vašem računalniku. - .file-type.mt-4 - button.btn.btn-primary.mt-4 UVOZI - .latest-uploads.mt-4 - .d-flex.w-100.justify-content-between - .d-flex - span.info-text-for-button Zadnji uvozi test - .d-flex - include /utilities/pager - +pager - - - - const localeOptions = { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - } - if imports.length - .table-responsive - table.styled-table - thead - tr - th= 'DATUM' - th= 'VRSTA' - th= 'ŠT. GESEL' - th= 'STATUS' - th= '' - - each element in imports - tr - td= element.timeStarted.toLocaleDateString('sl-SL', localeOptions) - td= element.fileFormat - td= element.countValidEntries - td= element.status - else - p Ni še dodanih uvozov. - - include /common/footer + include /utilities/dictionary-import-mixin + +dictionary-import(dictionary.id) diff --git a/express/views/pages/dictionaries/list.pug b/express/views/pages/dictionaries/list.pug index 995142b..f616616 100644 --- a/express/views/pages/dictionaries/list.pug +++ b/express/views/pages/dictionaries/list.pug @@ -11,7 +11,7 @@ block body +sideNavigationFake include /utilities/dictionaries-main-panel-header if user - - const c = { sideMenu: false, h2: 'Urejanje', h1: 'Moji slovarji', description: 'Nabor vseh slovarjev, ki jih uporabnik lahko ureja kot glavni urednik ali pa ima dodeljeno pravico urejanja, pregledovanja, popravljanja ipd.', buttons: [{ type: 'link', content: 'Nov slovar', url: '/slovarji/nov' }] } + - const c = { sideMenu: false, h2: t('Urejanje'), h1: t('Moji slovarji'), description: t('Nabor vseh slovarjev, ki jih uporabnik lahko ureja kot glavni urednik ali pa ima dodeljeno pravico urejanja, pregledovanja, popravljanja ipd.'), buttons: [{ type: 'link', content: t('Nov slovar'), url: '/slovarji/nov' }] } +mainHeader(c) .content-hold-prerequisites #offset-main.ps-3.pe-3 @@ -26,56 +26,64 @@ block body div( class=index ? 'container-fluid dictionary-content mt-4 pb-2 pt-1' : 'container-fluid dictionary-content mt-3 pb-2 pt-1' ) + //- TODO I18n p.dictionary-title.mb-2.mt-2.ms-2= dictionary.nameSl hr.me-2.mt-0.ms-2.mb-4 .container-fluid .d-sm-flex .d-lg-flex .d-sm-flex.mt-2 - span.dictionary-attributes.me-3.text-nowrap Število terminov + span.dictionary-attributes.me-3.text-nowrap= t('Število terminov') span.dictionary-value.me-5= dictionary.countEntries .d-sm-flex.mt-2 - span.dictionary-attributes.me-3.ms-xl-5.text-nowrap Zadnja sprememba + span.dictionary-attributes.me-3.ms-xl-5.text-nowrap= t('Zadnja sprememba') span.dictionary-value.me-md-5.me-2.d-block= dictionary.timeModified.toLocaleDateString('sl-SL', localeOptions) .d-lg-flex .d-sm-flex.mt-2 - span.dictionary-attributes.me-3.ms-xl-5.d-inline Status - span.dictionary-value.me-5= dictionary.status === 'closed' ? 'zaprt' : dictionary.status === 'reviewed' ? 'v predogledu' : 'odprt' + span.dictionary-attributes.me-3.ms-xl-5.d-inline= t('Status') + span.dictionary-value.me-5= dictionary.status === 'closed' ? t('zaprt') : dictionary.status === 'reviewed' ? t('v predogledu') : t('odprt') .d-sm-flex.mt-2 - span.dictionary-attributes.me-3.ms-lg-0.ms-xl-5.d-inline Komentarji + span.dictionary-attributes.me-3.ms-lg-0.ms-xl-5.d-inline= t('Komentarji') span.dictionary-value.me-5= dictionary.countComments .container-fluid.d-md-flex.p-0.mt-3.justify-content-between .d-xl-flex.justify-content-between - a.btn.btn-secondary.mb-2.mt-2.w-100.w-sm-none( - href=`/slovarji/${dictionary.id}/podatki` - ) - img.d-inline(src="/images/vector.svg" alt="") - span.ms-1.text-nowrap Uredi lastnosti - a.btn.btn-secondary.mb-2.mt-2.ms-xl-4.w-100.w-sm-none( + if dictionary.isAdmin + a.btn.border-header.mb-2.mt-2.w-100.w-sm-none( + href=`/slovarji/${dictionary.id}/podatki` + ) + img.d-inline(src="/images/vector.svg" alt="") + span.ms-1.text-nowrap= t('Uredi lastnosti') + else + a.btn.border-header.mb-2.mt-2.w-100.w-sm-none.disabled( + href=`/slovarji/${dictionary.id}/podatki` + ) + img.d-inline(src="/images/vector.svg" alt="") + span.ms-1.text-nowrap= t('Uredi lastnosti') + a.btn.border-header.mb-2.mt-2.ms-xl-4.w-100.w-sm-none( href=`/slovarji/${dictionary.id}/vsebina` ) img.d-inline(src="/images/book-colorized.svg" alt="") - span.ms-1.text-nowrap Uredi vsebino + span.ms-1.text-nowrap= t('Uredi vsebino') .ms-md-4.ms-xl-auto.d-xl-flex.justify-content-between - a.btn.btn-secondary.mb-2.mt-2.me-4.w-100.w-sm-none( + a.btn.border-header.mb-2.mt-2.me-4.w-100.w-sm-none( href=`/slovarji/${dictionary.id}/uvoz/datoteka` ) img.d-inline(src="/images/fi_upload.svg" alt="") - span.ms-1 Uvoz - a.btn.btn-secondary.mb-2.mt-2.w-100.w-sm-none( + span.ms-1= t('Uvoz') + a.btn.border-header.mb-2.mt-2.w-100.w-sm-none( href=`/slovarji/${dictionary.id}/izvoz` ) img.d-inline(src="/images/fi_download.svg" alt="") - span.ms-1 Izvoz + span.ms-1= t('Izvoz') else .d-flex.justify-content-center.mt-5 - p Nimate slovarjev za urejanje. + p= t('Nimate slovarjev za urejanje.') else - - const c = { sideMenu: false, h2: 'Urejanje', h1: 'Moji slovarji', description: 'Nabor vseh slovarjev, ki jih uporabnik lahko ureja kot glavni urednik ali pa ima dodeljeno pravico urejanja, pregledovanja, popravljanja ipd.' } + - const c = { sideMenu: false, h2: t('Urejanje'), h1: t('Moji slovarji'), description: t('Nabor vseh slovarjev, ki jih uporabnik lahko ureja kot glavni urednik ali pa ima dodeljeno pravico urejanja, pregledovanja, popravljanja ipd.') } +mainHeader(c) .content-hold-prerequisites.pe-md-5.ps-4.pe-2 #offset-main.pe-3.ps-2 .d-flex.justify-content-center.mt-5 - p Za urejanje slovarjev morate biti prijavljeni. + p= t('Za urejanje slovarjev morate biti prijavljeni.') include /common/footer diff --git a/express/views/pages/dictionaries/new.pug b/express/views/pages/dictionaries/new.pug index b486a8e..62859a4 100644 --- a/express/views/pages/dictionaries/new.pug +++ b/express/views/pages/dictionaries/new.pug @@ -11,7 +11,7 @@ block body include /common/side-menu-fake +sideNavigationFake include /utilities/dictionaries-main-panel-header - - const c = {sideMenu:false, h2: 'Urejanje', h1: 'Nov slovar', description: 'Izpolnite polja in na kratko opišite vsebino terminološkega slovarja.', helpLink: { linkHref: '/pomoc#help-editor', linkText: 'Več ...' }, buttons: [{ type: 'cancel', content: 'Prekliči', classAtr:"btn btn-secondary header-btn-secondary link-back" },{ type: 'button', content: 'Ustvari', form: 'form-dictionary-new' }] } + - const c = {sideMenu:false, h2: t('Urejanje'), h1: t('Nov slovar'), description: t('Izpolnite polja in na kratko opišite vsebino terminološkega slovarja.'), helpLink: { linkHref: '/pomoc#help-editor', linkText: 'Več ...' }, buttons: [{ type: 'cancel', content: 'Prekliči', classAtr:"btn btn-secondary header-btn-secondary link-back" },{ type: 'button', content: t('Ustvari'), form: 'form-dictionary-new' }] } +mainHeader(c) .content-hold-prerequisites #offset-main.container-fluid.mt-4.ps-sm-3 diff --git a/express/views/pages/dictionaries/structure.pug b/express/views/pages/dictionaries/structure.pug index ac92e43..5ebd19c 100644 --- a/express/views/pages/dictionaries/structure.pug +++ b/express/views/pages/dictionaries/structure.pug @@ -12,12 +12,12 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionary.nameSl, h1: 'Struktura slovarskega sestavka', description: 'V tem razdelku lahko določite elemente slovarskega sestavka. Z izbiro elementov se vam prikazuje podoba slovarskega sestavka. Izbiro lahko tudi med urejanjem vsebine kadarkoli spremenite.', helpLink: { linkHref: '/pomoc', linkText: 'Več ...' }, buttons: [{ type: 'disabled', content: 'Shrani', form: 'form-dictionary-structure' }] } + - const c = { sideMenu: true, h2: dictionary.nameSl, h1: t('Struktura slovarskega sestavka'), description: t('V tem razdelku lahko določite elemente slovarskega sestavka. Z izbiro elementov se vam prikazuje podoba slovarskega sestavka. Izbiro lahko tudi med urejanjem vsebine kadarkoli spremenite.'), helpLink: { linkHref: '/pomoc#help-structure-dict', linkText: t('Več ...') }, buttons: [{ type: 'disabled', content: t('Shrani'), form: 'form-dictionary-structure' }] } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container form#form-dictionary-structure.needs-validation(method="post" novalidate) include /utilities/dictionary-structure-input + include /common/footer include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - include /common/footer + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/dictionaries/users.pug b/express/views/pages/dictionaries/users.pug index 24d5a15..a465164 100644 --- a/express/views/pages/dictionaries/users.pug +++ b/express/views/pages/dictionaries/users.pug @@ -12,12 +12,13 @@ block body +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h2: dictionary.nameSl, h1: 'Uporabniki', description: 'Registrirani uporabniki lahko dobijo različne vloge na portalu. Kot administrator jim lahko dodelite tudi vlogo skrbnika slovarjev in/ali skrbnika svetovalnice.', buttons: [{ type: 'disabled', content: 'Shrani', form: 'admin-dictionary-users' }] } + //- TODO I18n + - const c = { sideMenu: true, h2: dictionary.nameSl, h1: t('Uporabniki'), description: t('Registrirani uporabniki lahko dobijo različne vloge na portalu. Kot administrator jim lahko dodelite tudi vlogo skrbnika slovarjev in/ali skrbnika svetovalnice.'), buttons: [{ type: 'disabled', content: t('Shrani'), form: 'admin-dictionary-users' }] } +mainHeader(c) .content-hold-prerequisites #offset-main.main-container.mt-1 form#admin-dictionary-users.container-xxl.ms-0.ps-0(method="post") - span.users-subtitle-txt.ms-0 Faze urejanja + span.users-subtitle-txt.ms-0= t('Faze urejanja') .row.align-items-center.mt-2 .col-sm-6 .switch-forms-and-key-word.d-md-flex.mt-2 @@ -28,10 +29,10 @@ block body disabled checked ) - label.form-check-label(for="preposition") Predlog + label.form-check-label(for="preposition")= t('Predlog') .col-sm - span.name-info-txt V slovarju so samo terminološki kandidati. Slovarja ne morete objaviti. + span.name-info-txt= t('V slovarju so samo terminološki kandidati. Slovarja ne morete objaviti.') .row.align-items-center .col-sm-6 @@ -43,11 +44,11 @@ block body disabled checked ) - label.form-check-label(for="editing") V urejanju + label.form-check-label(for="editing")= t('V urejanju') .col-sm - span.name-info-txt Slovarski sestavek je v fazi, ko se dodajajo in oblikujejo elementi, ki ste jih izbrali v strukturi slovarskega sestavka. - a.ms-1.name-info-txt(href="/pomoc#help-users-dict" target="_blank") Več... + span.name-info-txt= t('Slovarski sestavek je v fazi, ko se dodajajo in oblikujejo elementi, ki ste jih izbrali v strukturi slovarskega sestavka.') + a.ms-1.name-info-txt(href="/pomoc#help-users-dict" target="_blank")= t('Več...') .row.align-items-center .col-sm-6 @@ -59,9 +60,9 @@ block body disabled checked ) - label.form-check-label(for="edited") Urejeno + label.form-check-label(for="edited")= t('Urejeno') .col-sm - span.name-info-txt Slovarski sestavek je pregledan in dokončan. + span.name-info-txt= t('Slovarski sestavek je pregledan in dokončan.') .row.align-items-center.mt-2 .col-sm-6 @@ -74,16 +75,16 @@ block body name="hasTerminologyReview" checked ) - label.form-check-label(for="terminology_review") Strokovno pregledano + label.form-check-label(for="terminology_review")= t('Strokovno pregledano') else .form-check.form-switch.d-flex input#terminology_review.form-check-input( type="checkbox" name="hasTerminologyReview" ) - label.form-check-label(for="terminology_review") Strokovno pregledano + label.form-check-label(for="terminology_review")= t('Strokovno pregledano') .col-sm - span.name-info-txt Slovar je pregledal področni strokovnjak. + span.name-info-txt= t('Slovar je pregledal področni strokovnjak.') .row.align-items-center.mt-2 .col-sm-6 @@ -96,35 +97,35 @@ block body name="hasLanguageReview" checked ) - label.form-check-label(for="language_review") Jezikovno pregledano + label.form-check-label(for="language_review")= t('Jezikovno pregledano') else .form-check.form-switch.d-flex input#language_review.form-check-input( type="checkbox" name="hasLanguageReview" ) - label.form-check-label(for="language_review") Jezikovno pregledano + label.form-check-label(for="language_review")= t('Jezikovno pregledano') .col-sm - span.name-info-txt Slovar je pregledal jezikoslovec. + span.name-info-txt= t('Slovar je pregledal jezikoslovec.') .container-xl.mt-4.ms-0.ps-0 - span.users-subtitle-txt.ms-0.mt-3 Uporabniške pravice/vloge + span.users-subtitle-txt.ms-0.mt-3= t('Uporabniške pravice/vloge') table#user-roles-table.table-users.table-borderless.align-middle.mt-3 thead tr th.col-2 - span.user-rights-column-title Uporabnik + span.user-rights-column-title= t('Uporabnik') th.col-2 - span.user-rights-column-title Administracija + span.user-rights-column-title= t('Administracija') th.col-2.text-center - span.user-rights-column-title Urejanje + span.user-rights-column-title= t('Urejanje') th.col-2 - span#txt-term-rev.user-rights-column-title Strokovni pregled + span#txt-term-rev.user-rights-column-title= t('Strokovni pregled') th.col-2 - span#txt-lang-rev.user-rights-column-title Jezikovni pregled + span#txt-lang-rev.user-rights-column-title= t('Jezikovni pregled') th.col-1.justify-content-center - span.hidden-text Izbriši polje + span.hidden-text= t('Izbriši polje') if userRights each el in userRights @@ -207,7 +208,7 @@ block body form#form-add-user.container-xl.new-user-input.mt-4.ps-0.ms-0( action="/api/v1/users/addUser" ) - span.new-user-info NOV UPORABNIK + span.new-user-info= t('NOV UPORABNIK') .row.d-flex.justify-content-lg-start.align-items-center.mt-2.ms-0.ps-0 .col-lg-6.ms-0.ps-0.me-2 input.name-input.d-inline.form-control.ms-0.ps-0( @@ -216,13 +217,13 @@ block body autocomplete="off" ) .col.ms-xl-4.ms-0.ps-0.d-flex.justify-content-start.mt-2.mt-lg-0 - button.btn.btn-primary Dodaj + button.btn.btn-primary= t('Dodaj') - const minEntriesNum = parseInt(minEntries) - const countEntries = parseInt(entriesCount) .container-xxl.ms-0.ps-0 - .row.align-items-center - .col-sm-6.mt-4 + .row.align-items-center.mt-4 + .col-sm-6 .switch-forms-and-key-word.d-md-flex .form-check.form-switch.d-flex if publishApproval === 'F' @@ -263,21 +264,20 @@ block body form="admin-dictionary-users" checked ) - label.form-check-label(for="publish-switch") Slovar odprt + label.form-check-label(for="publish-switch")= t('Slovar odprt') .col-sm - span.name-info-txt Slovar je pregledal jezikoslovec. + span.name-info-txt= t('Slovar je odprt.') if dictionary.status === 'published' && countEntries < minEntriesNum .col-sm-6 - span.mt-3.d-md-inline.d-block.normal-red-note Število slovarskih sestavkov je manjše od zahtevanega števila - povečajte število slovarskih sestavkov, sicer ga skrbnik slovarjev lahko zapre + span.mt-3.d-md-inline.d-block.normal-red-note= t('Število slovarskih sestavkov je manjše od zahtevanega števila - povečajte število slovarskih sestavkov, sicer ga skrbnik slovarjev lahko zapre') if publishApproval === 'T' && dictionary.status === 'reviewed' .col-sm-6 - span.mt-3.d-md-inline.d-block.normal-red-note Slovar je v odpiranju - čaka na potrditev skrbnika slovarjev + span.mt-3.d-md-inline.d-block.normal-red-note= t('Slovar je v odpiranju - čaka na potrditev skrbnika slovarjev') + include /common/footer include /utilities/modal-alert include /utilities/modal-response +responseModal include /utilities/modal-alert-mixin - +alertModal('unsaved-data', 'Shrani', 'Ne', 'modal-save-btn', 'modal-dont-save-btn', 'Imate neshranjene spremebe. Ali jih želite shraniti?') - - include /common/footer + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/pages/extraction/docs-edit.pug b/express/views/pages/extraction/docs-edit.pug index 1f7fab4..3f5272a 100644 --- a/express/views/pages/extraction/docs-edit.pug +++ b/express/views/pages/extraction/docs-edit.pug @@ -11,7 +11,7 @@ block body include /common/side-menu-fake +sideNavigationFake include /utilities/dictionaries-main-panel-header - - const c = { noSidebar: false, hrefurl: '.', h2: 'Luščenje iz lastnih besedil | dodaj opravilo ', h1: 'Uporabnikovi dokumenti', description: 'Uporabnik na tem mestu izdela specializirani korpus iz besedil, ki jih je zbral in shranil sam. Besedila naj bodo izbrana po načelih tvorjenja specializiranih korpusov. Za uspešno luščenje priporočamo najmanj 10 besedil. Vsa besedila naj bodo shranjena v besedilnem formatu (.txt). Luščenje podpira tudi formate .docx in .pdf, vendar so rezultati slabši.', buttons: [{ type: 'cancel', content: 'Nazaj', classAtr: 'btn btn-secondary header-btn-secondary link-back' }] } + - const c = { noSidebar: false, hrefurl: '.', h2: t('Luščenje iz lastnih besedil | dodaj opravilo '), h1: t('Uporabnikovi dokumenti'), description: t('Uporabnik na tem mestu izdela specializirani korpus iz besedil, ki jih je zbral in shranil sam. Besedila naj bodo izbrana po načelih tvorjenja specializiranih korpusov. Za uspešno luščenje priporočamo najmanj 10 besedil. Vsa besedila naj bodo shranjena v besedilnem formatu (.txt). Luščenje podpira tudi formate .docx in .pdf, vendar so rezultati slabši.'), buttons: [{ type: 'cancel', content: t('Naprej'), classAtr: 'btn border-header header-btn-secondary link-back' }] } +mainHeader(c) .content-hold-prerequisites.mt-2 #offset-main @@ -19,24 +19,24 @@ block body .table-responsive table.styled-table thead - tr + tr#thead th= 'ID' - th= 'IME DATOTEKE' - th= 'VELIKOST' - th= 'DATUM' + th= t('IME DATOTEKE') + th= t('VELIKOST') + th= t('DATUM') th= '' tbody#files-list each document, index in extractionDocuments tr - td= index + 1 - td.filename= document.filename - td= document.size - td= new Date(document.timeModified).toLocaleDateString('sl-SL') - td - button.p-0.delete-file.delete-btn-table(type="button") - img(src="/images/red-trash-icon.svg" alt="Izbriši") - span.ms-2 Briši - .row.ms-2.me-2.align-items-center.mt-4 + td.file-index= index + 1 + td.file-name= document.filename + td.file-size= document.size + td.file-date-modified= new Date(document.timeModified).toLocaleDateString('sl-SL') + td.file-last-cell + button.p-0.delete-file.delete-btn-table + img(src="/images/red-trash-icon.svg" alt="") + span.ms-2= t('Briši') + #upload-files-container.row.ms-2.me-2.align-items-center.mt-4 form#upload-files.col-sm-5.upload-a-file-field.p-2.align-items-center.d-flex( method="post" enctype="multipart/form-data" @@ -44,7 +44,7 @@ block body input(type="hidden" name="extractionId" value=id) .col-sm-5.upload-inner-border.w-100.justify-content-center.d-sm-flex.p-3.drag-area div - button.btn.btn-primary(type="button") Dodaj datoteko + button.btn.btn-primary(type="button")= t('Dodaj datoteko') input#upload( type="file" name="extractionFile" @@ -53,7 +53,7 @@ block body hidden ) .col.d-flex.align-items-center.ms-md-3 - span.gray-info Dodaj besedilo za luščenje terminoloških kandidatov iz lastnega specializiranega korpusa. Ko boste dodali vsa besedila, ki ste jih izbrali, morate izbiro shraniti. - include /utilities/modal-spinner + span.gray-info= t('Dodaj besedilo za luščenje terminoloških kandidatov iz lastnega specializiranega korpusa. Ko boste dodali vsa besedila, ki ste jih izbrali, morate izbiro shraniti.') + //- include /utilities/modal-spinner include /utilities/modal-alert include /common/footer diff --git a/express/views/pages/extraction/edit-oss.pug b/express/views/pages/extraction/edit-oss.pug index 8d3ef1e..736c92a 100644 --- a/express/views/pages/extraction/edit-oss.pug +++ b/express/views/pages/extraction/edit-oss.pug @@ -11,14 +11,14 @@ block body include /common/side-menu-fake +sideNavigationFake include /utilities/dictionaries-main-panel-header - - const c = { noSidebar: false, hrefurl: '/luscenje', h2: 'Luščenje iz korpusa besedil OSS', h1: 'Korpus OSS', description: 'Luščenje terminoloških kandidatov iz besedil, ki so že predhodno oblikoslovno označena, nudi dobre rezultate, vendar je treba nabor besedil omejiti. Svetujemo vam, da zoožite področje in dodatno omejite izbiro s tipi besedil in časovnim razponom, v katerih so nastala. Ko vnesete podatke, morate vse spremembe shraniti. Po vnosu podatkov, s katerimi boste omejili nabor izbranih besedil, morate pritisniti gumb Najdi. Izbiro boste shranili lahko le v primeru, da ne bo število najdenih dokumentov preveliko.', buttons: [{ type: 'cancel', url: '/luscenje', content: 'Nazaj', classAtr: 'btn btn-secondary header-btn-secondary' }] } + - const c = { noSidebar: false, hrefurl: '/luscenje', h2: t('Luščenje iz korpusa besedil OSS'), h1: t('Korpus OSS'), description: t('Luščenje terminoloških kandidatov iz besedil, ki so že predhodno oblikoslovno označena, nudi dobre rezultate, vendar je treba nabor besedil omejiti. Svetujemo vam, da zoožite področje in dodatno omejite izbiro s tipi besedil in časovnim razponom, v katerih so nastala. Ko vnesete podatke, morate vse spremembe shraniti. Po vnosu podatkov, s katerimi boste omejili nabor izbranih besedil, morate pritisniti gumb Najdi. Izbiro boste shranili lahko le v primeru, da ne bo število najdenih dokumentov preveliko.'), buttons: [{ type: 'cancel', url: '/luscenje', content: t('Naprej'), classAtr: 'btn border-header header-btn-secondary' }] } +mainHeader(c) .content-hold-prerequisites #offset-main.mt-2 form#form-edit-oss(method="post") .title .subject-name - label.smaller-black-uppercase(for="name") IME + label.smaller-black-uppercase(for="name")= t('IME') .row .col-sm-6 input.name-input.form-control.d-inline( @@ -27,11 +27,11 @@ block body value=extraction.name ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Izberite takšno ime, ki vam bo pomagalo slediti rezultatom, če boste luščenje besedil opravili večkrat. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Izberite takšno ime, ki vam bo pomagalo slediti rezultatom, če boste luščenje besedil opravili večkrat.') .cerif.mt-4 .subject-name - label.smaller-black-uppercase PODROČJE + label.smaller-black-uppercase= t('PODROČJE') .row .col-sm-6 select.name-input.form-control.d-inline(name="domain") @@ -42,17 +42,18 @@ block body selected=domain.id === extraction.domainId )= domain.nameSl .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Izberite področje iz seznama področij, da zmanjšate obseg besedil, iz katerih bo potekalo luščenje. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Izberite področje iz seznama področij, da zmanjšate obseg besedil, iz katerih bo potekalo luščenje.') .cerif.mt-4 .subject-name - label.smaller-black-uppercase VRSTA DOKUMENTA + label.smaller-black-uppercase= t('VRSTA DOKUMENTA') .row .col-sm-6 select.pick-multiple.name-input.form-control.d-inline( name="documentType" multiple ) + //- TODO i18n - Angleški šifrant, baza, itd.? option option( value=101 @@ -295,11 +296,11 @@ block body selected=extraction.documentType.includes(325) ) Druga izvedena dela .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Izberite vrsto dokumenta iz seznama, npr. članek, diplomsko delo. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Izberite vrsto dokumenta iz seznama, npr. članek, diplomsko delo.') .year.mt-4 .subject-name - label.smaller-black-uppercase LETO + label.smaller-black-uppercase= t('LETO') .row .col-sm-6 select.enter-multiple.name-input.form-control.d-inline( @@ -309,11 +310,11 @@ block body each year in extraction.year option(value=year selected)= year .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Dodajte leto izida besedil, ki jih želite luščiti. Lahko dodate več posameznih let. Če boste polje pustili prazno, bodo vključena vsa leta. Če bo besedil preveč, boste morali omejiti izbiro. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Dodajte leto izida besedil, ki jih želite luščiti. Lahko dodate več posameznih let. Če boste polje pustili prazno, bodo vključena vsa leta. Če bo besedil preveč, boste morali omejiti izbiro.') .words.mt-4 .subject-name - label.smaller-black-uppercase KLJUČNE BESEDE + label.smaller-black-uppercase= t('KLJUČNE BESEDE') .row .col-sm-6 select.enter-multiple.name-input.form-control.d-inline( @@ -323,15 +324,15 @@ block body each keyword in extraction.keywords option(value=keyword selected)= keyword .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 S ključnimi besedami, ki so vključene v večino znanstvenih in strokovnih, lahko bolj natančno izberete besedila, ki jih želite uporabiti za luščenje terminoloških kandidatov. + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('S ključnimi besedami, ki so vključene v večino znanstvenih in strokovnih, lahko bolj natančno izberete besedila, ki jih želite uporabiti za luščenje terminoloških kandidatov.') .table-kas.mt-5.d-sm-flex .d-flex.flex-column.col-sm-6 .personal-documents.justify-content-between.d-flex.align-items-center - span.smaller-black-uppercase (STOP) Termini + span.smaller-black-uppercase= t('(STOP) Termini') a#edit-stop-terms.btn.btn-primary.button-edit.align-items-center.d-flex.justify-content-center.me-2( type="link" href=`${id}/stop-termini` - ) Uredi + )= t('Uredi') div table.table.mt-2 tbody @@ -341,23 +342,23 @@ block body td.text-sm-start.normal-gray= file.filename td.text-sm-end.normal-gray= file.size .col.d-flex.ms-2 - .d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Vse spremembe morate shraniti. #[a.smaller-gray-info(href="/pomoc#help-stop-lists") Več...] + .d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3!= t('Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Vse spremembe morate shraniti. Več...') .col-sm-4.mt-5 - button#search-btn.btn.btn-primary(type="button") Najdi + button#search-btn.btn.btn-primary(type="button")= t('Najdi') #search-result .task.task-completed.p-3.mb-3.mt-4.d-none .d-sm-flex.justify-content-between .d-sm-grid - span.normal-gray Število dokumentov + span.normal-gray= t('Število dokumentov') span#results-count.bold-weight-black .d-sm-flex.align-items-center button#save-params.btn.btn-primary.align-items-center.d-flex( disabled ) img(src="/images/external-link-white.svg") - span.ms-1 Shrani + span.ms-1= t('Shrani') include /utilities/modal-spinner include /utilities/modal-alert diff --git a/express/views/pages/extraction/edit-own.pug b/express/views/pages/extraction/edit-own.pug index d91b2ae..a3e40bc 100644 --- a/express/views/pages/extraction/edit-own.pug +++ b/express/views/pages/extraction/edit-own.pug @@ -2,6 +2,7 @@ extends /layout block pageSpecificScipts script(nonce=cspNonce src="/javascripts/extraction.js") + script(nonce=cspNonce src="/javascripts/extraction-edit-own.js") block body section#fixed-top-section @@ -10,14 +11,14 @@ block body include /common/side-menu-fake +sideNavigationFake include /utilities/dictionaries-main-panel-header - - const c = { noSidebar: false, hrefurl: '/luscenje', h2: 'Luščenje iz lastnih besedil', h1: 'Dodaj opravilo', description: 'Luščenje terminoloških kandidatov iz besedil, ki jih uporabnik namensko izbere, je najučinkovitejše. Primerno izbrano ime opravila, vam omogoča, da lahko sledite, katera luščenja ste že opravili. Ko vnesete podatke, morate vse spremembe shraniti. Zaradi omejenega prostora za shranjevanje lahko shranite največ pet zadnjih luščenj. ', buttons: [{ type: 'cancel', url:'/luscenje', content: 'Prekliči', classAtr:"btn btn-secondary header-btn-secondary" }, { type: 'button', content: 'Shrani', form: 'extraction-name' }] } + - const c = { noSidebar: false, hrefurl: '/luscenje', h2: t('Luščenje iz lastnih besedil'), h1: t('Dodaj opravilo'), description: t('Luščenje terminoloških kandidatov iz besedil, ki jih uporabnik namensko izbere, je najučinkovitejše. Primerno izbrano ime opravila, vam omogoča, da lahko sledite, katera luščenja ste že opravili. Ko vnesete podatke, morate vse spremembe shraniti. Zaradi omejenega prostora za shranjevanje lahko shranite največ pet zadnjih luščenj. '), buttons: [{ type: 'cancel', url:'/luscenje', content: t('Naprej'), classAtr:"btn border-header header-btn-secondary" }, { type: 'button', content: t('Shrani'), classAtr:'btn btn-primary disabled header-btn', form: 'extraction-name' }] } +mainHeader(c) .root-container .content-hold-prerequisites.mt-2 #offset-main form#extraction-name(method="post") .subject-name - label.smaller-black-uppercase(for="name") IME + label.smaller-black-uppercase(for="name")= t('IME') .row.pe-0 .col-sm-6.pe-0 input#name.name-input.form-control.d-inline( @@ -26,16 +27,16 @@ block body value=extraction.name ) .col-sm.d-flex.align-items-center.ms-sm-4.ps-sm-0.mt-1.mt-sm-0 - span.d-md-inline.d-block.smaller-gray-info Izberite takšno ime, ki vam bo pomagalo slediti rezultatom, če boste luščenje besedil opravili večkrat. + span.d-md-inline.d-block.smaller-gray-info= t('Izberite takšno ime, ki vam bo pomagalo slediti rezultatom, če boste luščenje besedil opravili večkrat.') .table-documents.mt-4.d-sm-flex .col-sm-6.d-flex.flex-column .personal-documents.justify-content-between.d-flex.align-items-center - span.smaller-black-uppercase Lastni dokumenti + span.smaller-black-uppercase= t('Lastni dokumenti') a.btn.btn-primary.button-edit.align-items-center.d-flex.justify-content-center( type="link" href=`/luscenje/${id}/besedila` - ) Uredi + )= t('Uredi') div table.table.mt-2 tbody @@ -45,16 +46,16 @@ block body td.text-sm-start.normal-gray= document.filename td.text-sm-end.normal-gray= document.size .col-sm.d-flex.ms-sm-4.mt-1.mt-sm-0 - span.d-md-inline.d-block.smaller-gray-info Preden naložite besedila, jih shranite v besedilni obliki (končnica .txt). + span.d-md-inline.d-block.smaller-gray-info= t('Preden naložite besedila, jih shranite v besedilni obliki (končnica .txt).') .table-kas.mt-4.d-sm-flex .col-sm-6.d-flex.flex-column .personal-documents.justify-content-between.d-flex.align-items-center - span.smaller-black-uppercase (STOP) Termini + span.smaller-black-uppercase= t('(STOP) Termini') a.btn.btn-primary.button-edit.align-items-center.d-flex.justify-content-center( type="link" href=`/luscenje/${id}/stop-termini` - ) Uredi + )= t('Uredi') div table.table.mt-2 tbody @@ -64,6 +65,6 @@ block body td.text-sm-start.normal-gray= file.filename td.text-sm-end.normal-gray= file.size .col-sm.d-flex.ms-sm-4.mt-1.mt-sm-0 - .d-md-inline.d-block.smaller-gray-info Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. #[a.smaller-gray-info(href="/pomoc#help-stop-lists") Več...] + .d-md-inline.d-block.smaller-gray-info!= t('Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Vse spremembe morate shraniti. Več...') include /common/footer diff --git a/express/views/pages/extraction/list.pug b/express/views/pages/extraction/list.pug index 6a41884..e780ffd 100644 --- a/express/views/pages/extraction/list.pug +++ b/express/views/pages/extraction/list.pug @@ -12,7 +12,7 @@ block body +sideNavigationFake if user include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: false, h2: 'Luščenje', h1: 'Seznam luščenj', description: 'Nabor vseh luščenj, s katerimi lahko uporabnik iz izbranih besedil pridobi sezname terminoloških kandidatov. Posamezne terminološke kandidate lahko preveri tudi v konkordančniku.' } + - const c = { sideMenu: false, h2: t('Luščenje'), h1: t('Seznam luščenj'), description: t('Nabor vseh luščenj, s katerimi lahko uporabnik iz izbranih besedil pridobi sezname terminoloških kandidatov. Posamezne terminološke kandidate lahko preveri tudi v konkordančniku.') } +mainHeader(c) - const localeDayOptions = { @@ -28,26 +28,30 @@ block body } .content-hold-prerequisites #offset-main.mt-2 - form(method="post") - input(type="hidden" name="extractionType" value="own") - .row.align-items-center - .col-xl-6.white-border-background.p-4.d-flex.ms-lg-3 - span.smaller-blue-subheading.me-auto.mt-3 Luščenje iz lastnih besedil - button.btn.btn-primary.ps-5.pe-5 DODAJ - .col-sm.mt-2.mt-xl-0.ms-2 - span.span.d-md-inline.d-block.gray-info Luščenje terminoloških kandidatov iz besedil, ki jih ima uporabnik shranjena pri sebi. Za boljšo učinkovitost svetujemo format .txt. - form(method="post") - input(type="hidden" name="extractionType" value="oss") - .row.mt-4.align-items-center - .col-xl-6.white-border-background.p-4.d-flex.ms-lg-3 - span.smaller-blue-subheading.me-auto.mt-3 Luščenje iz korpusa besedil OSS - button.btn.btn-primary.ps-5.pe-5 DODAJ - .col-md.mt-2.mt-xl-0.ms-2 - span.span.d-md-inline.d-block.gray-info Luščenje terminoloških kandidatov iz besedil, ki jih uporabnik izbere med vsemi besedili, vključenimi v #[a.gray-info(href="https://openscience.si/") Nacionalni portal odprte znanosti]. + if extractions.length >= 5 + .d-flex.justify-content-center.mt-3 + p= t('Če želite dodati novo luščenje, morate najprej pobrisati vsaj eno od obstoječih, saj je na posameznega uporabnika dovoljenih največ 5 luščenj.') + else + form(method="post") + input(type="hidden" name="extractionType" value="own") + .row.align-items-center + .col-xl-6.white-border-background.px-0.py-4.d-flex.ms-xl-3 + span.smaller-blue-subheading.ms-3.me-auto.mt-3= t('Luščenje iz lastnih besedil') + button.btn.btn-primary.ps-5.pe-5.me-3= t('DODAJ') + .col-sm.mt-2.mt-xl-0.ps-1.ms-lg-3 + span.span.d-md-inline.d-block.gray-info= t('Luščenje terminoloških kandidatov iz besedil, ki jih ima uporabnik shranjena pri sebi. Za boljšo učinkovitost svetujemo format .txt.') + form(method="post") + input(type="hidden" name="extractionType" value="oss") + .row.mt-4.align-items-center + .col-xl-6.white-border-background.px-0.py-4.d-flex.ms-xl-3 + span.smaller-blue-subheading.ms-3.me-auto.mt-3= t('Luščenje iz korpusa besedil OSS') + button.btn.btn-primary.ps-5.pe-5.me-3= t('DODAJ') + .col-md.mt-2.mt-xl-0.ps-1.ms-lg-3 + span.span.d-md-inline.d-block.gray-info!= t('Luščenje terminoloških kandidatov iz besedil, ki jih uporabnik izbere med vsemi besedili, vključenimi v Nacionalni portal odprte znanosti.') - #extraction-list.all-tasks.mt-5.ms-2.ps-0 + #extraction-list.all-tasks.mt-4.ps-0 if !extractions.length - p Nimate luščenj za urejanje. + p= t('Nimate luščenj za urejanje.') else each el in extractions if el.status === 'new' @@ -56,26 +60,26 @@ block body .d-grid.task-name span.bold-weight-black= el.name .d-flex.align-items-center - button.btn.btn-secondary.extraction-btn.btn-begin.align-items-center.d-flex( + button.btn.border-header.extraction-btn.btn-begin.align-items-center.d-flex( disabled=!el.canBegin ? true : false ) img(src="/images/fi_arrow-right-circle.svg") - span.ms-1 ZAČNI + span.ms-1= t('ZAČNI') hr.mt-2.mb-3 - .d-flex.justify-content-sm-between + .d-flex.justify-content-sm-between.align-items-center .d-sm-flex.align-items-center.mb-0 img(src="/images/alert-circle.svg") - span.normal-gray.ms-1.me-2.me-sm-0 Nov - .d-flex.align-items-center + span.normal-gray.ms-1.me-2.me-sm-0= t('Nov') + .d-flex.align-items-center.ms-auto a.btn.p-0.align-items-center.me-lg-3.edit-task( href=`luscenje/${el.id}` ) img(src="/images/u_edit-alt.svg" alt="Edit") - span.ms-1.normal-gray Uredi + span.ms-1.normal-gray= t('Uredi') .ms-3 button.p-0.btn.delete-task.btn-delete(type="button") img(src="/images/red-trash-icon.svg" alt="") - span.normal-gray.ms-2 Briši + span.normal-gray.ms-2= t('Briši') if el.status === 'in progress' .task.task-in-progress.p-3.mb-3(data-id=el.id) @@ -87,7 +91,7 @@ block body .d-grid.ms-1.align-items-center .d-flex.align-items-center img.me-1(src="/images/fi_calendar.svg") - span.smaller-gray-uppercase.align-items-center Začetek + span.smaller-gray-uppercase.align-items-center= t('Začetek') .d-grid.ms-4.align-items-center span.bold-weight-black= el.timeStarted.toLocaleString('sl-SI', localeDayOptions) span.normal-gray= el.timeStarted.toLocaleString('sl-SI', localeHourOptions) @@ -95,20 +99,20 @@ block body .d-flex.justify-content-between .d-sm-flex.align-items-center.mb-0.flex-grow-1 img(src="/images/clock.svg") - span.normal-gray.ms-1 V obdelavi + span.normal-gray.ms-1= t('V obdelavi') if el.status === 'failed' .task.task-cancelled.p-3.mb-3(data-id=el.id) - .d-lg-flex.justify-content-between - .d-flex.flex-column + .d-lg-flex + .d-flex.flex-column.task-name span.bold-weight-black= el.name - .d-sm-flex + .d-sm-flex.dates-div .d-flex .d-sm-flex.start-date .d-sm-grid.ms-1.align-items-center.mt-2.mt-md-0 .d-sm-flex.align-items-center img.me-1(src="/images/fi_calendar.svg") - span.smaller-gray-uppercase.align-items-center Začetek + span.smaller-gray-uppercase.align-items-center= t('Začetek') .d-grid.ms-2.ms-sm-4.align-items-center span.bold-weight-black= el.timeStarted.toLocaleString('sl-SI', localeDayOptions) span.normal-gray= el.timeStarted.toLocaleString('sl-SI', localeHourOptions) @@ -116,24 +120,22 @@ block body .d-md-grid.ms-md-1.align-items-center .d-md-flex.align-items-center img.me-1(src="/images/fi_calendar.svg") - span.smaller-gray-uppercase.align-items-center Konec + span.smaller-gray-uppercase.align-items-center= t('Konec') .d-grid.ms-4.align-items-center span.bold-weight-black= el.timeFinished.toLocaleString('sl-SI', localeDayOptions) span.normal-gray= el.timeFinished.toLocaleString('sl-SI', localeHourOptions) - //- TODO: Once duplication is implemented remove "disabled" attribute from btn - .d-flex.align-items-center.justify-content-lg-end.mt-2.mt-sm-0.ms-2.ms-md-0 - button.btn.btn-secondary.double-task.align-items-center.d-flex( - disabled - ) + //- TODO: Once duplication is implemented remove ".invisible" class + .d-flex.align-items-center.justify-content-lg-end.mt-2.mt-sm-0.ms-2.ms-md-0.invisible + button.btn.border-header.double-task.align-items-center.d-flex img(src="/images/fi_copy_border.svg") - span.ms-1 PODVOJI + span.ms-1= t('PODVOJI') hr.mt-2.mb-3 .d-flex.justify-content-sm-between .d-sm-flex.align-items-center.mb-sm-0.me-3.me-sm-0 img(src="/images/alert-triangle.svg") - span.normal-gray.ms-sm-1.ms-0 Prekinjen - .d-flex.align-items-center.mb-sm-0.me-3.mt-0 + span.normal-gray.ms-sm-1.ms-0= t('Prekinjen') + .d-flex.mb-sm-0.mt-0 //- a.edit-task.btn.p-0(href=`luscenje/${el.id}`) //- img(src="/images/u_edit-alt.svg" alt="Edit") //- span.normal-gray.ms-sm-1.ms-0 Uredi @@ -143,19 +145,19 @@ block body .ms-3 button.p-0.btn.delete-task.btn-delete(type="button") img(src="/images/red-trash-icon.svg" alt="") - span.normal-gray.ms-2 Briši + span.normal-gray.ms-2= t('Briši') if el.status === 'finished' .task.task-completed.p-3.mb-3(data-id=el.id) - .d-lg-flex.justify-content-between + .d-sm-flex .d-flex.flex-column.task-name span.bold-weight-black= el.name - .d-sm-flex + .d-sm-flex.dates-div .d-flex .d-sm-flex.start-date .d-sm-grid.ms-1.align-items-center.mt-2.mt-md-0 .d-sm-flex.align-items-center img.me-1(src="/images/fi_calendar.svg") - span.smaller-gray-uppercase.align-items-center Začetek + span.smaller-gray-uppercase.align-items-center= t('Začetek') .d-grid.ms-2.ms-sm-4.align-items-center span.bold-weight-black= el.timeStarted.toLocaleString('sl-SI', localeDayOptions) span.normal-gray= el.timeStarted.toLocaleString('sl-SI', localeHourOptions) @@ -163,58 +165,60 @@ block body .d-md-grid.ms-md-1.align-items-center .d-md-flex.align-items-center img.me-1(src="/images/fi_calendar.svg") - span.smaller-gray-uppercase.align-items-center Konec + span.smaller-gray-uppercase.align-items-center= t('Konec') .d-grid.ms-4.align-items-center span.bold-weight-black= el.timeFinished.toLocaleString('sl-SI', localeDayOptions) span.normal-gray= el.timeFinished.toLocaleString('sl-SI', localeHourOptions) - .d-sm-flex.align-items-center.ms-sm-5.mt-2.mt-md-0 - img(src="/images/fi_check-circle.svg") - span.ms-2 Končan - .d-sm-flex.align-items-center.mt-2.mt-md-0.ms-auto.me-3 - a.edit-task.btn.p-0(href=`luscenje/${el.id}`) - img(src="/images/u_edit-alt.svg") - span.ms-1 UREDI + + .d-flex.align-items-center.ms-2.ms-sm-auto.mt-2.mt-md-0 + img(src="/images/fi_check-circle.svg") + span.ms-2= t('Končan') + //- .d-sm-flex.align-items-center.mt-2.mt-md-0.ms-auto.me-3 + //- a.edit-task.btn.p-0.ms-auto(href=`luscenje/${el.id}`) + //- img(src="/images/u_edit-alt.svg") + //- span.ms-1 UREDI hr.mt-2.mb-3 .d-lg-flex.justify-content-between - .d-lg-flex.align-content-center.mb-0.flex-grow-1 - a#terminology-candidates.btn.btn-primary.align-items-center( + .d-lg-flex.align-items-center.mb-0.flex-grow-1 + a.terminology-candidates.btn.btn-primary.align-items-center( href=`luscenje/${el.id}/kandidati` ) img(src="/images/book-white.svg") - span.ms-1 Terminološki kandidati [#{ el.termCandidatesCount }] + //- TODO I18n - Možna kakšna napaka, trenutno ne morem testirati luščenja + span.ms-1 #{ t('Terminološki kandidati') } [#{ el.termCandidatesCount }] if el.status === 'finished' && el.corpusId - a.btn.btn-secondary.ps-2.pe-2.mt-2.mt-lg-0.ms-md-1( + a.btn.border-header.ext-secondary-btn.mt-2.mt-lg-0.ms-md-1( href=`/korpus/${el.corpusId}` target="_blank" ) img(src="/images/list.svg") - span.normal-gray.ms-1 Uporabniški korpus + span.normal-gray.ms-1= t('Uporabniški korpus') if el.status === 'finished' && el.ossParams - a.btn.btn-secondary.ps-2.pe-2.mt-2.mt-lg-0.ms-md-1( - href="https://www.clarin.si/noske/run.cgi/corp_info?corpname=oss&struct_attr_stats=1" + a.btn.border-header.ext-secondary-btn.mt-2.mt-lg-0.ms-md-1( + href="https://www.clarin.si/ske/#dashboard?corpname=oss" target="_blank" ) img(src="/images/list.svg") - span.normal-gray.ms-1 Korpus KAS+ - .d-flex.align-items-center.mb-0.me-3.mt-2.mt-lg-0 - .align-items-center.double-task - img(src="/images/fi_copy_border.svg" alt="Copy") - span.normal-gray.ms-1 Podvoji + span.normal-gray.ms-1= t('Korpus OSS') + .d-flex.align-items-center.mb-0.mt-2.mt-lg-0.justify-content-end + //- .align-items-center.double-task + //- img(src="/images/fi_copy_border.svg" alt="Copy") + //- span.normal-gray.ms-1 Podvoji .ms-3 button.p-0.btn.delete-task.btn-delete(type="button") img(src="/images/red-trash-icon.svg" alt="") - span.normal-gray.ms-2 Briši + span.normal-gray.ms-2= t('Briši') include /utilities/modal-alert include /utilities/modal-alert-mixin - +alertModal("max-extractions", "Razumem", "Prekliči", "ok-btn-modal", "cancel-btn", 'TODO text več kot 5 luščenj!') + +alertModal("max-extractions", t("Razumem"), t("Prekliči"), "ok-btn-modal", "cancel-btn", t('Ne morete imeti več kot 5 luščenj!')) include /utilities/modal-response - +responseModal("begin-response", "Razumem", "understand-btn", "Luščenje je bilo dano v fazo obdelave.") + +responseModal("begin-response", t("Razumem"), "understand-btn", t("Luščenje je bilo dano v fazo obdelave."), t("Ko bo luščenje uspešno zaključeno, boste na svoj elektronski naslov, ki ste ga navedli ob registraciji, prejeli obvestilo.")) else include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: false, h2: 'Luščenje', h1: 'Seznam luščenj', description: 'Nabor vseh luščenj, s pomočjo katerih uporabnik iz vhodnih besedil pridobi sezname terminoloških kandidatov in dostop do konkordančnika po teh besedilih.' } + - const c = { sideMenu: false, h2: t('Luščenje'), h1: t('Seznam luščenj'), description: t('Nabor vseh luščenj, s pomočjo katerih uporabnik iz vhodnih besedil pridobi sezname terminoloških kandidatov in dostop do konkordančnika po teh besedilih.') } +mainHeader(c) .content-hold-prerequisites #offset-main .d-flex.justify-content-center.mt-5 - p Za luščenje terminoloških kandidatov morate biti prijavljeni. + p= t('Za luščenje terminoloških kandidatov morate biti prijavljeni.') include /common/footer diff --git a/express/views/pages/extraction/stop-terms-edit.pug b/express/views/pages/extraction/stop-terms-edit.pug index 25270cc..f023005 100644 --- a/express/views/pages/extraction/stop-terms-edit.pug +++ b/express/views/pages/extraction/stop-terms-edit.pug @@ -11,7 +11,7 @@ block body include /common/side-menu-fake +sideNavigationFake include /utilities/dictionaries-main-panel-header - - const c = { noSidebar: false, hrefurl: '.', h2: 'Luščenje | dodaj opravilo', h1: 'Seznam neželenih terminov', description: 'Seznam terminoloških kandidatov bo natančnejši, če boste dodali tudi seznam neželenih besed, ki naj jih luščilnik izloči iz seznama. Seznam lahko vsebuje splošne termine, npr. tabela, kazalnik, ali pogoste slovnične besede, zlasti veznike, pomožne glagole.', buttons: [{ type: 'cancel', content: 'Nazaj', classAtr: 'btn btn-secondary header-btn-secondary link-back' }] } + - const c = { noSidebar: false, hrefurl: '.', h2: t('Luščenje | dodaj opravilo'), h1: t('Seznam neželenih terminov'), description: t('Seznam terminoloških kandidatov bo natančnejši, če boste dodali tudi seznam neželenih besed, ki naj jih luščilnik izloči iz seznama. Seznam lahko vsebuje splošne termine, npr. tabela, kazalnik, ali pogoste slovnične besede, zlasti veznike, pomožne glagole.'), buttons: [{ type: 'cancel', content: t('Naprej'), classAtr: 'btn border-header header-btn-secondary link-back' }] } +mainHeader(c) .content-hold-prerequisites.mt-2 #offset-main @@ -19,24 +19,24 @@ block body .table-responsive table.styled-table thead - tr + tr#thead th= 'ID' - th= 'IME DATOTEKE' - th= 'VELIKOST' - th= 'DATUM' + th= t('IME DATOTEKE') + th= t('VELIKOST') + th= t('DATUM') th= '' tbody#files-list each document, index in stopTermsFiles tr - td= index + 1 - td.filename= document.filename - td= document.size - td= new Date(document.timeModified).toLocaleDateString('sl-SL') - td - button.p-0.delete-file.delete-btn-table(type="button") - img(src="/images/red-trash-icon.svg" alt="Izbriši") - span.ms-2 Briši - .row.ms-2.me-2.align-items-center.mt-4 + td.file-index= index + 1 + td.file-name= document.filename + td.file-size= document.size + td.file-date-modified= new Date(document.timeModified).toLocaleDateString('sl-SL') + td.file-last-cell + button.p-0.delete-file.delete-btn-table + img(src="/images/red-trash-icon.svg" alt="") + span.ms-2= t('Briši') + #upload-files-container.row.ms-2.me-2.align-items-center.mt-4 form#upload-files.col-sm-5.upload-a-file-field.p-2.align-items-center.d-flex( method="post" enctype="multipart/form-data" @@ -44,7 +44,7 @@ block body input(type="hidden" name="extractionId" value=id) .col-sm-5.upload-inner-border.w-100.justify-content-center.d-sm-flex.p-3.drag-area div - button.btn.btn-primary(type="button") Dodaj datoteko + button.btn.btn-primary(type="button")= t('Dodaj datoteko') input#upload( type="file" name="extractionFile" @@ -53,7 +53,7 @@ block body hidden ) .col.d-flex.align-items-center.ms-md-3 - span.gray-info Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Na koncu morate spremembe shraniti. #[a.smaller-gray-info(href="/pomoc#help-stop-lists") Več...] - include /utilities/modal-spinner + span.gray-info!= t('Dodate lahko seznam besed, ki jih v seznam terminoloških kandidatov ne želite vključiti. Seznam naj bo shranjen v formatu .txt. Na koncu morate spremembe shraniti. Več ...') + //- include /utilities/modal-spinner include /utilities/modal-alert include /common/footer diff --git a/express/views/pages/extraction/term-candidates.pug b/express/views/pages/extraction/term-candidates.pug index d44b352..946a32d 100644 --- a/express/views/pages/extraction/term-candidates.pug +++ b/express/views/pages/extraction/term-candidates.pug @@ -12,13 +12,11 @@ block body include /common/side-menu-fake +sideNavigationFake include /utilities/dictionaries-main-panel-header - - const c = { noSidebar: false, hrefurl: '.', h2: 'Luščenje', h1: 'Terminološki kandidati', description: 'Seznam terminoloških kandidatov, ki so rezultat izbranega luščenja.', buttons: [{ type: 'cancel', url: '/luscenje', content: 'Nazaj', classAtr: 'btn btn-secondary header-btn-secondary' }] } + - const c = { noSidebar: false, hrefurl: '.', h2: t('Luščenje'), h1: t('Terminološki kandidati'), description: t('Seznam terminoloških kandidatov, ki so rezultat izbranega luščenja.'), buttons: [{ type: 'cancel', url: '/luscenje', content: t('Naprej'), classAtr: 'btn border-header header-btn-secondary' }] } +mainHeader(c) .content-hold-prerequisites.me-2 #offset-main.mt-3 - .d-flex.w-100.justify-content-between.mb-4 - .d-flex - include /components/search-and-filter/inline-search + .d-flex.w-100.justify-content-end.mb-4 .d-flex include /utilities/pager +pager @@ -26,30 +24,30 @@ block body .table-responsive table.styled-table thead - tr - th= '#' - th= 'KANONIČNA OBLIKA' - th= 'RANKING' - th= 'POGOSTOST OBJAVLJANJA' + tr#thead + th.td-index= '#' + th.td-cannon= t('KANONIČNA OBLIKA') + th= t('UTEŽ') + th= t('POJAVITVE') tbody#page-results each termCandidate, index in firstPageOfTermCandidates tr td= index + 1 td= termCandidate.kanonicnaoblika td= termCandidate.ranking - td= termCandidate.pogostostpojavljanja + td= termCandidate.pogostostpojavljanja[0] form(action=`/api/v1/extraction/${extractionId}/term-candidates-export`) .row.mt-4 .me-0.pe-0.d-flex.align-items-center - span.radio-button-labels Izvozi termine od številke + span.radio-button-labels= t('Izvozi termine od številke') input.without-arrows.form-control.terminology-input.ms-1( type="number" name="from" maxlength="5" min="0" ) - span.radio-button-labels.ms-1 do številke + span.radio-button-labels.ms-1= t('do številke') input.without-arrows.form-control.terminology-input.ms-1( type="number" name="to" @@ -99,7 +97,7 @@ block body //- span.name-info-txt.ms-5 Navodilo k posamičnemu polju. Anim duis ullamco Lorem reprehenderit. .d-sm-flex.mt-4 button.btn.btn-primary.me-2 - | Izvozi + = t('Izvozi') include /common/footer diff --git a/express/views/pages/help-pug-demo-frame.pug b/express/views/pages/help-pug-demo-frame.pug index 7014fbb..8c638ec 100644 --- a/express/views/pages/help-pug-demo-frame.pug +++ b/express/views/pages/help-pug-demo-frame.pug @@ -1,3 +1,6 @@ +// DEMO?? +//- TODO MARK FOR DELETION + extends /layout block body diff --git a/express/views/pages/help-pug-demo.pug b/express/views/pages/help-pug-demo.pug index adab44e..48c4061 100644 --- a/express/views/pages/help-pug-demo.pug +++ b/express/views/pages/help-pug-demo.pug @@ -1,3 +1,7 @@ +//- TODO MARK FOR DELETION + + + h2 Iskanje p bold #[b adasdasda] diff --git a/express/views/pages/help.pug b/express/views/pages/help.pug index e3209c7..af6366b 100644 --- a/express/views/pages/help.pug +++ b/express/views/pages/help.pug @@ -15,5 +15,8 @@ block body data-bs-target="#list-example" tabindex="0" ) - include /utilities/help-text - include /common/footer + if determinedLanguage === 'sl' + include /utilities/help-text_sl + else + include /utilities/help-text_en + include /common/footer diff --git a/express/views/pages/index.pug b/express/views/pages/index.pug index cde51d2..7687f38 100644 --- a/express/views/pages/index.pug +++ b/express/views/pages/index.pug @@ -1,8 +1,8 @@ extends /layout block pageSpecificScipts - script(nonce=cspNonce src="/javascripts/collapsable-comments.js") - script(nonce=cspNonce src="/javascripts/comments.js") + //- script(nonce=cspNonce src="/javascripts/collapsable-comments.js") + //- script(nonce=cspNonce src="/javascripts/comments.js") block body .index-footer @@ -11,7 +11,8 @@ block body .index-nav ul.nav li.nav-item - img.logo(src="/images/logo.png" alt="Logo") + a(href="https://www.slovenscina.eu/") + img.logo(src="/images/logo.png" alt="Logo") include ../components/navigation/main-navigation-right-mixin @@ -24,9 +25,9 @@ block body //- p Terminološki portal je samostojna, odprto dostopna spletna storitev, v katero so vključeni terminološki viri na samem portalu, na integriranem iskalniku pa so dostopni tudi zadetki iz spletišča Terminologišče in portala Termania. Registriranim uporabnikom je na voljo tudi luščilnik terminoloških kandidatov iz specializiranih korpusov, konkordančnik za specializirana besedila, označevalnik terminov v strokovnih besedilih, urejevalnik terminoloških virov, terminološka svetovalnica in stran s pomočjo in navodili za uporabo posameznih funkcij portala. Terminološki portal je moderiran. p!= portalDescription p.pt-5.fw-500.d-flex - span.d-flex Zadnji objavljeni slovarji + span.d-flex #{ t("Zadnji objavljeni slovarji") } span.navigation-text-color.d-flex.flex-grow-1.justify-content-end - a.text-decoration-none(href="/slovarji") Vsi slovarji > + a.text-decoration-none(href="/slovarji") #{ t("Vsi slovarji") } > include /components/dictionary/dictionary-list +table(latestDicts) @@ -41,9 +42,9 @@ block body a(href="/slovarji/id_slovarja/o-slovarju") Davčni slovar span= ', ' a(href="/slovarji/id_slovarja/o-slovarju") Glosar akademske integritete - p.pt-5 + //- p.pt-5 - include /utilities/comments-with-collapsable-pager + //- include /utilities/comments-with-collapsable-pager //- uncomment scripts for comments if uncomment this //- +pager include /common/footer diff --git a/express/views/pages/my-dictionaries-root.pug b/express/views/pages/my-dictionaries-root.pug index 56e1a51..f48c0b8 100644 --- a/express/views/pages/my-dictionaries-root.pug +++ b/express/views/pages/my-dictionaries-root.pug @@ -1,3 +1,5 @@ +//- TODO MARK FOR DELETION + extends ../layout block body diff --git a/express/views/pages/privacy-policy_en.pug b/express/views/pages/privacy-policy_en.pug new file mode 100644 index 0000000..d99d847 --- /dev/null +++ b/express/views/pages/privacy-policy_en.pug @@ -0,0 +1,82 @@ +extends /layout + +block body + section.sticky-top + include /common/main-navigation + +main-navigation(false) + .consultancy-padding-main.p-squeeze-lg.mt-2 + h1 Privacy policy + p This Privacy policy defines collection, storage, and processing of personal data, collected by the University of Ljubljana during user registration on the #[b Slovenian Terminology Portala]. + .mt-4 + h2 Data controller + p Data controller as defined by the EU General Data Protection Regulation and the applicable act that regulates personal data protection is: + span University of Ljubljana + span Kongresni trg 12, 1000 Ljubljana, Slovenija + a(href="mailto:dpo@uni-lj.si") dpo@uni-lj.si + .mt-4 + h4 Data protection officer + p Data protection officer (DPO) and the person who can answer all questions related to processing of personal data in the scope of the Development of Slovene in a Digital Environment Project is Nina Komočar Urbanija. You can send all your questions, queries, and demands to exercise your rights related to your personal data in the scope of the Development of Slovene in a Digital Environment Project: + span.ms-4 - via e-mail to #[a(href="mailto:dpo@uni-lj.si") dpo@uni-lj.si], + span.ms-4 - via mail to the address of the Data controller: DPO, University of Ljubljana, Kongresni trg 12, 1000 Ljubljana, Slovenija. + .mt-4 + h2 Personal data and our purposes for processing it + p To use advanced functions of the Slovenian Terminology Portal, you must register via registration form on the webpage #[a(href="http://terminoloski.slovenscina.eu" target="_blank") http://terminoloski.slovenscina.eu]. By filling in and submitting this form, you will send the University of Ljubljana the following contact data: + ul + li #[b User name] + li #[b First Name] + li #[b Last Name] + li #[b E-mail] + p We need the contact data collected via the registration form to personalize your content and enable some of the Terminology Portal functions (e.g. storing extraction results), and to communicate with you regarding any questions related to the functions of the Terminology Portal. + p If you decide not to submit the above mentioned contact data, your registration will not be completed and you will not be able to use all functions offered by the Terminology Portal. + p The University of Ljubljana respects your privacy and is committed to act with diligence and in accordance with the applicable data protection regulations when collecting, storing and processing personal data. + p We use appropriate technological and organizational processes to protect the data we collect, to prevent unauthorized access to the collected data and any data disclosures, and to ensure accuracy and appropriate use of the data. + .mt-4 + h2 Legal basis for data processing - consent + p The University of Ljubljana processes the personal data you provide during the registration process on the #[b Slovenian Terminology Portal] with your clear and unambiguous consent based on Article 6 (1), (a) of the EU General Data Protection Regulation (GDPR). + span You consent to the processing of your personal data by filling in the required data and clicking "Register" on the Slovenian Terminology Portal web page found at the link #[a(href="http://terminoloski.slovenscina.eu" target="_blank") http://terminoloski.slovenscina.eu]. + span You can withdraw your consent at any time by deleting your user account in the "Account settings" tab. + span If you withdraw your consent and close your user account, we will stop using your personal data to communicate with you. + span Potential withdrawal of the consent does not impact the legality of personal data processing in the time period before the consent was withdrawn. + .mt-4 + h2 Your rights + span In accordance with the provisions of the EU General Data Protection Regulation (GDPR), you have the right to access your personal data, the right to rectification, the right to erasure ("the right to be forgotten"), the right to data portability, the right to request the restriction of the processing of personal data and the right to object. You can check and change your personal data at any time by clicking the link "Change profile settings". + p To exercise all of your rights or to obtain additional information, please contact our Data Protection Officer at the e-mail address: #[a(href="mailto:dpo@uni-lj.si") dpo@uni-lj.si]. We will process your application and respond to it in accordance with the GDPR. + p If you believe your rights or any data protection regulations are being violated, you can complain to the competent national authority: + p You can send the complaint to the Information Officer (Dunajska cesta 22, 1000 Ljubljana, e-mail: #[a(href="mailto:gp.ip@ip-rs.si") gp.ip@ip-rs.si] phone: 012309730, website: #[a(href="www.ip-rs.si" target="_blank") www.ip-rs.si] ) + .mt-4 + h2 Personal data storage period + p The University of Ljubljana stores your personal data (first name, last name, e-mail address) for as long as this is necessary for the purpose of your use of the Slovenian Terminology Portal, i.e. from your registration until the closure of your user account. + .mt-4 + h2 Personal Data Users + p Contracted Data Processor who maintains and hosts the website for the University of Ljubljana. + .mt-4 + h2 Transfer of personal data to third countries or international organizations + p Personal data in not transferred to third countries or international organizations. + .mt-4 + h2 Cookies + .mt-3 + h4 What are cookies? + p Cookies are files in which website settings are stored. Websites store cookies on the devices the users use to access the internet in order to recognize individual devices and the settings individual users used when accessing the website. Cookies allow websites to remember whether the user has already visited a specific website. Advanced applications use cookies to personalize certain settings to individual users. The browser used by the user in in complete control of the cookie storing process. This means that the user can limit or deny which cookies are stored as desired. + h4 Why are cookies necessary? + p Cookies are essential for providing user-friendly on-line services. None of the most common e-commerce functions would be possible without cookies. Cookies make the interactions between the user and the website faster and simpler. They allow the website to remember the user’s preferences and experiences, making website browsing a more efficient and pleasant experience. There are several reasons to use cookies. Cookies can be used to store information on the status of individual web pages (details about the personalizations of individuals pages), to enable certain on-line services (e.g. on-line stores), to gather statistical data about the users and visits of the website, the visitors’ habits, etc. This means that cookies allow us to measure the effectiveness of our website design. You can read more about the recommended and allowed use of cookies on websites on the #[a(href="https://www.ip-rs.si" target="_blank") Information Officer website]. + h4 List of cookies on our website + .mt-2 + h4 First party cookies + p #[b sid] - System cookie. It is also used for user sign-in. It is essential for correct functioning of the website. The cookie remains present for up to two hours after your last activity on the portal. + p #[b remember_me] - It is used for long-term user sign-in, if the user selects “Remember me" during the signing in process. The cookie remains present for 1 year after your last activity on the portal. + p The cookies used on our website do not collect any personal data that could be used to recognize you personally and cannot harm your computer, tablet or mobile phone. The cookies make our website work and help us understand which information are the most useful for our visitors. + .mt-4 + h2 Consent + p By using this website you agree to let the website place cookies to your computer or mobile device. + .mt-4 + h2 Managing and deleting cookies + p If you want to change the way cookies are used in your browser, including blocking or deleting the cookies, you can do that by changing the browser settings. If you want to manage your cookies, most of the browsers allow you to accept or deny all cookies, allow you to accept only a certain type of cookies, or notify you when a website wants to install cookies in the browser. The cookies stored by the browser can also simply be deleted. If you change or delete the file with the cookies on your browser, or change or update your browser or device, it is possible that you will have to disable the cookies again. The process for managing and deleting cookies is different in every browser. If you need help, consult your browser’s help section. + .mt-4 + h2 Here are the instructions to edit cookies in some browsers: + ul + li #[a(href="https://support.google.com/chrome/answer/95647?hl=sl" target="_blank") Google Chrome] + li #[a(href="https://support.microsoft.com/en-us/windows/delete-and-manage-cookies-168dab11-0753-043d-7c16-ede5947fc64d#ie=ie-10" target="_blank") Internet Explorer] + li #[a(href="https://support.apple.com/guide/safari/manage-cookies-sfri11471/mac" target="_blank") Safari] + li #[a(href="https://support.mozilla.org/en-US/kb/enhanced-tracking-protection-firefox-desktop?redirectslug=enable-and-disable-cookies-website-preferences&redirectlocale=en-US" target="_blank") Firefox] + li #[a(href="https://help.opera.com/en/latest/web-preferences" target="_blank") Opera] + include /common/footer diff --git a/express/views/pages/privacy-policy.pug b/express/views/pages/privacy-policy_sl.pug similarity index 99% rename from express/views/pages/privacy-policy.pug rename to express/views/pages/privacy-policy_sl.pug index d462560..6158057 100644 --- a/express/views/pages/privacy-policy.pug +++ b/express/views/pages/privacy-policy_sl.pug @@ -8,7 +8,7 @@ block body h1 Politika zasebnosti p Ta politika zasebnosti ureja zbiranje, hrambo in obdelavo osebnih podatkov, ki jih Univerza v Ljubljani zbira od vas, ko se registrirate za uporabo #[b Slovenskega terminološkega portala]. .mt-4 - h2 Upravljalec + h2 Upravljavec p Upravljavec osebnih podatkov, kot ga določata Splošna uredba EU o varstvu osebnih podatkov in veljavni zakon, ki ureja varstvo osebnih podatkov, je: span Univerza v Ljubljani span Kongresni trg 12, 1000 Ljubljana, Slovenija diff --git a/express/views/pages/profile/change-password.pug b/express/views/pages/profile/change-password.pug index 977de04..53ba04d 100644 --- a/express/views/pages/profile/change-password.pug +++ b/express/views/pages/profile/change-password.pug @@ -11,15 +11,15 @@ block body - const d = { selection: 'change-profile-password' } +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h1: 'Spremeni geslo', description: 'Tukaj lahko spremenite geslo za prijavo.', buttons: [{ type: 'disabled', content: 'Shrani', form: 'profileForm' }] } + - const c = { sideMenu: true, h1: title, description: t('Tukaj lahko spremenite geslo za prijavo.'), buttons: [{ type: 'disabled', content: t('Shrani'), form: 'profileForm' }] } +mainHeader(c) .content-hold-prerequisites - #offset-main.main-container.mt-2.pe-5 + #offset-main.main-container.mt-2 form.needs-validation(method="post" novalidate) div .subject-name - label.input-name-txt(for="password-old") STARO GESLO + label.input-name-txt(for="password-old") #{ t('STARO GESLO') } .row .col-sm-6.align-items-center input#password-old.name-input.form-control.d-inline( @@ -27,14 +27,14 @@ block body name="password-old" value="" ) - .invalid-feedback Napačno geslo. + .invalid-feedback #{ t('Napačno geslo.') } .col-sm.d-flex.align-items-center span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 .mt-4 .subject-name - label.input-name-txt(for="dictionary-title-en") NOVO GESLO + label.input-name-txt(for="dictionary-title-en") #{ t('NOVO GESLO') } .row .col-sm-6 input#password-new.name-input.form-control.d-inline( @@ -43,14 +43,14 @@ block body minLength="1" value="" ) - .invalid-feedback Geslo je prekratko. + .invalid-feedback #{ t('Geslo je prekratko.') } .col-sm.d-flex.align-items-center span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 .mt-4 .subject-name - label.input-name-txt(for="dictionary-title-en") PONOVI NOVO GESLO + label.input-name-txt(for="dictionary-title-en") #{ t('PONOVI NOVO GESLO') } .row .col-sm-6 input#password-repeat.name-input.form-control.d-inline( @@ -59,9 +59,9 @@ block body minLength="1" value="" ) - .invalid-feedback Geslo se ne ujema. + .invalid-feedback #{ t('Geslo se ne ujema.') } .col-sm.d-flex.align-items-center span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 - include /common/footer + include /common/footer diff --git a/express/views/pages/profile/change-profile-settings.pug b/express/views/pages/profile/change-profile-settings.pug index af4541f..54e1bd6 100644 --- a/express/views/pages/profile/change-profile-settings.pug +++ b/express/views/pages/profile/change-profile-settings.pug @@ -11,15 +11,15 @@ block body - const d = { selection: 'change-profile-settings' } +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h1: 'Nastavitve računa', description: 'Tukaj lahko spremenite nekatere nastavitve, vezane na posameznega uporabnika.', buttons: [{ type: 'disabled', content: 'Shrani', form: 'profileForm' }] } + - const c = { sideMenu: true, h1: title, description: t('Tukaj lahko spremenite nekatere nastavitve, vezane na posameznega uporabnika.'), buttons: [{ type: 'disabled', content: t('Shrani'), form: 'profileForm' }] } +mainHeader(c) .content-hold-prerequisites - #offset-main.main-container.mt-2.pe-5 + #offset-main.main-container.mt-2 form#profileForm.needs-validation(method="post" novalidate) div .subject-name - label.input-name-txt(for="number-of-results") ŠTEVILO ZADETKOV NA STRANI + label.input-name-txt(for="number-of-results") #{ t('ŠTEVILO ZADETKOV NA STRANI') } .row .col-sm-6.align-items-center select#numberOfHits.form-control.d-inline(name="numberOfHits") @@ -31,4 +31,4 @@ block body .col-sm.d-flex.align-items-center span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 - include /common/footer + include /common/footer diff --git a/express/views/pages/profile/delete-profile.pug b/express/views/pages/profile/delete-profile.pug new file mode 100644 index 0000000..13603d9 --- /dev/null +++ b/express/views/pages/profile/delete-profile.pug @@ -0,0 +1,94 @@ +extends /layout + +block pageSpecificScipts + script(nonce=cspNonce src="/javascripts/admin.js") + script(nonce=cspNonce src="/javascripts/profile-scripts.js") + +block body + section#fixed-top-section + include /common/main-navigation + +main-navigation(false) + include /pages/profile/my-profile-side-menu-mixin + - const d = { selection: 'delete-profile' } + +sideNavigation(d) + include /utilities/dictionaries-main-panel-header + - const c = { sideMenu: true, h1: title, description: t('Tukaj lahko izbrišete svoj račun.'), buttons: [{ type: 'disabled', content: t('Shrani'), form: 'profileForm' }] } + +mainHeader(c) + + .content-hold-prerequisites + #offset-main.main-container.mt-2 + form#profileForm.needs-validation(method="post" novalidate) + div + .subject-name + label.input-name-txt(for="profile-username") #{ t('UPORABNIŠKO IME') } + .row + .col-sm-6.align-items-center + input#profile-username.name-input.form-control.d-inline( + type="text" + name="username" + disabled + value=user.userName + ) + .invalid-feedback + + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 + + .mt-4 + .subject-name + label.input-name-txt(for="profile-name") #{ t('IME') } + .row + .col-sm-6 + input#profile-name.name-input.form-control.d-inline( + type="text" + name="name" + minLength="1" + disabled + value=user.firstName + ) + .invalid-feedback #{ t('Niste vpisali imena') } + + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 + + .mt-4 + .subject-name + label.input-name-txt(for="profile-surname") #{ t('PRIIMEK') } + .row + .col-sm-6 + input#profile-surname.name-input.form-control.d-inline( + type="text" + name="surname" + minLength="1" + disabled + value=user.lastName + ) + .invalid-feedback #{ t('Niste vpisali priimka.') } + + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 + + //- .mt-4 + .subject-name + label.input-name-txt(for="profile-surname") ELEKTRONSKI NASLOV + .row + .col-sm-6 + input#profile-surname.name-input.form-control.d-inline( + type="text" + name="email" + minLength="1" + value=user.email + ) + .invalid-feedback Neveljavni elektronski naslov. + + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 + .mt-5.delete-btn-section + button.btn.btn-delete-style( + data-bs-target="#alert-modal" + data-bs-toggle="modal" + ) + img.i32x32(src="/images/exclamation-triangle-white.svg" alt="DEL") + span.ps-2.text-white #{ t('IZBRIŠI') } + include /common/footer + include /utilities/modal-alert diff --git a/express/views/pages/profile/my-profile-side-menu-mixin.pug b/express/views/pages/profile/my-profile-side-menu-mixin.pug index 998d398..372bf5b 100644 --- a/express/views/pages/profile/my-profile-side-menu-mixin.pug +++ b/express/views/pages/profile/my-profile-side-menu-mixin.pug @@ -5,27 +5,33 @@ mixin sideNavigation(sideNavigationData) button#nav-button.nav-button img#burger-menu-img( src="/images/burger-menu-button-icon.svg" - alt="Meni" + alt=t('Meni') ) - span.nav-title Moj Profil + span.nav-title #{ t('Moj Profil') } #mobile-right-holder nav - ul.admin-nav-content.slidable + ul.admin-nav-content.slidable.scroller-style li( class=sideNavigationData.selection === 'change-profile-data' ? 'focused-menu' : 'admin-nav-item' ) a.active(href="/moj-racun") - img(src="/images/user-gray.svg" alt="Basic") - p Osnovni podatki + img(src="/images/user-gray.svg" alt=t('Basic')) + p #{ t('Osnovni podatki') } li( class=sideNavigationData.selection === 'change-profile-password' ? 'focused-menu' : 'admin-nav-item' ) a.active(href="/spremeni-geslo") - img(src="/images/lock-gray.svg" alt="Settings") - p Spremeni geslo + img(src="/images/lock-gray.svg" alt=t('Settings')) + p #{ t('Spremeni geslo') } li( class=sideNavigationData.selection === 'change-profile-settings' ? 'focused-menu' : 'admin-nav-item' ) a.active(href="/nastavitve-racuna") - img(src="/images/cog.svg" alt="Settings") - p Nastavitve + img(src="/images/cog.svg" alt=t('Settings')) + p #{ t('Nastavitve') } + li( + class=sideNavigationData.selection === 'delete-profile' ? 'focused-menu' : 'admin-nav-item' + ) + a.active(href="/izbrisi-racun") + img(src="/images/user-x.svg" alt=t('Delete profile')) + p #{ t('Izbriši račun') } diff --git a/express/views/pages/profile/my-profile.pug b/express/views/pages/profile/my-profile.pug index 7c387fb..93c3944 100644 --- a/express/views/pages/profile/my-profile.pug +++ b/express/views/pages/profile/my-profile.pug @@ -11,15 +11,15 @@ block body - const d = { selection: 'change-profile-data' } +sideNavigation(d) include /utilities/dictionaries-main-panel-header - - const c = { sideMenu: true, h1: 'Osnovni podatki', description: 'Tukaj lahko spremenite ime, priimek in elektronski naslov uporabnika.', buttons: [{ type: 'disabled', content: 'Shrani', form: 'profileForm' }] } + - const c = { sideMenu: true, h1: title, description: t('Tukaj lahko spremenite ime, priimek in elektronski naslov uporabnika.'), buttons: [{ type: 'disabled', content: t('Shrani'), form: 'profileForm' }] } +mainHeader(c) .content-hold-prerequisites - #offset-main.main-container.mt-2.pe-5 + #offset-main.main-container.mt-2 form#profileForm.needs-validation(method="post" novalidate) div .subject-name - label.input-name-txt(for="profile-username") UPORABNIŠKO IME + label.input-name-txt(for="profile-username") #{ t('UPORABNIŠKO IME') } .row .col-sm-6.align-items-center input#profile-username.name-input.form-control.d-inline( @@ -35,7 +35,7 @@ block body .mt-4 .subject-name - label.input-name-txt(for="profile-name") IME + label.input-name-txt(for="profile-name") #{ t('IME') } .row .col-sm-6 input#profile-name.name-input.form-control.d-inline( @@ -44,14 +44,14 @@ block body minLength="1" value=user.firstName ) - .invalid-feedback Niste vpisali imena + .invalid-feedback #{ t('Niste vpisali imena') } .col-sm.d-flex.align-items-center span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 .mt-4 .subject-name - label.input-name-txt(for="profile-surname") PRIIMEK + label.input-name-txt(for="profile-surname") #{ t('PRIIMEK') } .row .col-sm-6 input#profile-surname.name-input.form-control.d-inline( @@ -60,14 +60,14 @@ block body minLength="1" value=user.lastName ) - .invalid-feedback Niste vpisali priimka. + .invalid-feedback #{ t('Niste vpisali priimka.') } .col-sm.d-flex.align-items-center span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 .mt-4 .subject-name - label.input-name-txt(for="profile-surname") ELEKTRONSKI NASLOV + label.input-name-txt(for="profile-surname") #{ t('ELEKTRONSKI NASLOV') } .row .col-sm-6 input#profile-surname.name-input.form-control.d-inline( @@ -76,8 +76,8 @@ block body minLength="1" value=user.email ) - .invalid-feedback Neveljavni elektronski naslov. + .invalid-feedback #{ t('Neveljavni elektronski naslov.') } .col-sm.d-flex.align-items-center span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 - include /common/footer + include /common/footer diff --git a/express/views/pages/reset-password/reset-password.pug b/express/views/pages/reset-password/reset-password.pug new file mode 100644 index 0000000..d3d7a80 --- /dev/null +++ b/express/views/pages/reset-password/reset-password.pug @@ -0,0 +1,45 @@ +extends /layout + +block pageSpecificScipts + script(nonce=cspNonce src="/javascripts/reset-password.js") + +block body + section#fixed-top-section + include /common/main-navigation + +main-navigation(false) + include /utilities/generic-main-panel-header + - let c + if (isValidToken) + - c = { h1: t('Ponastavi geslo'), description: t('Vnesite novo geslo. Ko ga potrdite, se boste v vaš uporabniški račun lahko spet prijavili z novim geslom.') } + else + - c = { h1: t('Obvestilo'), description: '' } + + +mainHeader(c) + .content-hold-prerequisites.ps-3.pe-3 + #offset-main.container-fluid.mt-4.errc.px-0 + .p-squeeze-lg + .rpm-corr + if (isValidToken) + // h1.navigation-text-color.mt-3 Ponastavi Geslo + form#reset-and-redirect.max-512px( + action="/reset-password" + method="POST" + ) + input#reset-password.mb-3.form-control( + type="password" + placeholder=t('Geslo') + ) + input#reset-password-repeat.mb-3.form-control( + type="password" + placeholder=t('Ponovi Geslo') + ) + .d-flex.justify-content-between + button#cancel-btn.btn.btn-secondary.h-40px(type="button") #{ t('Prekliči') } + button#reset-password-btn.btn.btn-primary.h-40px(type="submit") #{ t('Potrdi') } + input#token.d-none(value=token) + else + //h1.navigation-text-color.mt-3 Obvestilo + p #{ t('Povezava ni (več) veljavna. Prosimo, da ponovno zahtevate ponastavitev gesla.') } + + include /utilities/modal-reset-password-success + include /common/footer diff --git a/express/views/pages/search/no-results.pug b/express/views/pages/search/no-results.pug index 2ecb7cb..2be043e 100644 --- a/express/views/pages/search/no-results.pug +++ b/express/views/pages/search/no-results.pug @@ -18,9 +18,14 @@ block body .ps-lg-5.pe-lg-4 .p-squeeze-lg.text-center img.mx-auto(src="/images/search-error.svg" alt="Search Error") - h1.text-blue-info-title Ni zadetkov - p.pb-5.mt-5.px-10-large.text-center Vaše iskanje ni bilo uspešno. Vpišite novo iskalno poizvedbo in poizkusite znova. - //- p.mt-5.px-20-large.h-41 - button.btn.btn-secondary.d-inline-block.h-41.mx-2 Opcija A - button.btn.btn-primary.d-inline-block.h-41.mx-2 Opcija B + h1.text-blue-info-title #{ t("Ni zadetkov") } + p.pb-5.mt-5.px-10-large.text-center #{ t("Vaše iskanje ni bilo uspešno. Vpišite novo iskalno poizvedbo in poizkusite znova.") } + //- p.mt-5.px-20-large.h-41px + button.btn.btn-secondary.d-inline-block.h-41px.mx-2 Opcija A + button.btn.btn-primary.d-inline-block.h-41px.mx-2 Opcija B + .mt-4.row + .col.d-flex.justify-content-center + if (consultancyHits > 0) + #consulancy-results-container + include /components/search-and-filter/consultancy-results-content include /common/footer diff --git a/express/views/pages/search/result-detail-dictionary.pug b/express/views/pages/search/result-detail-dictionary.pug index 3ad7779..a38dd10 100644 --- a/express/views/pages/search/result-detail-dictionary.pug +++ b/express/views/pages/search/result-detail-dictionary.pug @@ -4,6 +4,7 @@ block pageSpecificScipts script(nonce=cspNonce src="/javascripts/search-results.js") script(nonce=cspNonce src="/javascripts/search-results-content.js") script(nonce=cspNonce src="/javascripts/comments.js") + script(nonce=cspNonce src="/javascripts/collapsable-comments.js") block body section#fixed-top-section @@ -16,7 +17,7 @@ block body //- include /utilities/dictionaries-main-panel-header include /utilities/dictionary-info-panel - - const c = { sideMenu: true, h2: '', h1: finalData.dictName, description: '', buttons: [{ type: 'link', url: `/iskanje?q=*&d=${dictId}`, content: 'Išči po slovarju' }] } + - const c = { sideMenu: true, h2: '', h1: finalData.dictName, description: '', buttons: [{ type: 'link', url: `/iskanje?q=*&d=${dictId}`, content: t('Išči po slovarju') }] } +mainHeader(c) //- empty header for code to work @@ -32,13 +33,14 @@ block body //- span SL //- .col-md-9 .col - span!= finalData.description ? finalData.description : 'Ta slovar nima opisa' + span!= finalData.description ? finalData.description : t('Ta slovar nima opisa') //- .col-md-2 .result-comment-section - .comments-content.col-12.col-md-12.py-md-3.bd-content + .comments-content.col-12.col-md-12.pt-md-3.bd-content #offset-main - include /utilities/comments-with-pager + //- include /utilities/comments-with-pager + include /utilities/comments-with-collapsable-pager - include /common/footer + include /common/footer diff --git a/express/views/pages/search/result-detail.pug b/express/views/pages/search/result-detail.pug index 855c9f7..3fbfe64 100644 --- a/express/views/pages/search/result-detail.pug +++ b/express/views/pages/search/result-detail.pug @@ -4,6 +4,7 @@ block pageSpecificScipts script(nonce=cspNonce src="/javascripts/search-results.js") script(nonce=cspNonce src="/javascripts/search-results-content.js") script(nonce=cspNonce src="/javascripts/comments.js") + script(nonce=cspNonce src="/javascripts/collapsable-comments.js") block body section#fixed-top-section @@ -21,7 +22,7 @@ block body #offset-main.main-container.ps-1.mt-2 #chevrons-left.d-flex .header-container-divider-left - h1#site-heading O terminu + h1#site-heading #{ t("O terminu") } hr#disposable-break.mt-0 .d-flex.mb-4.w-100.flex-column @@ -42,8 +43,10 @@ block body +entryPreview .result-comment-section - .comments-content.col-12.col-md-12.py-md-3.bd-content + .comments-content.col-12.col-md-12.pt-md-3.mb-0.pb-0.bd-content #offset-main - include /utilities/comments-with-pager + //- include /utilities/comments-with-pager + include /utilities/comments-with-collapsable-pager + //- +pager - include /common/footer + include /common/footer diff --git a/express/views/pages/search/results.pug b/express/views/pages/search/results.pug index 537b5e1..e9ed06c 100644 --- a/express/views/pages/search/results.pug +++ b/express/views/pages/search/results.pug @@ -19,9 +19,9 @@ block body #offset-padding.offset-padding.header-section-root .content-hold-prerequisites.mt-4 - #offset-main.main-container.ps-1.mt-2 + #offset-main.main-container.ps-1 .d-flex.mb-4.w-100 include /components/search-and-filter/result-panel +result-panel(entries) - include /common/footer + include /common/footer diff --git a/express/views/pages/search/special-keys-demo.pug b/express/views/pages/search/special-keys-demo.pug index 444dd06..287c140 100644 --- a/express/views/pages/search/special-keys-demo.pug +++ b/express/views/pages/search/special-keys-demo.pug @@ -1,3 +1,5 @@ +//- TODO MARK FOR DELETION + extends /layout block pageSpecificScipts diff --git a/express/views/pages/terms-of-use_en.pug b/express/views/pages/terms-of-use_en.pug new file mode 100644 index 0000000..d971d76 --- /dev/null +++ b/express/views/pages/terms-of-use_en.pug @@ -0,0 +1,20 @@ +extends /layout + +block body + section.sticky-top + include /common/main-navigation + +main-navigation(false) + .consultancy-padding-main.p-squeeze-lg.mt-2 + h1 TERMS OF USE - SLOVENIAN TERMINOLOGY PORTAL + p Welcome to Slovenian Terminology Portal, which was developed as part of the #[i Development of Slovene in a Digital Environment Project], co-financed by the Ministry of Culture of the Republic of Slovenia and the European Regional Development Fund. + p The website was created to allow integrated use and editing of Slovenian terminology resources in one place. + p We ask all visitors of our website to comply with the Terms of Use listed below. By visiting this website, you agree to accept the Terms of Use in their entirety. + p Users can use the Slovenian Terminology Portal under the #[a(href="https://creativecommons.org/licenses/by/4.0/legalcode.sl" target="_blank") CC BY 4.0] licence. All resources you edit and publish on the Slovenian Terminology Portal will also be available to other registered users under the CC BY 4.0 licence. + .mt-2 + h2 Personal Data Protection + p Slovenian Terminology Portal uses cookies. You can read more about how we handle personal data in our #[a(href="/politika-zasebnosti" target="_blank") Privacy Policy]. + p Thank you for visiting Slovenian Terminology Portal. + .mt-2 + h2 Contact + p If you have any questions related to our Terms of Use, please contact us at #[a(href="mailto:info@terminoloski.slovenscina.eu") info@terminoloski.slovenscina.eu]. + include /common/footer diff --git a/express/views/pages/terms-of-use.pug b/express/views/pages/terms-of-use_sl.pug similarity index 87% rename from express/views/pages/terms-of-use.pug rename to express/views/pages/terms-of-use_sl.pug index 3272dc6..f148627 100644 --- a/express/views/pages/terms-of-use.pug +++ b/express/views/pages/terms-of-use_sl.pug @@ -14,5 +14,7 @@ block body h2 Varstvo osebnih podatkov p Slovenski terminološki portal uporablja piškotke. Več o obdelavi osebnih podatkov je navedeno v #[a(href="/politika-zasebnosti" target="_blank") Politiki zasebnosti]. p Zahvaljujemo se vam za obisk. - + .mt-2 + h2 Kontakt + p Za kakršnakoli vprašanja v povezavi s pogoji uporabe nas, prosimo, kontaktirajte na #[a(href="mailto:info@terminoloski.slovenscina.eu") info@terminoloski.slovenscina.eu]. include /common/footer diff --git a/express/views/utilities/comments-input-text.pug b/express/views/utilities/comments-input-text.pug index 68b8e41..53ef6b9 100644 --- a/express/views/utilities/comments-input-text.pug +++ b/express/views/utilities/comments-input-text.pug @@ -6,20 +6,23 @@ if user textarea#comment-message-input( contenteditable="" type="text" - placeholder="Dodaj komentar..." + placeholder=t('Dodaj komentar...') name="comment" ) .verticalLine.mt-1.mb-1 button#comment-submit-btn(type="button") - img.img-comment-svg(src="/images/comment.svg" alt="Pošlji") - | Pošlji + img.img-comment-svg(src="/images/comment.svg" alt=t('Pošlji')) + = t('Pošlji') #reply-form-container.hide form#comment-reply-form.comment-reply-form input#comment-quote-id(type="hidden" name="quoteId") #reply-circle.circle.comment-initials-container IP - textarea#comment-reply-input(placeholder="Dodaj odgovor..." type="text") + textarea#comment-reply-input( + placeholder=t('Dodaj odgovor...') + type="text" + ) .verticalLine.mt-1.mb-1 button#comment-submit-reply-btn(type="button") - img(src="/images/fi_down_left.svg" alt="Odgovori") - | Odgovori + img(src="/images/fi_down_left.svg" alt=t('Odgovori')) + = t('Odgovori') diff --git a/express/views/utilities/comments-pager.pug b/express/views/utilities/comments-pager.pug index f056542..66cb067 100644 --- a/express/views/utilities/comments-pager.pug +++ b/express/views/utilities/comments-pager.pug @@ -1,3 +1,5 @@ +//- TODO MARK FOR DELETION + .comments-pager span#comments-count.comment-count span diff --git a/express/views/utilities/comments-with-collapsable-pager.pug b/express/views/utilities/comments-with-collapsable-pager.pug index 5e7bfab..edbb7ce 100644 --- a/express/views/utilities/comments-with-collapsable-pager.pug +++ b/express/views/utilities/comments-with-collapsable-pager.pug @@ -1,12 +1,12 @@ .pager-holder .comments-pager button#collapseComments.no-bg.no-border - span.navigation-text-color.fw-500 Komentarji - img#dropImage(src="images/arrow_drop_down.svg") + span.navigation-text-color.fw-500 #{ t('Komentarji') } + img#dropImage(src="/images/arrow_drop_down.svg") span#comments-count.comment-count.d-flex.ms-auto.me-4.text-p875rem include /utilities/pager +pager - hr.comments-top-hr.mt-3 + hr.collapable-hr.comments-top-hr.mt-3 .d-flex.flex-row.w-100.mb-4 ul#comments-container.comments-container.pe-1 include /utilities/comments-input-text diff --git a/express/views/utilities/consultancy-admin-panel-header.pug b/express/views/utilities/consultancy-admin-panel-header.pug index 71700af..badddb1 100644 --- a/express/views/utilities/consultancy-admin-panel-header.pug +++ b/express/views/utilities/consultancy-admin-panel-header.pug @@ -26,9 +26,9 @@ mixin mainHeader(headerContent, footerIncluded = true) .row .col-sm-6.align-items-center.d-flex if (entries.length>0) - p.mb-0.text-p875rem.strength500.text-header-title-gray Število vprašanj: #{ queryCount } + p.mb-0.text-p875rem.strength500.text-header-title-gray #{ t('Število vprašanj:') } #{ queryCount } else - p.mb-0.text-p875rem.strength500.text-header-title-gray Ni vprašanj. + p.mb-0.text-p875rem.strength500.text-header-title-gray #{ t('Ni vprašanj.') } .col-sm-6.align-items-center.justify-content-end.d-flex .col-sm-6.align-items-center.justify-content-end.d-flex .col-sm-6.align-items-center.justify-content-end.d-flex diff --git a/express/views/utilities/consultancy-main-panel-header.pug b/express/views/utilities/consultancy-main-panel-header.pug index 70ac588..945aee7 100644 --- a/express/views/utilities/consultancy-main-panel-header.pug +++ b/express/views/utilities/consultancy-main-panel-header.pug @@ -3,13 +3,13 @@ mixin topHeaderContent(headerContent) .header-container-divider-left h2#site-header-title= headerContent.h2 h1#site-heading= headerContent.h1 - span#text-description.page-description.pe-0= headerContent.description + span#text-description.page-description.pe-0!= headerContent.description if (headerContent.helpLink) a.ms-2.page-description(href=headerContent.helpLink.linkHref)= headerContent.helpLink.linkText mixin mainHeader(headerContent, footerIncluded = true) - const checkForSpecificRights = headerContent.specificRights - #offset-padding.header-section-root.no-side-menu.consultancy-padding-main.flex-grow-0.p-squeeze-lg.mx-xxl-auto + #offset-padding.header-section-root.no-side-menu.consultancy-padding-main.flex-grow-0.p-squeeze-lg.mx-xxl-auto.gray-bg.pb-3 .d-flex.justify-content-between.flex-wrap.flex-row.row if checkForSpecificRights .header-container-left-side.d-flex.col-sm-10 @@ -25,7 +25,7 @@ mixin mainHeader(headerContent, footerIncluded = true) .header-consultancy-content.w-100 include ../components/search-and-filter/search-with-primary-domain-filter - +searchWithPrimaryDomainFilter("consultancy-form", "searchbar-main", "couns-search-btn") + +searchWithPrimaryDomainFilter("consultancy-form", "searchbar-main", "couns-search-btn", "mt-3", queryKey) .header-consultancy-divider-right.d-flex.align-items-center.justify-content-end.col-sm-5.pe-0.ps-3 .fit-content if (headerContent.buttons && headerContent.buttons.length) @@ -48,17 +48,17 @@ mixin mainHeader(headerContent, footerIncluded = true) .col-sm-6.align-items-center.d-flex if (entries.length>0) if (resultAmountDisplay) - p.mb-0.text-p875rem.strength500.text-header-title-gray Odgovori na vprašanja: #{ queryCount } + p.mb-0.text-p875rem.strength500.text-header-title-gray #{ t('Odgovori na vprašanja:') } #{ queryCount } else - p.mb-0.text-p875rem.strength500.text-header-title-gray Zadnji odgovori na vprašanja + p.mb-0.text-p875rem.strength500.text-header-title-gray #{ t('Zadnji odgovori na vprašanja') } else - p.mb-0.text-p875rem.strength500.text-header-title-gray Ni vprašanj. + p.mb-0.text-p875rem.strength500.text-header-title-gray #{ t('Ni vprašanj.') } .col-sm-6.align-items-center.justify-content-end.d-flex .col-sm-5.align-items-center.justify-content-end.d-flex //- if headerContent.exportButtonPresent button#export-consultancy-info.btn.btn-secondary img(src="/images/download.svg") - span.ps-2 Izvozi + span.ps-2 #{ t('Izvozi') } if (!headerContent.show5MostRecent) .col-sm-7.align-items-center.justify-content-end.d-flex include /utilities/pager diff --git a/express/views/utilities/content-classic-overview.pug b/express/views/utilities/content-classic-overview.pug index 7715aeb..0141d06 100644 --- a/express/views/utilities/content-classic-overview.pug +++ b/express/views/utilities/content-classic-overview.pug @@ -4,12 +4,12 @@ .col-12.col-sm-3.flex-fill.me-2 span.term-id.normal-gray-label .col-12.col-sm-3.flex-fill - span#preview-version.normal-gray-label Verzija 1 + span#preview-version.normal-gray-label= t('Verzija 1') .d-sm-flex.col-xl-6.col-12.align-items-center .col-12.col-sm-3.flex-fill - span#preview-author.normal-gray-label Ime Priimek + span#preview-author.normal-gray-label= t('Ime Priimek') .col-12.col-sm-3.flex-fill.ms-sm-2 - span.normal-gray-label Zaporedje: + span.normal-gray-label= t('Zaporedje:') input#preview-homonym.ms-2.homonym-field.form-control.d-inline( type="text" disabled @@ -21,9 +21,9 @@ select#status-overview.form-select.select-content-state.w-auto( disabled ) - option(value="preposition" disabled) Predlog - option(value="edited") Urejeno - option(value="in-editing") V urejanju + option(value="preposition" disabled)= t('Predlog') + option(value="edited")= t('Urejeno') + option(value="in-editing")= t('V urejanju') .col-12.col-sm-3.flex-fill .form-check.form-switch @@ -32,7 +32,7 @@ name="isPublished" disabled ) - label.normal-gray-label(for="") Objavljeno + label.normal-gray-label(for="")= t('Objavljeno') .d-sm-flex.col-xl-6.col-12.align-items-center .col-12.col-sm-3.flex-fill @@ -41,29 +41,29 @@ name="" disabled ) - label.normal-gray-label(for="") Strokovno pregledano + label.normal-gray-label(for="")= t('Strokovno pregledano') .col-12.col-sm-3.flex-fill.ms-sm-2 input#language-overview.form-check-input.special-cbox.me-2( type="checkbox" name="" disabled ) - label.normal-gray-label(for="") Jezikovno pregledano + label.normal-gray-label(for="")= t('Jezikovno pregledano') .d-flex.justify-content-between.align-items-center.collapsible-data .collapse-content .d-flex.collapsed-text.align-items-center .white-background - span.status-collapsed.normal-gray.m-2 V urejanju + span.status-collapsed.normal-gray.m-2= t('V urejanju') .published-collapsed.d-flex.d-none span.normal-gray.ms-2 | - span.normal-gray.ms-2 Objavljeno + span.normal-gray.ms-2= t('Objavljeno') .terminology-collapsed.d-flex.d-none span.normal-gray.ms-2 | - span.normal-gray.ms-2 Strokovno pregledano + span.normal-gray.ms-2= t('Strokovno pregledano') .lang-collapsed.d-flex.d-none span.normal-gray.ms-2 | - span.normal-gray.ms-2 Jezikovno pregledano + span.normal-gray.ms-2= t('Jezikovno pregledano') .homonim-collapse-div.d-flex.d-none span.normal-gray.ms-2 | span.homonim-collapsed.normal-gray.ms-2 @@ -83,7 +83,7 @@ .d-grid .d-flex.align-items-center img(src="/images/alert-triangle-dark-red.svg") - span.ms-2.normal-red-note Slovarski sestavek je neveljaven + span.ms-2.normal-red-note= t('Slovarski sestavek je neveljaven') ul.mt-2.ms-4.mb-2 - li.error-type-1.heavy-gray-text.d-none Tip napake 1 (ni slovenskega termina) - li.error-type-2.heavy-gray-text.d-none Tip napake 2 (ni definicije ali tujejezičnega termina) + li.error-type-1.heavy-gray-text.d-none= t('Tip napake 1 (ni slovenskega termina)') + li.error-type-2.heavy-gray-text.d-none= t('Tip napake 2 (ni definicije ali tujejezičnega termina)') diff --git a/express/views/utilities/content-date-scroller.pug b/express/views/utilities/content-date-scroller.pug index 36bcf29..a439012 100644 --- a/express/views/utilities/content-date-scroller.pug +++ b/express/views/utilities/content-date-scroller.pug @@ -11,10 +11,6 @@ ) label#latest-version-label.btn-outline-dates.latest-version-el.btn.ms-0.date-element( for="latest-radio" - data-bs-custom-class="dark-gray-tooltip" - data-bs-toggle="tooltip" - data-bs-placement="bottom" - title="Pokaži verzijo" - ) Zadnja verzija + ) #go-right.d-flex.align-items-center.me-2.ms-auto.ps-2 img(src="/images/chevron-right-2.svg" alt="") diff --git a/express/views/utilities/content-edit.pug b/express/views/utilities/content-edit.pug index 6ff8918..4b1c67e 100644 --- a/express/views/utilities/content-edit.pug +++ b/express/views/utilities/content-edit.pug @@ -13,12 +13,12 @@ form#form-edit-content.needs-validatio.mt-2( .col-12.col-sm-3.flex-fill.me-2 span.term-id.normal-gray-label ID: .col-12.col-sm-3.flex-fill - span#edit-version-el.normal-gray-label Verzija 1 + span#edit-version-el.normal-gray-label= t('Verzija 1') .d-sm-flex.col-xl-6.col-12.align-items-center .col-12.col-sm-3.flex-fill - span#edit-name-el.normal-gray-label Ime Priimek + span#edit-name-el.normal-gray-label= t('Ime Priimek') .col-12.col-sm-3.flex-fill.ms-sm-2 - span.normal-gray-label Zaporedje: + span.normal-gray-label= t('Zaporedje:') input#homonym-sort.ms-2.homonym-field.form-control.d-inline( type="text" name="homonymSort" @@ -30,9 +30,9 @@ form#form-edit-content.needs-validatio.mt-2( select#status-edit.form-select.select-content-state.w-auto( name="status" ) - option(value="suggestion" disabled) Predlog - option(value="in_edit") V urejanju - option(value="complete") Urejeno + option(value="suggestion" disabled)= t('Predlog') + option(value="in_edit")= t('V urejanju') + option(value="complete")= t('Urejeno') .col-12.col-sm-3.flex-fill .form-check.form-switch @@ -40,20 +40,20 @@ form#form-edit-content.needs-validatio.mt-2( type="checkbox" name="isPublished" ) - label.normal-gray-label(for="published-cbox") Objavljeno + label.normal-gray-label(for="published-cbox")= t('Objavljeno') .d-sm-flex.col-xl-6.col-12.align-items-center .col-12.col-sm-3.flex-fill input#terminology-cbox.form-check-input.me-2( type="checkbox" name="isTerminologyReviewed" ) - label.normal-gray-label(for="terminology-cbox") Strokovno pregledano + label.normal-gray-label(for="terminology-cbox")= t('Strokovno pregledano') .col-12.col-sm-3.flex-fill.ms-sm-2 input#language-cbox.form-check-input.me-2( type="checkbox" name="isLanguageReviewed" ) - label.normal-gray-label(for="language-cbox") Jezikovno pregledano + label.normal-gray-label(for="language-cbox")= t('Jezikovno pregledano') .d-flex.justify-content-between.align-items-center.collapsible-data .collapse-content .d-flex.collapsed-text.align-items-center @@ -61,13 +61,13 @@ form#form-edit-content.needs-validatio.mt-2( span.status-collapsed.normal-gray.m-2 .published-collapsed.d-flex.d-none span.normal-gray.ms-2 | - span.normal-gray.ms-2 Objavljeno + span.normal-gray.ms-2= t('Objavljeno') .terminology-collapsed.d-flex.d-none span.normal-gray.ms-2 | - span.normal-gray.ms-2 Strokovno pregledano + span.normal-gray.ms-2= t('Strokovno pregledano') .lang-collapsed.d-flex.d-none span.normal-gray.ms-2 | - span.normal-gray.ms-2 Jezikovno pregledano + span.normal-gray.ms-2= t('Jezikovno pregledano') .homonim-collapse-div.d-flex.d-none span.normal-gray.ms-2 | span.homonim-collapsed.normal-gray.ms-2 @@ -87,10 +87,10 @@ form#form-edit-content.needs-validatio.mt-2( .d-grid .d-flex.align-items-center img(src="/images/alert-triangle-dark-red.svg") - span.ms-2.normal-red-note Slovarski sestavek je neveljaven + span.ms-2.normal-red-note= t('Slovarski sestavek je neveljaven') ul.mt-2.ms-4.mb-2 - li.error-type-1.heavy-gray-text.d-none Tip napake 1 (ni slovenskega termina) - li.error-type-2.heavy-gray-text.d-none Tip napake 2 (ni definicije ali tujejezičnega termina) + li.error-type-1.heavy-gray-text.d-none= t('Tip napake 1 (ni slovenskega termina)') + li.error-type-2.heavy-gray-text.d-none= t('Tip napake 2 (ni definicije ali tujejezičnega termina)') .col.note-section-line.me-1.my-1.d-none hr.my-0 input#hidden-dictionary-id.hidden-input-dict-id( @@ -99,8 +99,8 @@ form#form-edit-content.needs-validatio.mt-2( value=dictionaryId ) .term-input-group.mt-xl-4.mt-3 - .subject-name.col-sm-6.d-flex.justify-content-between - label.input-name-txt(for="term-name") TERMIN + .subject-name.col-sm-12.col-xl-8.col-xxl-6.d-flex.justify-content-between + label.input-name-txt(for="term-name")= t('TERMIN') include /utilities/mixed-content - const type = 'two' +mixedContentBtns @@ -112,9 +112,9 @@ form#form-edit-content.needs-validatio.mt-2( ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt Vnesite termin. + span.d-md-inline.d-block.name-info-txt= t('Vnesite termin.') - #create-headword-group.mt-sm-3.mt-2 + //- #create-headword-group.mt-sm-3.mt-2 .subject-name label.input-name-txt(for="btn-new-headword-grp") OBLIKE, NAGLASI, IZGOVOR .row @@ -127,22 +127,22 @@ form#form-edit-content.needs-validatio.mt-2( #created-headword-group.mt-sm-4.mt-2.d-none .subject-name - span.input-name-txt Oblike, naglasi, izgovor + span.input-name-txt= t('Oblike, naglasi, izgovor') .col-sm-12.col-xl-8.col-xxl-6 .table-responsive table#headword-table.table-striped.table tr td#term-key td - a(href="") Oblike + a(href="")= t('Oblike') td - a(href="") Naglas + a(href="")= t('Naglas') td - a(href="") Izgovor + a(href="")= t('Izgovor') if structure.hasDomainLabels .title.mt-sm-4.mt-2 .subject-name - label.input-name-txt(for="headword-group") PODROČNE OZNAKE + label.input-name-txt(for="headword-group")= t('PODROČNE OZNAKE') .row .col-sm-12.col-xl-8.col-xxl-6 select#domain-secondary.name-input.d-inline.form-control.multiple( @@ -154,24 +154,24 @@ form#form-edit-content.needs-validatio.mt-2( option(value=domain.id)= domain.name .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Vstavite ustrezno področno oznako, ki jo želite določiti za posamezni termin. + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Vstavite ustrezno področno oznako, ki jo želite določiti za posamezni termin.') if structure.hasLabel .title.mt-sm-4.mt-2 - .subject-name.col-sm-6.d-flex.justify-content-between - label.input-name-txt(for="explanation-field") POJASNILO + .subject-name.col-sm-12.col-xl-8.col-xxl-6.d-flex.justify-content-between + label.input-name-txt(for="explanation-field")= t('POJASNILO') +mixedContentBtns(type) .row .col-sm-12.col-xl-8.col-xxl-6 - textarea#explanation-field.form-control.explanation-field.mc-field( + textarea#explanation-field.form-control.explanation-field.mc-field.dispatch-tab( type="text" name="label" ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Pojasnilo ... + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Dodajte podatek o zunajjezikovnih okoliščinah, ki niso povezane s pojmom, npr. letnico.') if structure.hasDefinition .title.mt-sm-4.mt-2 - .subject-name.col-sm-6.d-flex.justify-content-between - label.input-name-txt(for="definition-field") DEFINICIJA + .subject-name.col-sm-12.col-xl-8.col-xxl-6.d-flex.justify-content-between + label.input-name-txt(for="definition-field")= t('DEFINICIJA') +mixedContentBtns(type) .row .col-sm-12.col-xl-8.col-xxl-6 @@ -180,11 +180,11 @@ form#form-edit-content.needs-validatio.mt-2( name="definition" ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Vstavite definicijo termina. + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Vstavite definicijo pojma.') if structure.hasSynonyms .title.mt-sm-4.mt-2 .subject-name.col-sm-6.d-flex.justify-content-between - label.input-name-txt(for="synonyms-input") SINONIMI + label.input-name-txt(for="synonyms-input")= t('SINONIMI') //- +mixedContentBtns .row .col-sm-12.col-xl-8.col-xxl-6 @@ -193,47 +193,47 @@ form#form-edit-content.needs-validatio.mt-2( multiple ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Vstavite sinonime, ki se za termin še uporabljajo. + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Vstavite termine, ki se za definirani pojem tudi uporabljajo.') if structure.hasLinks .title.mt-sm-4.mt-2 - .subject-name.col-sm-6.d-flex.justify-content-between - label.input-name-txt(for="connections") POVEZANI TERMIN + .subject-name.col-12.col-xl-8.col-xxl-6.d-flex.justify-content-between + label.input-name-txt(for="connections")= t('POVEZANI TERMIN') +mixedContentBtns .row.align-items-center.link-row - .col-xl-2.col-6 + .col-xxl-2.col-xl-3.col-4 select.name-input.form-select.d-inline( type="text" name="type" placeholder="Tip" ) - option(value="related") Sorodni - option(value="broader") Širši - option(value="narrow") Ožji - .col-sm-12.col-xl-4 - input#connections.name-input.form-control.d-inline.mc-field( + option(value="related")= t('Sorodni') + option(value="broader")= t('Širši') + option(value="narrow")= t('Ožji') + .col-8.col-xl-5.col-xxl-4 + input#connections.name-input.form-control.d-inline.mc-field.dispatch-tab( type="text" name="links" maxlength="120" ) .col.d-none.d-xl-flex.align-items-cente - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Povezani termin ... + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Dodajte termin, ki je sicer definiran v samostojnem slovarskem sestavku, vendar je povezan s terminom, ki ga opisujete v tem slovarskem sestavku.') #add-new-connection.mt-sm-4.mt-2 .subject-name - label.input-name-txt(for="input-new-connection") DODATNI POVEZANI TERMIN + label.input-name-txt(for="input-new-connection")= t('DODATNI POVEZANI TERMIN') .row .col-sm-12.col-xl-8.col-xxl-6 button#input-new-connection.input-new-btn.form-control(type="button") - span.new-author-text-btn Dodaj povezani termin. + span.new-author-text-btn= t('Dodaj povezani termin.') .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Dodajte termin, ki je sicer definiran v samostojnem slovarskem sestavku, vendar je povezan s terminom, ki ga opisujete v tem slovarskem sestavku. + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 if structure.hasOther .title.mt-sm-4.mt-2 - .subject-name.col-sm-6.d-flex.justify-content-between - label.input-name-txt(for="other-field") DRUGO + .subject-name.col-sm-12.col-xl-8.col-xxl-6.d-flex.justify-content-between + label.input-name-txt(for="other-field")= t('DRUGO') - const br = 'br' +mixedContentBtns(br) .row @@ -243,12 +243,12 @@ form#form-edit-content.needs-validatio.mt-2( name="other" ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Definicija ... + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Vnesite podatke, ki niso sistemsko vključeni v druga polja, npr. vir, zgled rabe.') if structure.hasForeignLanguages #foreign-languages-group .mt-sm-4.mt-2 - span.input-name-txt TUJI JEZIKI + span.input-name-txt= t('TUJI JEZIKI') .languages-group-white.pb-3.pt-1.mt-2 if languages.length each language, index in languages @@ -260,8 +260,8 @@ form#form-edit-content.needs-validatio.mt-2( name=`foreign[${language.id}][code]` value=language.code ) - .subject-name.mt-2.col-sm-6.d-flex.justify-content-between - label.input-name-txt.mt-2(for="foreign-term") TERMINI + .subject-name.mt-2.col-sm-12.col-xl-8.col-xxl-6.d-flex.justify-content-between + label.input-name-txt.mt-2(for="foreign-term")= t('TERMINI') +mixedContentBtns .row .col-sm-12.col-xl-8.col-xxl-6 @@ -270,12 +270,12 @@ form#form-edit-content.needs-validatio.mt-2( multiple ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Dodajte tujejezični ustreznik. + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Dodajte tujejezični ustreznik.') if structure.hasForeignDefinitions .mt-sm-4.mt-2 - .subject-name.d-flex.justify-content-between.col-sm-6 - label.input-name-txt DEFINICIJA + .subject-name.col-sm-12.col-xl-8.col-xxl-6.d-flex.justify-content-between.col-sm-6 + label.input-name-txt= t('DEFINICIJA') +mixedContentBtns(type) .row .col-sm-12.col-xl-8.col-xxl-6 @@ -284,11 +284,11 @@ form#form-edit-content.needs-validatio.mt-2( name=`foreign[${language.id}][definition]` ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Vpišite definicijo v tujem jeziku. + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Vpišite definicijo v tujem jeziku.') if structure.hasForeignSynonyms .mt-sm-4.mt-2 .subject-name.d-flex.justify-content-between.col-sm-6 - label.input-name-txt.mt-2 SINONIMI + label.input-name-txt.mt-2= t('SINONIMI') //- +mixedContentBtns .row.mb-4 .col-sm-12.col-xl-8.col-xxl-6 @@ -297,12 +297,12 @@ form#form-edit-content.needs-validatio.mt-2( multiple ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Dodajte tujejezične ustreznike, ki se za opisani termin tudi uporabljajo v tujem jeziku. + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Dodajte tujejezične ustreznike, ki se za opisani pojem tudi uporabljajo v tujem jeziku.') else .language.px-3 span.bold-blue-text= languages.nameSl .subject-name.d-flex.flex-column.mt-2 - label.input-name-txt.mt-2(for="foreign-term") TERMINI + label.input-name-txt.mt-2(for="foreign-term")= t('TERMINI') .row .col-sm-12.col-xl-8.col-xxl-6 select.name-input.d-inline.form-control.without-dropdown( @@ -310,15 +310,15 @@ form#form-edit-content.needs-validatio.mt-2( multiple ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Dodajte tujejezični ustreznik. + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Dodajte tujejezični ustreznik.') if structure.hasImages || structure.hasAudio || structure.hasVideo .mt-sm-4.mt-2 - span.input-name-txt MULTIMEDIJA + span.input-name-txt= t('MULTIMEDIJA') .multimedia-section.px-3.pb-3.pt-1 if structure.hasImages .mt-3 .subject-name - label.input-name-txt(for="image-input-field") SLIKA + label.input-name-txt(for="image-input-field")= t('SLIKA') .row .col-sm-12.col-xl-8.col-xxl-6 input#image-input-field.form-control.d-inline( @@ -327,21 +327,21 @@ form#form-edit-content.needs-validatio.mt-2( placeholder="URL" ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Slika ... + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Dodajte povezavo do slike.') #add-new-image.mt-sm-4.mt-2 .subject-name - label.input-name-txt(for="input-new-image") NOVA SLIKA + label.input-name-txt(for="input-new-image")= t('NOVA SLIKA') .row .col-sm-12.col-xl-8.col-xxl-6 button#input-new-image.input-new-btn.form-control(type="button") - span.new-author-text-btn Nova slika + span.new-author-text-btn= t('Nova slika') .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Dodaj video ... + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 if structure.hasAudio .mt-sm-4.mt-2 .subject-name - label.input-name-txt(for="audio-input-field") ZVOK + label.input-name-txt(for="audio-input-field")= t('ZVOK') .row .col-sm-12.col-xl-8.col-xxl-6 input#audio-input-field.form-control.d-inline( @@ -350,21 +350,21 @@ form#form-edit-content.needs-validatio.mt-2( placeholder="URL" ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Zvok ... + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Dodajte povezavo do zvočnega posnetka.') #add-new-audio.mt-sm-4.mt-2 .subject-name - label.input-name-txt(for="input-new-audio") NOV ZVOK + label.input-name-txt(for="input-new-audio")= t('NOV ZVOK') .row .col-sm-12.col-xl-8.col-xxl-6 button#input-new-audio.input-new-btn.form-control(type="button") - span.new-author-text-btn Nov zvok + span.new-author-text-btn= t('Nov zvok') .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Dodaj zvok ... + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 if structure.hasVideo .mt-sm-4.mt-2 .subject-name - label.input-name-txt(for="video-input-field") VIDEO + label.input-name-txt(for="video-input-field")= t('VIDEO') .row .col-sm-12.col-xl-8.col-xxl-6 input#video-input-field.form-control.d-inline( @@ -373,14 +373,14 @@ form#form-edit-content.needs-validatio.mt-2( placeholder="URL" ) .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Video ... + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0= t('Dodajte povezavo do videa.') #add-new-video.mt-sm-4.mt-2 .subject-name - label.input-name-txt(for="input-new-video") NOV VIDEO + label.input-name-txt(for="input-new-video")= t('NOV VIDEO') .row .col-sm-12.col-xl-8.col-xxl-6 button#input-new-video.input-new-btn.form-control(type="button") - span.new-author-text-btn Nov video + span.new-author-text-btn= t('Nov video') .col.d-none.d-xl-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 Dodaj video ... + span.d-md-inline.d-block.name-info-txt.mt-3.mt-sm-0 diff --git a/express/views/utilities/content-header.pug b/express/views/utilities/content-header.pug index 7810bb1..f075d4b 100644 --- a/express/views/utilities/content-header.pug +++ b/express/views/utilities/content-header.pug @@ -2,18 +2,19 @@ .d-flex.justify-content-between.flex-wrap.flex-md-nowrap .cont-header-container-left-side.d-flex #chevrons-left.d-flex.align-items-start.justify-content-start + //- TODO I18n .header-container-divider-left h1.ms-1= dictionaryName .header-container-divider-right .d-xxl-flex.justify-content-between .d-flex.site-links - button#content-preview.site-link.active-site-link Predogled + button#content-preview.site-link.active-site-link= t('Predogled') if terms.length - button#content-edit.site-link Urejanje - button#content-comments.site-link Komentarji + button#content-edit.site-link= t('Urejanje') + button#content-comments.site-link= t('Komentarji') else - button#content-edit.site-link.disabled(disabled) Urejanje - button#content-comments.site-link.disabled(disabled) Komentarji + button#content-edit.site-link.disabled(disabled)= t('Urejanje') + button#content-comments.site-link.disabled(disabled)= t('Komentarji') if (terms.length) #preview-buttons @@ -26,7 +27,7 @@ title="Ustari novo geslo" ) img(src="/images/u_edit-alt.svg") - span Nov + span= t('Nov') button#show-dates.ms-1.btn.border-header.ps-2.pe-2( type="button" data-bs-toggle="collapse" @@ -39,20 +40,20 @@ data-bs-custom-class="gray-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" - title="Prikaži datume" + title=t('Prikaži datume') ) button#duplicate-entry.ms-1.btn.border-header.ps-2.pe-2( data-bs-custom-class="gray-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" - title="Podvoji" + title=t('Podvoji') ) img(src="/images/copy.svg") button#delete-entry.ms-1.btn.border-header.ps-2.pe-2( data-bs-custom-class="red-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" - title="Briši" + title=t('Briši') ) img(src="/images/red-trash-icon.svg") @@ -60,7 +61,7 @@ form="form-edit-content" disabled ) - | Shrani + = t('Shrani') else #preview-buttons .d-flex.align-items-center.my-2 @@ -73,7 +74,7 @@ data-term="1" ) img(src="/images/u_edit-alt.svg") - span Nov + span= t('Nov') button#show-dates.ms-1.btn.border-header.ps-2.pe-2( type="button" data-bs-toggle="collapse" @@ -87,14 +88,14 @@ data-bs-custom-class="gray-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" - title="Prikaži datume" + title=t('Prikaži datume') ) button#duplicate-entry.ms-1.btn.border-header.ps-2.pe-2( href="#" data-bs-custom-class="gray-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" - title="Podvoji" + title=t('Podvoji') disabled ) img(src="/images/copy.svg") @@ -103,7 +104,7 @@ data-bs-custom-class="red-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" - title="Briši" + title=t('Briši') disabled ) img(src="/images/red-trash-icon.svg") @@ -112,7 +113,7 @@ form="form-edit-content" disabled ) - | Shrani + = t('Shrani') #comments-buttons.d-flex.d-none.align-items-center.ms-2.ms-xl-0.me-2 .form-check @@ -121,10 +122,10 @@ name="comments-type" checked ) - label.ms-0.form-check-label(for="internal") Interni + label.ms-0.form-check-label(for="internal")= t('Interni') .form-check.ms-2 input#external.form-check-input(type="radio" name="comments-type") - label.ms-0.form-check-label(for="external") Zunanji + label.ms-0.form-check-label(for="external")= t('Zunanji') hr#header-row.mt-0.ms-0.mb-0 diff --git a/express/views/utilities/content-selected-overview.pug b/express/views/utilities/content-selected-overview.pug index ab67281..2830157 100644 --- a/express/views/utilities/content-selected-overview.pug +++ b/express/views/utilities/content-selected-overview.pug @@ -1,3 +1,5 @@ +// TODO MARK FOR DELETION + .row.d-flex.align-items-center .col-sm-3 span.bold-blue-text Verzija 12 diff --git a/express/views/utilities/content-side-menu.pug b/express/views/utilities/content-side-menu.pug index 692470d..fdfcbf6 100644 --- a/express/views/utilities/content-side-menu.pug +++ b/express/views/utilities/content-side-menu.pug @@ -2,12 +2,14 @@ .content-nav-mobile button#nav-content-button.nav-button img#burger-menu-img(src="/images/burger-menu-button-icon.svg" alt="Meni") - span.nav-title.ms-0 Vsebina - - .nav-title - a#chevrons-back.link-back(href="/") + span.nav-title.ms-0= t('Vsebina') + a.link-back.text-decoration-none.ms-auto.me-3(href="/") + img(src="/images/chevrons-left.svg") + span.normal-gray.ms-1= t('Nazaj') + .content-nav-title + a#chevrons-back.link-back.text-decoration-none img(src="/images/chevrons-left.svg") - span.nav-title.ms-2 Vsebina + span.normal-gray.ms-2= t('Nazaj') nav ul.content-nav-content.scroller-style.ps-0 ul.filter-search-settings.ps-0 @@ -15,24 +17,24 @@ li#unfiltered.filter-search-section.pb-0.px-3 .d-flex select.form-select.py-0.filter-select.ps-1.pe-0(name="field") - option(value="term") Termin - option(value="domainLabels") Področna oznaka - option(value="label") Pojasnilo - option(value="definition") Definicija - option(value="synonyms") Sinonimi - option(value="links") Povezave - option(value="other") Drugo - option(value="foreignTerms") Tuj termin - option(value="foreignDefinition") Tuja definicija - option(value="foreignSynonyms") Tuj sinonim - option(value="") Iskanje po vseh + option(value="term")= t('Termin') + option(value="domainLabels")= t('Področna oznaka') + option(value="label")= t('Pojasnilo') + option(value="definition")= t('Definicija') + option(value="synonyms")= t('Sinonimi') + option(value="links")= t('Povezave') + option(value="other")= t('Drugo') + option(value="foreignTerms")= t('Tuj termin') + option(value="foreignDefinition")= t('Tuja definicija') + option(value="foreignSynonyms")= t('Tuj sinonim') + option(value="")= t('Iskanje po vseh') .d-flex.justify-content-between.mt-4 .classic-search-container.w-75 .classic-search.border1px-gray-2 input.input-search( name="q" type="text" - placeholder="Išči" + placeholder=t('Išči') autocomplete="off" ) .position-relative @@ -41,7 +43,7 @@ button.btn.clear-search-input.px-0(type="button") img(src="/images/x-gray.svg") .d-flex.justify-content-between.align-items-center.mt-4 - label#span-filter.normal-gray-label(for="show-filter") Filtriranje + label#span-filter.normal-gray-label(for="show-filter")= t('Filtriranje') button#show-filter.btn.ps-0.pe-0( type="button" data-bs-toggle="collapse" @@ -54,7 +56,7 @@ data-bs-custom-class="gray-tooltip" data-bs-toggle="tooltip" data-bs-placement="bottom" - title="Prikaži filtre" + title=t('Prikaži filtre') ) li#filtered.filter-search-section.collapse.pt-1 .form-check.d-flex.align-items-center @@ -62,42 +64,42 @@ type="checkbox" name="isValid" ) - label.form-check-label(for="valid") Veljavno + label.form-check-label(for="valid")= t('Veljavno') .form-check.d-flex.align-items-center input#published.form-check-input.indeterminate-cbox( type="checkbox" name="isPublished" ) - label.form-check-label(for="published") Obljavljeno + label.form-check-label(for="published")= t('Obljavljeno') .form-check.d-flex.align-items-center input#comments.form-check-input.indeterminate-cbox( type="checkbox" name="hasComments" ) - label.form-check-label(for="comments") Komentarji + label.form-check-label(for="comments")= t('Komentarji') .form-check.d-flex.align-items-center input#edited.form-check-input.indeterminate-cbox( type="checkbox" name="isComplete" ) - label.form-check-label(for="edited") Urejeno + label.form-check-label(for="edited")= t('Urejeno') .form-check.d-flex.align-items-center input#terminology-checked.form-check-input.indeterminate-cbox( type="checkbox" name="isTerminologyReviewed" ) - label.form-check-label(for="terminology-checked") Strokovno pregledano + label.form-check-label(for="terminology-checked")= t('Strokovno pregledano') .form-check.d-flex.align-items-center input#language-checked.form-check-input.indeterminate-cbox( type="checkbox" name="isLanguageReviewed" ) - label.form-check-label(for="language-checked") Jezikovno pregledano + label.form-check-label(for="language-checked")= t('Jezikovno pregledano') #term-list.btn-group.termin-list.d-flex.flex-column(role="group") if terms.length @@ -126,7 +128,7 @@ data-bs-custom-class="label-tooltip" data-bs-original-title="" title - )= '[ni termina]' + )= t('[ni termina]') span.me-4= term.commentActivityIndicator else .d-flex.justify-content-between.align-items-center @@ -138,7 +140,7 @@ data-bs-custom-class="label-tooltip" data-bs-original-title="" title - )= '[ni termina]' + )= t('[ni termina]') span.me-4= term.commentActivityIndicator else .d-flex.justify-content-between.align-items-center @@ -152,7 +154,7 @@ data-bs-custom-class="label-tooltip" data-bs-original-title="" title - )!= term.term + )!= `${term.term} ${term.homonymSort ? '(' + term.homonymSort + ')' : ''}` span.me-4= term.commentActivityIndicator else label.terms-label.btn.p-2.ms-2.me-3.text-truncate.justify-content-start.d-inline( @@ -163,7 +165,7 @@ data-bs-custom-class="label-tooltip" data-bs-original-title="" title - )!= term.term + )!= `${term.term} ${term.homonymSort ? '(' + term.homonymSort + ')' : ''}` span.me-4= term.commentActivityIndicator else label.terms-label.not-valid-not-published.btn.p-2.ms-2.me-3.text-truncate.justify-content-start.d-inline( @@ -174,7 +176,7 @@ data-bs-custom-class="label-tooltip" data-bs-original-title="" title - )!= term.term + )!= `${term.term} ${term.homonymSort ? '(' + term.homonymSort + ')' : ''}` span.me-4= term.commentActivityIndicator .lds-default.d-none div diff --git a/express/views/utilities/dictionary-description-mixin.pug b/express/views/utilities/dictionary-description-mixin.pug new file mode 100644 index 0000000..6061204 --- /dev/null +++ b/express/views/utilities/dictionary-description-mixin.pug @@ -0,0 +1,228 @@ +mixin dictionary-description(dictionaryId) + .content-hold-prerequisites + #offset-main.main-container.mt-1 + if dictionaryId + .title + .subject-name + label.input-name-txt(for="")= t('ID Slovarja') + .row + .col-sm-6 + span.page-title-header= dictionary.id + form#admin-description.needs-validation.mt-3(method="post" novalidate) + .title + .subject-name + label.input-name-txt(for="dictionary-title")= t('NASLOV SLOVARJA *') + .row + .col-sm-6 + input#dictionary-title.name-input.form-control( + type="text" + name="nameSl" + required + maxlength="120" + value=dictionary.nameSl + ) + .invalid-feedback= t('Niste vpisali naslova slovarja.') + + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Celotni naslov slovarja, ki bo zabeležen v bibliografskih podatkih.') + + .english-title.mt-4 + .subject-name + label.input-name-txt(for="dictionary-title-en")= t('ANGLEŠKI NASLOV SLOVARJA *') + .row + .col-sm-6 + input#dictionary-title-en.name-input.form-control( + type="text" + name="nameEn" + maxlength="120" + value=dictionary.nameEn ? dictionary.nameEn : '' + required + ) + .invalid-feedback= t('Niste vpisali angleškega naslova slovarja.') + + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Celotni naslov slovarja v angleščini.') + + .short-title.mt-4 + .subject-name + label.input-name-txt(for="short-dictionary-title")= t('SKRAJŠANI NASLOV SLOVARJA') + .row + .col-sm-6 + input#short-dictionary-title.name-input.d-inline.form-control( + type="text" + name="nameSlShort" + maxlength="15" + value=dictionary.nameSlShort ? dictionary.nameSlShort : '' + ) + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesede nje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki.') + //- TODO enable field when slug is in DB + //- .mt-4 + //- .subject-name + //- label.input-name-txt(for="slug") Slug + //- .row + //- .col-sm-6 + //- input#slug.name-input.d-inline.form-control( + //- type="text" + //- placeholder="V razvoju ..." + //- disabled + //- ) + //- .col-sm.d-flex.align-items-center + //- span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Slug + + if dictionary.author + each ele, index in dictionary.author + .author.mt-4.added-field + .subject-name + label.input-name-txt(for="author")= t('AVTOR SLOVARJA') + .row + .col-sm-6 + .input-group + input( + class=index != 0 ? 'name-input d-inline form-control icon-trash' : 'name-input d-inline form-control' + type="text" + name="author" + maxlength="64" + value=ele ? ele : '' + ) + if (index != 0) + button.input-group-text.delete-author-btn(type="button") + img.delete-author.p-0( + src="/images/red-trash-icon.svg" + alt="Delete" + ) + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime.') + else + #first-author.author.mt-4 + .subject-name + label.input-name-txt(for="author")= t('AVTOR SLOVARJA') + .row + .col-sm-6 + input#author.name-input.d-inline.form-control( + type="text" + name="author" + maxlength="64" + ) + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime.') + + #add-new-author.author.mt-4 + .subject-name + label.input-name-txt(for="input-new-author")= t('NOV AVTOR SLOVARJA') + .row + .col-sm-6 + button#input-new-author.form-control(type="button") + span.new-author-text-btn= t('Nov avtor') + + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Dodajte ime in priimek naslednjega avtorja slovarja..') + //- TODO enable field when weight is in DB + //- .mt-4 + //- .subject-name + //- label.input-name-txt(for="weight") TEŽA + //- .row + //- .col-sm-6.d-flex.align-items-center + //- input#weight.name-input.form-control.autocomplete.d-inline( + //- disabled + //- placeholder="V razvoju ..." + //- ) + + //- .col-sm.d-flex.align-items-center + //- span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3 Statična teža. + + if dictionaryId + .mt-4 + .subject-name + label.input-name-txt(for="status")= t('STATUS') + .row + .col-sm-6.d-flex.align-items-center + select#status.name-input.form-select.d-inline(name="status") + option( + value="closed" + selected=status === 'closed' ? true : false + )= t('Zaprt') + option( + value="reviewed" + selected=status === 'reviewed' ? true : false + disabled=status === 'reviewed' ? false : true + )= t('V odpiranju') + option( + value="published" + selected=status === 'published' ? true : false + )= t('Odprt') + + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Terminološkemu slovarju določite status. Izbirate lahko med zaprt, v urejanju in odprt.') + + .cerif-area.mt-4 + .subject-name + label.input-name-txt(for="select-cerif")= t('PODROČJE *') + .row + .col-sm-6 + select.name-input.d-inline.form-select( + name="domainPrimary" + required + ) + each domain in allPrimaryDomains + option( + value=domain.id + selected=domain.id === dictionary.domainPrimary + )= domain.nameSl + #invalid-section.invalid-feedback-selection.hidden-section.mt-3= t('Nimate izbranega področja CERIF.') + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Izberite področje svojega terminološkega slovarja na seznamu področij.') + + .small-name-area.mt-4 + .subject-name + label.input-name-txt(for="domain-secondary")= t('PODPODROČJE') + .row + .col-sm-6 + select#domain-secondary.name-input.d-inline.form-control.without-addition( + name="domainSecondary" + multiple + ) + each domain in allSecondaryDomains + if associatedSecondaryDomains.some(associatedDomain => associatedDomain.id === domain.id) + option(value=domain.id selected)= domain.nameSl + else + option(value=domain.id)= domain.nameSl + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Podpodročje.') + + #add-new-area.author.mt-4 + .subject-name + label.input-name-txt= t('NOVO PODPODROČJE') + .row + .col-sm-6 + button#input-new-area.form-control(type="button") + span.new-author-text-btn= t('Novo podpodročje') + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('Vpišite novo podpodročje. Na seznamu podpodročij bo vidno takoj po potrditvi administratorja portala.') + #text-editor.mt-4 + .subject-name + span.input-name-txt= t('OPIS SLOVARJA') + .container-xxl.ps-0.ms-0 + textarea.summernote( + name="description" + value=dictionary.description + ) + if dictionary.description + p= dictionary.description + .col-sm-6.smaller-gray-info.mt-2 + | + #issn-field.mt-4 + .subject-name + label.input-name-txt(for="issn")= t('ISSN OZNAKA') + .row + .col-sm-6 + input#issn.name-input.d-inline.form-control( + type="text" + name="issn" + maxlength="20" + value=dictionary.issn ? dictionary.issn : '' + ) + .col-sm.d-flex.align-items-center + span.d-md-inline.d-block.smaller-gray-info.ms-xxl-3.ms-md-3= t('ISSN oznaka.') + a.ms-1.smaller-gray-info(href="/pomoc" target="_blank")= t('Več ...') + include /common/footer diff --git a/express/views/utilities/dictionary-domain-labels-mixin.pug b/express/views/utilities/dictionary-domain-labels-mixin.pug new file mode 100644 index 0000000..afd9928 --- /dev/null +++ b/express/views/utilities/dictionary-domain-labels-mixin.pug @@ -0,0 +1,92 @@ +mixin dictionary-domain-labels + .content-hold-prerequisites + #offset-main.main-container.mt-2 + if results.length + .d-flex.justify-content-end.my-3 + .d-flex + include /utilities/pager + +pager + + form#dictionary-domain-labels( + method="post" + action="/api/v1/dictionaries/update-domain-labels" + ) + input#subareas-dict-id( + type="hidden" + name="dictionaryId" + value=dictionary.id + ) + table#all-areas-table.table.areas-table.table-responsive + thead + tr + th.visible-th(scope="col")= t('Vidno') + th(scope="col")= t('Področna oznaka') + th(scope="col") + tbody#page-results + each result in results + tr + input(type="hidden" name="domainLabelId" value=result.id) + th(scope="row") + if result.isVisible + input.form-check.checkbox-table( + type="checkbox" + name="isVisible" + checked + disabled + ) + else + input.form-check.checkbox-table( + type="checkbox" + name="isVisible" + disabled + ) + td.tdata-area= result.name + td.buttons-group + .table-buttons + button.p-0.table-button-grp.me-3.edit-row-btn( + type="button" + ) + img(src="/images/u_edit-alt.svg" alt="") + button.p-0.table-button-grp.delete-row-btn( + type="button" + data-bs-target="#alert-modal" + data-bs-toggle="modal" + ) + img(src="/images/red-trash-icon.svg" alt="") + else + form#dictionary-domain-labels( + method="post" + action="/api/v1/dictionaries/update-domain-labels" + ) + input#subareas-dict-id( + type="hidden" + name="dictionaryId" + value=dictionary.id + ) + #subareas-info.d-flex.justify-content-center.mt-5 + p= t('Niste vnesli področnih oznak.') + #no-subareas-section.d-none + .d-flex.justify-content-end.mt-4 + .d-flex + include /utilities/pager + +pager + + table#all-areas-table.table.areas-table.table-responsive + thead + tr + th.visible-th(scope="col")= t('Vidno') + th(scope="col")= t('Področna oznaka') + th(scope="col") + tbody + tr.hidden(hidden) + .row + .col-sm-3 + .subject-name + label.input-name-txt= t('PODROČNA OZNAKA') + input#subarea-input.form-control(type="text") + .col.d-flex.align-items-end.mt-2 + button#add-area.btn.btn-primary(type="submit" disabled)= t('Dodaj') + include /common/footer + include /utilities/modal-alert + include /utilities/modal-alert-mixin + +alertModal('unsaved-data', t('Shrani'), t('Ne'), 'modal-save-btn', 'modal-dont-save-btn', t('Imate neshranjene spremebe. Ali jih želite shraniti?')) diff --git a/express/views/utilities/dictionary-export-content-mixin.pug b/express/views/utilities/dictionary-export-content-mixin.pug new file mode 100644 index 0000000..cdadffb --- /dev/null +++ b/express/views/utilities/dictionary-export-content-mixin.pug @@ -0,0 +1,291 @@ +mixin dictionary-export-content(dictionaryId) + .content-hold-prerequisites + #offset-main.main-container.mt-2 + form#dictionary-export( + method="post" + action=`/api/v1/dictionaries/${dictionaryId}/export-begin` + ) + .row.validity.align-items-center + .col-xxl-6.col-md-7 + span.header-table-wrapper= t('Veljavnost') + ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 + .options-width-box + li.form-check.radio-button.ms-0.ps-3 + input#allKeys.form-check-input( + type="radio" + name="isValidFilter" + checked + ) + label.radio-button-labels.form-check-label.ms-0.text-nowrap( + for="allKeys" + ) + = t('Vsi slovarski sestavki') + .options-another-box + li.form-check.radio-button.ms-0.ps-3 + input#validKeys.form-check-input( + type="radio" + name="isValidFilter" + value="true" + ) + label.radio-button-labels.form-check-label.ms-0( + for="validKeys" + ) + = t('Veljavni') + .options-another-box + li.form-check.radio-button.ms-0.ps-3 + input#notValidKeys.form-check-input( + type="radio" + name="isValidFilter" + value="false" + ) + label.radio-button-labels.form-check-label.ms-0( + for="notValidKeys" + ) + = t('Neveljavni') + .col.mb-3.mb-md-0 + span.name-info-txt= t('Izberite, katere slovarske sestavke želite izpisati glede na njihovo veljavnost.') + .row.posted.align-items-center + .col-xxl-6.col-md-7 + span.header-table-wrapper= t('Objavljeno') + ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 + .options-width-box + li.form-check.radio-button.ms-0.ps-3 + input#allPostedKeys.form-check-input( + type="radio" + name="isPublishedFilter" + checked + ) + label.radio-button-labels.form-check-label.ms-0.text-nowrap( + for="allPostedKeys" + ) + = t('Vsi slovarski sestavki') + .options-another-box + li.form-check.radio-button.ms-0.ps-3 + input#onlyPosted.form-check-input( + type="radio" + name="isPublishedFilter" + value="true" + ) + label.radio-button-labels.form-check-label.ms-0( + for="onlyPosted" + ) + = t('Objavljeni') + .options-another-box + li.form-check.radio-button.ms-0.ps-3 + input#notPosted.form-check-input( + type="radio" + name="isPublishedFilter" + value="false" + ) + label.radio-button-labels.form-check-label.ms-0( + for="notPosted" + ) + = t('Neobjavljeni') + .col.mb-3.mb-md-0 + span.name-info-txt= t('Izberite, ali želite izpisati samo objavljene ali tudi neobjavljene slovarske sestavke.') + .row.phases.align-items-center + .col-xxl-6.col-md-7 + span.header-table-wrapper= t('Faze urejanja') + ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 + .options-width-box + li.form-check.radio-button.ms-0.ps-3 + input#allEditedKeys.form-check-input( + type="radio" + name="statusFilter" + checked + ) + label.radio-button-labels.form-check-label.ms-0.text-nowrap( + for="allEditedKeys" + ) + = t('Vsi slovarski sestavki') + .options-another-box + li.form-check.radio-button.ms-0.ps-3 + input#editedKeys.form-check-input( + type="radio" + name="statusFilter" + value="complete" + ) + label.radio-button-labels.form-check-label.ms-0( + for="editedKeys" + ) + = t('Urejeni') + .options-another-box + li.form-check.radio-button.ms-0.ps-3 + input#inEditing.form-check-input( + type="radio" + name="statusFilter" + value="inEdit" + ) + label.radio-button-labels.form-check-label.ms-0( + for="inEditing" + ) + = t('V urejanju') + .col.mb-3.mb-md-0 + span.name-info-txt= t('Izberite, ali želite izpisati vse slovarske sestavke ali samo tiste, ki so v določeni fazi urejanja.') + .row.proffesional-check.align-items-center + .col-xxl-6.col-md-7 + span.header-table-wrapper= t('Strokovni pregled') + ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 + .options-width-box + li.form-check.radio-button.ms-0.ps-3 + input#allChecked.form-check-input( + type="radio" + name="isTerminologyReviewedFilter" + checked + ) + label.radio-button-labels.form-check-label.ms-0( + for="allChecked" + ) + = t('Vsi slovarski sestavki') + .options-another-box + li.form-check.radio-button.ms-0.ps-3 + input#onlyProffesionallyChecked.form-check-input( + type="radio" + name="isTerminologyReviewedFilter" + value="true" + ) + label.radio-button-labels.form-check-label.ms-0( + for="onlyProffesionallyChecked" + ) + = t('Pregledani') + .options-another-box + li.form-check.radio-button.ms-0.ps-3 + input#notProffesionallyChecked.form-check-input( + type="radio" + name="isTerminologyReviewedFilter" + value="false" + ) + label.radio-button-labels.form-check-label.ms-0( + for="notProffesionallyChecked" + ) + = t('Nepregledani') + .col.mb-3.mb-md-0 + span.name-info-txt= t('Izberite, ali želite izpisati samo strokovno pregledane slovarske sestavke.') + .row.terminology-check.align-items-center + .col-xxl-6.col-md-7 + span.header-table-wrapper= t('Jezikovni pregled') + ul.d-sm-flex.validity-row.ps-2.mb-0.mb-md-2 + .options-width-box + li.form-check.radio-button.ms-0.ps-3 + input#allGramaticallyChecked.form-check-input( + type="radio" + name="isLanguageReviewedFilter" + checked + ) + label.radio-button-labels.form-check-label.ms-0( + for="allGramaticallyChecked" + ) + = t('Vsi slovarski sestavki') + .options-another-box + li.form-check.radio-button.ms-0.ps-3 + input#onlyGramaticallyChecked.form-check-input( + type="radio" + name="isLanguageReviewedFilter" + value="true" + ) + label.radio-button-labels.form-check-label.ms-0( + for="onlyGramaticallyChecked" + ) + = t('Pregledani') + .options-another-box + li.form-check.radio-button.ms-0.ps-3 + input#notGramaticallyChecked.form-check-input( + type="radio" + name="isLanguageReviewedFilter" + value="false" + ) + label.radio-button-labels.form-check-label.ms-0( + for="notGramaticallyChecked" + ) + = t('Nepregledani') + .col.mb-3.mb-md-0 + span.name-info-txt= t('Izberite, ali želite izpisati samo jezikovno pregledane slovarske sestavke.') + .file-type + span.new-user-info= t('FORMAT ZAPISA') + .row.align-items-center + .col-xxl-6.col-md-7 + .form-check.radio-button.ms-2.ps-3 + input#file-format-xml.form-check-input( + type="radio" + name="exportFileFormat" + value="xml" + checked + ) + label.form-check-label.ms-0(for="file-format-xml") XML + .form-check.radio-button.ms-3 + input#file-format-csv.form-check-input( + type="radio" + name="exportFileFormat" + value="csv" + ) + label.form-check-label.ms-0(for="file-format-csv") CSV + .form-check.radio-button.ms-3 + input#file-format-tsv.form-check-input( + type="radio" + name="exportFileFormat" + value="tsv" + ) + label.form-check-label.ms-0(for="file-format-tsv") TSV + .form-check.radio-button.ms-3 + input#file-format-txt.form-check-input( + type="radio" + name="exportFileFormat" + value="tbx" + ) + label.form-check-label.ms-0(for="file-format-txt") TBX + .col.mb-3.mb-md-0 + span.name-info-txt= t('Izberite format izpisa terminološkega slovarja.') + button.btn.btn-primary.mt-4= t('IZVOZI') + .latest-exports.mt-4 + input#dictionary-id(hidden value=dictionary.id) + .d-flex.w-100.justify-content-between + .d-flex + span.info-text-for-button= t('Zadnji izvozi') + .d-flex + include /utilities/pager + +pager + + - + const localeOptions = { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit' + } + if results.length + .table-responsive + table.styled-table + thead + tr#thead + th= t('DATUM') + th= t('VRSTA') + th= t('ŠT. GESEL') + th= t('STATUS') + tbody#page-results + each result in results + tr + - const date = new Date(result.time_created).toLocaleDateString('sl-SL', localeOptions) + td= date + td= result.export_file_format + td= result.entry_count + td + if (result.status === 'finished') + a.btn.btn-primary( + href=`/slovarji/export-download/${result.id}` + )= t('Shrani') + .mt-3 + p!= t('Shemo za format xml si prenesete tukaj.') + include /common/footer + //- .d-block.w-100 + //- include /utilities/table-mixin + //- - + //- const headerRow = ['Datum', 'Vrsta', 'Št. gesel', 'Status'] + //- const dataRows = exportsList.map(e => [ + //- e.dateCreated, + //- e.typeString, + //- e.entryCount, + //- e.status, + //- e.status !== 'finished' ? '' : {link: {href: `/slovarji/export-download/${e.id}`, content:"Shrani"}} + //- ]) + //- +tableHeader(headerRow, dataRows) diff --git a/express/views/utilities/dictionary-extraction-import-mixin.pug b/express/views/utilities/dictionary-extraction-import-mixin.pug new file mode 100644 index 0000000..3055508 --- /dev/null +++ b/express/views/utilities/dictionary-extraction-import-mixin.pug @@ -0,0 +1,54 @@ +mixin dictionary-extraction-import + .content-hold-prerequisites + #offset-main.main-container.mt-3 + .small-name-area + .subject-name + span.input-name-txt= t('IME LUŠČENJA') + .row + .col-lg-6 + select#select-extraction-name.name-input.d-inline.form-control + option(selected value="" disabled hidden)= t('Izberite luščenje') + each el in extractions + option(value=el.id)= el.name + .col-sm.d-flex.align-items-center + span.name-info-txt.ms-lg-3.mt-2.mt-lg-0= t('Izberite enega od rezultatov luščenja s seznama.') + .list-terminology-candidates.mt-4.me-2 + .d-flex.w-100.justify-content-between + .d-flex.align-items-center + span.info-text-for-button= t('Seznam terminoloških kandidatov') + .d-flex + include /utilities/pager + +pager + .table-responsive.mt-2.me-2 + table.styled-table + thead + tr#thead + th= t('#') + th= t('KANONIČNA OBLIKA') + th= t('UTEŽ') + th= t('POJAVITVE') + tbody#page-results + form#import-form(method="post") + .row.mt-4 + .me-0.pe-0.d-flex.align-items-center + span.radio-button-labels= t('Uvozi termine od številke') + input.without-arrows.form-control.terminology-input.ms-1( + type="number" + maxlength="5" + name="from" + min="0" + ) + span.radio-button-labels.ms-1= t('do številke') + input.without-arrows.form-control.terminology-input.ms-1( + type="number" + maxlength="5" + name="to" + min="0" + ) + button#import-btn.btn.btn-primary.mt-4(disabled)= t('UVOZI') + + include /common/footer + + script#variables-transport-script(nonce=cspNonce). + const dictionaryId = #{ dictionary.id }; + document.getElementById('variables-transport-script').remove(); diff --git a/express/views/utilities/dictionary-import-mixin.pug b/express/views/utilities/dictionary-import-mixin.pug new file mode 100644 index 0000000..58d1bba --- /dev/null +++ b/express/views/utilities/dictionary-import-mixin.pug @@ -0,0 +1,132 @@ +mixin dictionary-import(dictionaryId) + .content-hold-prerequisites + #offset-main.main-container + .import-container.mt-3.ms-1 + form#file-import-form(method="post" enctype="multipart/form-data") + .file-type + span.new-user-info= t('DATOTEKA') + .row + .col-md-6.ms-2.white-border-background.p-4.d-flex.align-items-center.justify-content-between.me-2 + .align-items-center.d-flex + span#chosen-file.info-text-for-button= t('Izberi datoteko') + div + label(for="upload") + button#button-import.btn.btn-primary(type="button")= t('IZBERI') + input#upload( + type="file" + name="dictionaryImportFile" + accept=".xml" + ) + + .col-md.d-flex.align-items-center + span.name-info-txt= t('Izberite slovarske podatke, ki ste jih shranili na svojem računalniku.') + .file-type.mt-4 + span.new-user-info= t('NAČIN UVOZA') + .row + .col-sm-6 + .checkbox + input#flexCheckDefault.form-check-input( + type="checkbox" + name="deleteExistingEntries" + ) + label.form-check-label(for="flexCheckDefault") + span.user-email= t('Izbriši obstoječe slovarske sestavke.') + .col.ms-2 + span.name-info-txt= t('Če izberete to možnost, se bodo vsi doslejšnji slovarski sestavki ob uvozu nove datoteke izbrisali.') + .file-type.mt-4 + span.new-user-info= t('FAZA UREJANJA') + #file-type-selection.file-types + .row + .col-sm-6.ms-2 + .form-check.radio-button.ms-0.ps-3 + input#in-edit-radio.form-check-input( + type="radio" + name="entryStatus" + value="inEdit" + checked + ) + label.form-check-label.ms-0(for="in-edit-radio") + = t('V urejanju') + #complete-radio.form-check.radio-button.ms-3 + input#complete-radio.form-check-input( + type="radio" + name="entryStatus" + value="complete" + ) + label.form-check-label.ms-0(for="complete-radio") + = t('Urejeno') + .col + span.name-info-txt= t('Z izbiro te možnosti boste pobrisali samo slovarske sestavke, ki so v določeni fazi urejanja.') + .file-type.mt-4 + span.new-user-info= t('FORMAT ZAPISA') + #file-type-selection.file-types + .row + .col-sm-6.ms-2.text-nowrap + .form-check.radio-button.ms-0.ps-3 + input#file-format-xml.form-check-input( + type="radio" + name="importFileFormat" + value="xml" + checked + ) + label.form-check-label.ms-0(for="file-format-xml") XML + .form-check.radio-button.ms-2 + input#file-format-csv.form-check-input( + disabled + type="radio" + name="importFileFormat" + value="csv" + ) + label.form-check-label.ms-0(for="file-format-csv") CSV + .form-check.radio-button.ms-2 + input#file-format-tsv.form-check-input( + disabled + type="radio" + name="importFileFormat" + value="tsv" + ) + label.form-check-label.ms-0(for="file-format-tsv") TSV + .col + span.name-info-txt= t('Izberite format datoteke, v kateri je slovar shranjen na vašem računalniku.') + .file-type.mt-4 + button.btn.btn-primary.mt-4= t('UVOZI') + .latest-uploads.mt-4 + input#dictionary-id(hidden value=dictionary.id) + .d-flex.w-100.justify-content-between + .d-flex + span.info-text-for-button= t('Zadnji uvozi') + .d-flex + include /utilities/pager + +pager + + - + const localeOptions = { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit' + } + + if results.length + .table-responsive + table.styled-table + thead + tr#thead + th= t('DATUM') + th= t('VRSTA') + th= t('ŠT. GESEL') + th= t('STATUS') + tbody#page-results + each result in results + tr + - const date = new Date(result.time_started).toLocaleDateString('sl-SL', localeOptions) + td= date + td= result.file_format + td= result.count_valid_entries + td= result.status + else + p= t('Ni še dodanih uvozov.') + .mt-4 + p!= t('Shemo za format xml si prenesete tukaj.') + include /common/footer diff --git a/express/views/utilities/dictionary-main-input.pug b/express/views/utilities/dictionary-main-input.pug index b1c6bf5..d107ca2 100644 --- a/express/views/utilities/dictionary-main-input.pug +++ b/express/views/utilities/dictionary-main-input.pug @@ -3,7 +3,7 @@ mixin dictionaryMainInput(data) form.needs-validation(id=data.formId method="post" novalidate) .title .subject-name - label.input-name-txt(for="dictionary-title") NASLOV SLOVARJA * + label.input-name-txt(for="dictionary-title")= t('NASLOV SLOVARJA *') .row .col-sm-6.align-items-center input#dictionary-title.name-input.form-control.d-inline( @@ -12,14 +12,14 @@ mixin dictionaryMainInput(data) required maxlength="120" ) - .invalid-feedback Niste vpisali imena slovarja. + .invalid-feedback= t('Niste vpisali imena slovarja.') .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Celotni naslov slovarja, ki bo zabeležen v bibliografskih podatkih. + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Celotni naslov slovarja, ki bo zabeležen v bibliografskih podatkih.') .english-title.mt-4 .subject-name - label.input-name-txt(for="dictionary-title-en") ANGLEŠKI NASLOV SLOVARJA * + label.input-name-txt(for="dictionary-title-en")= t('ANGLEŠKI NASLOV SLOVARJA *') .row .col-sm-6 input#dictionary-title-en.name-input.form-control.d-inline( @@ -28,14 +28,14 @@ mixin dictionaryMainInput(data) maxlength="120" required ) - .invalid-feedback Niste vpisali angleškega naslova slovarja. + .invalid-feedback= t('Niste vpisali angleškega naslova slovarja.') .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Celotni naslov slovarja v angleščini. + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Celotni naslov slovarja v angleščini.') .short-title.mt-4 .subject-name - label.input-name-txt(for="short-dictionary-title") SKRAJŠANI NASLOV SLOVARJA + label.input-name-txt(for="short-dictionary-title")= t('SKRAJŠANI NASLOV SLOVARJA') .row .col-sm-6 input#short-dictionary-title.name-input.d-inline.form-control( @@ -44,11 +44,11 @@ mixin dictionaryMainInput(data) maxlength="15" ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesedenje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Zaradi organizacije podatkov na portalu za skrajšani naslov slovarja predlagamo poenobesedenje, ki se bo izpisovalo ob slovarju, npr. Davčni terminološki slovar → Davki') #first-author.author.mt-4 .subject-name - label.input-name-txt(for="author") AVTOR SLOVARJA + label.input-name-txt(for="author")= t('AVTOR SLOVARJA') .row .col-sm-6 input#author.name-input.d-inline.form-control( @@ -57,63 +57,65 @@ mixin dictionaryMainInput(data) maxlength="64" ) .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime. + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime.') #add-new-author.author.mt-4 .subject-name - label.input-name-txt(for="input-new-author") NOV AVTOR SLOVARJA + label.input-name-txt(for="input-new-author")= t('NOV AVTOR SLOVARJA') .row .col-sm-6 button#input-new-author.form-control(type="button") - span.new-author-text-btn Nov avtor + span.new-author-text-btn= t('Nov avtor') .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime. + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Vpišite avtorja slovarja, če ste glavni avtor, na prvo mesto zapišite svoje ime.') .cerif-area.mt-4 .subject-name - label.input-name-txt(for="select-cerif") PODROČJE * + label.input-name-txt(for="select-cerif")= t('PODROČJE *') .row .col-sm-6 select.name-input.d-inline.form-select( name="domainPrimary" required ) - option(value="" hidden disabled selected) Izberite področje + //- TODO I18n + option(value="" hidden disabled selected)= t('Izberite področje') each domain in allPrimaryDomains option(value=domain.id)= domain.nameSl - #invalid-section.invalid-feedback-selection.hidden-section.mt-3 Niste izbrali glavnega področja. + #invalid-section.invalid-feedback-selection.hidden-section.mt-3= t('Niste izbrali glavnega področja.') .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Področje. + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Področje.') .small-name-area.mt-4 .subject-name - label.input-name-txt(for="domain-secondary") PODPODROČJE + label.input-name-txt(for="domain-secondary")= t('PODPODROČJE') .row .col-sm-6 select#domain-secondary.name-input.d-inline.form-control.without-addition( name="domainSecondary" multiple ) + //- TODO I18n each domain in allSecondaryDomains option(value=domain.id)= domain.nameSl .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Vpišite podpodročje glavnega področja, ki ste ga izbrali. Če podpodročja ni med naborom, izberite polje Novo podpodročje. + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Vpišite podpodročje glavnega področja, ki ste ga izbrali. Če podpodročja ni med naborom, izberite polje Novo podpodročje.') #add-new-area.author.mt-4 .subject-name - label.input-name-txt NOVO PODPODROČJE + label.input-name-txt= t('NOVO PODPODROČJE') .row .col-sm-6 button#input-new-area.form-control(type="button") - span.new-author-text-btn Novo podpodročje + span.new-author-text-btn= t('Novo podpodročje') .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Vpišite novo podpodročje. Na seznamu podpodročij bo vidno takoj po potrditvi administratorja portala. + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Vpišite novo podpodročje. Na seznamu podpodročij bo vidno takoj po potrditvi administratorja portala.') if (data.hasDescription) #text-editor.mt-4 .subject-name - span.input-name-txt OPIS SLOVARJA + span.input-name-txt= t('OPIS SLOVARJA') .container-xxl.ps-0.ms-0 #summernote if (data.hasStructure) diff --git a/express/views/utilities/dictionary-structure-input.pug b/express/views/utilities/dictionary-structure-input.pug index 6622502..b67e2ee 100644 --- a/express/views/utilities/dictionary-structure-input.pug +++ b/express/views/utilities/dictionary-structure-input.pug @@ -1,7 +1,7 @@ block input-structure .switches .subject-name.mt-2 - span.input-name-txt STRUKTURA - ELEMENTI SLOVARSKEGA SESTAVKA: + span.input-name-txt= t('STRUKTURA - ELEMENTI SLOVARSKEGA SESTAVKA:') .switch-forms-and-key-word .row @@ -9,11 +9,11 @@ block input-structure .switch-forms.mb-3.mb-sm-0 .form-switch.d-flex input.form-check-input(type="checkbox" disabled checked) - label.form-check-label(for="Termin") Termin + label.form-check-label(for="Termin")= t('Termin') - .form-switch.d-flex - input.form-check-input(type="checkbox" disabled checked) - label.form-check-label(for="headword-ele") Oblike, naglasi, izgovor + //- .form-switch.d-flex + //- input.form-check-input(type="checkbox" disabled checked) + //- label.form-check-label(for="headword-ele") Oblike, naglasi, izgovor if dictionary if dictionary.hasDomainLabels @@ -23,21 +23,21 @@ block input-structure name="hasDomainLabels" checked ) - label.form-check-label.text-nowrap(for="domain-labels") Področna oznaka + label.form-check-label.text-nowrap(for="domain-labels")= t('Področna oznaka') else .form-switch.d-flex input#domain-labels.form-check-input( type="checkbox" name="hasDomainLabels" ) - label.form-check-label.text-nowrap(for="domain-labels") Področna oznaka + label.form-check-label.text-nowrap(for="domain-labels")= t('Področna oznaka') else .form-switch.d-flex input#domain-labels.form-check-input( type="checkbox" name="hasDomainLabels" ) - label.form-check-label.text-nowrap(for="domain-labels") Področna oznaka + label.form-check-label.text-nowrap(for="domain-labels")= t('Področna oznaka') if dictionary if dictionary.hasLabel @@ -47,14 +47,14 @@ block input-structure name="hasLabel" checked ) - label.form-check-label(for="label-checkbox") Pojasnilo + label.form-check-label(for="label-checkbox")= t('Pojasnilo') else .form-switch.d-flex input#label-checkbox.form-check-input( type="checkbox" name="hasLabel" ) - label.form-check-label(for="label-checkbox") Pojasnilo + label.form-check-label(for="label-checkbox")= t('Pojasnilo') else .form-switch.d-flex @@ -62,7 +62,7 @@ block input-structure type="checkbox" name="hasLabel" ) - label.form-check-label(for="label-checkbox") Pojasnilo + label.form-check-label(for="label-checkbox")= t('Pojasnilo') .form-switch.d-flex input#definition-check-box.form-check-input( @@ -70,7 +70,7 @@ block input-structure name="hasDefinition" checked ) - label.form-check-label(for="definition-check-box") Definicija + label.form-check-label(for="definition-check-box")= t('Definicija') if dictionary if dictionary.hasSynonyms @@ -80,21 +80,21 @@ block input-structure name="hasSynonyms" checked ) - label.form-check-label(for="synonyms") Sinonim + label.form-check-label(for="synonyms")= t('Sinonim') else .form-switch.d-flex input#synonyms.form-check-input( type="checkbox" name="hasSynonyms" ) - label.form-check-label(for="synonyms") Sinonim + label.form-check-label(for="synonyms")= t('Sinonim') else .form-switch.d-flex input#synonyms.form-check-input( type="checkbox" name="hasSynonyms" ) - label.form-check-label(for="synonyms") Sinonim + label.form-check-label(for="synonyms")= t('Sinonim') if dictionary if dictionary.hasLinks @@ -104,16 +104,16 @@ block input-structure name="hasLinks" checked ) - label.form-check-label(for="links") Povezani termin + label.form-check-label(for="links")= t('Povezani termin') else .form-switch.d-flex input#links.form-check-input(type="checkbox" name="hasLinks") - label.form-check-label(for="links") Povezani termin + label.form-check-label(for="links")= t('Povezani termin') else .form-switch.d-flex input#links.form-check-input(type="checkbox" name="hasLinks") - label.form-check-label(for="links") Povezani termin + label.form-check-label(for="links")= t('Povezani termin') //- .form-switch.d-flex //- input#other.form-check-input(type="checkbox" name="hasOther") @@ -127,15 +127,15 @@ block input-structure name="hasOther" checked ) - label.form-check-label(for="other") Drugo + label.form-check-label(for="other")= t('Drugo') else .form-switch.d-flex input#other.form-check-input(type="checkbox" name="hasOther") - label.form-check-label(for="other") Drugo + label.form-check-label(for="other")= t('Drugo') else .form-switch.d-flex input#other.form-check-input(type="checkbox" name="hasOther") - label.form-check-label(for="other") Drugo + label.form-check-label(for="other")= t('Drugo') if dictionary if dictionary.hasForeignLanguages @@ -145,14 +145,14 @@ block input-structure name="hasForeignLanguages" checked ) - label.form-check-label.text-nowrap(for="language-group") Tuji jezik + label.form-check-label.text-nowrap(for="language-group")= t('Tuji jezik') else .form-switch.d-flex input#language-group.form-check-input( type="checkbox" name="hasForeignLanguages" ) - label.form-check-label.text-nowrap(for="language-group") Tuji jezik + label.form-check-label.text-nowrap(for="language-group")= t('Tuji jezik') else .form-switch.d-flex input#language-group.form-check-input( @@ -160,7 +160,7 @@ block input-structure name="hasForeignLanguages" checked ) - label.form-check-label.text-nowrap(for="language-group") Tuji jezik + label.form-check-label.text-nowrap(for="language-group")= t('Tuji jezik') if dictionary if !dictionary.hasForeignLanguages @@ -170,7 +170,7 @@ block input-structure ) label#termin-subgroup-label.form-check-label.language-subgroups( for="termin-language-subgroup" - ) Termin + )= t('Termin') else .form-switch.d-flex input#termin-language-subgroup.form-check-input.disabled-input( @@ -179,7 +179,7 @@ block input-structure ) label#termin-subgroup-label.form-check-label.language-subgroups( for="termin-language-subgroup" - ) Termin + )= t('Termin') else .form-switch.d-flex input#termin-language-subgroup.form-check-input.disabled-input( @@ -188,7 +188,7 @@ block input-structure ) label#termin-subgroup-label.form-check-label.language-subgroups( for="termin-language-subgroup" - ) Termin + )= t('Termin') if dictionary if !dictionary.hasForeignLanguages @@ -199,7 +199,7 @@ block input-structure ) label#definition-subgroup-label.form-check-label.language-subgroups( for="definition-language-subgroup" - ) Definicija + )= t('Definicija') else if dictionary.hasForeignDefinitions .form-switch.d-flex input#definition-language-subgroup.form-check-input( @@ -209,7 +209,7 @@ block input-structure ) label#definition-subgroup-label.form-check-label.language-subgroups( for="definition-language-subgroup" - ) Definicija + )= t('Definicija') else .form-switch.d-flex input#definition-language-subgroup.form-check-input( @@ -218,7 +218,7 @@ block input-structure ) label#definition-subgroup-label.form-check-label.language-subgroups( for="definition-language-subgroup" - ) Definicija + )= t('Definicija') else .form-switch.d-flex input#definition-language-subgroup.form-check-input( @@ -227,7 +227,7 @@ block input-structure ) label#definition-subgroup-label.form-check-label.language-subgroups( for="definition-language-subgroup" - ) Definicija + )= t('Definicija') if dictionary if !dictionary.hasForeignLanguages @@ -238,7 +238,7 @@ block input-structure ) label#synonym-subgroup-label.form-check-label.language-subgroups( for="synonym-language-subgroup" - ) Sinonim + )= t('Sinonim') else if dictionary.hasForeignSynonyms .form-switch.d-flex input#synonym-language-subgroup.form-check-input( @@ -248,7 +248,7 @@ block input-structure ) label#synonym-subgroup-label.form-check-label.language-subgroups( for="synonym-language-subgroup" - ) Sinonim + )= t('Sinonim') else .form-switch.d-flex input#synonym-language-subgroup.form-check-input( @@ -257,7 +257,7 @@ block input-structure ) label#synonym-subgroup-label.form-check-label.language-subgroups( for="synonym-language-subgroup" - ) Sinonim + )= t('Sinonim') else .form-switch.d-flex input#synonym-language-subgroup.form-check-input( @@ -266,7 +266,7 @@ block input-structure ) label#synonym-subgroup-label.form-check-label.language-subgroups( for="synonym-language-subgroup" - ) Sinonim + )= t('Sinonim') if dictionary if dictionary.hasImages @@ -276,18 +276,18 @@ block input-structure name="hasImages" checked ) - label.form-check-label(for="images") Slika + label.form-check-label(for="images")= t('Slika') else .form-switch.d-flex input#images.form-check-input( type="checkbox" name="hasImages" ) - label.form-check-label(for="images") Slika + label.form-check-label(for="images")= t('Slika') else .form-switch.d-flex input#images.form-check-input(type="checkbox" name="hasImages") - label.form-check-label(for="images") Slika + label.form-check-label(for="images")= t('Slika') if dictionary if dictionary.hasAudio @@ -297,15 +297,15 @@ block input-structure name="hasAudio" checked ) - label.form-check-label(for="audio") Zvok + label.form-check-label(for="audio")= t('Zvok') else .form-switch.d-flex input#audio.form-check-input(type="checkbox" name="hasAudio") - label.form-check-label(for="audio") Zvok + label.form-check-label(for="audio")= t('Zvok') else .form-switch.d-flex input#audio.form-check-input(type="checkbox" name="hasAudio") - label.form-check-label(for="audio") Zvok + label.form-check-label(for="audio")= t('Zvok') if dictionary if dictionary.hasVideo @@ -315,27 +315,28 @@ block input-structure name="hasVideos" checked ) - label.form-check-label(for="video") Video + label.form-check-label(for="video")= t('Video') else .form-switch.d-flex input#video.form-check-input( type="checkbox" name="hasVideos" ) - label.form-check-label(for="video") Video + label.form-check-label(for="video")= t('Video') else .form-switch.d-flex input#video.form-check-input(type="checkbox" name="hasVideos") - label.form-check-label(for="video") Video + label.form-check-label(for="video")= t('Video') .ms-2.ms-md-5.flex-grow-1 include /utilities/entry-preview .subject-name.mt-3 - span.input-name-txt TUJI JEZIKI + span.input-name-txt= t('TUJI JEZIKI') .row.mb-3 if dictionary if dictionary.hasForeignLanguages .col-sm-6.select-languages + //- TODO I18n select#languages-input.without-addition( name="language" multiple @@ -366,6 +367,6 @@ block input-structure each language in allLanguages option(value=language.id)= language.nameSl .col-sm.d-flex.align-items-center - span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0 Izberite vse tuje jezike, ki jih bo terminološki slovar vseboval. + span.d-md-inline.d-block.name-info-txt.ms-xxl-3.ms-md-3.mt-3.mt-sm-0= t('Izberite vse tuje jezike, ki jih bo terminološki slovar vseboval.') #invalid-language-section.invalid-feedback-selection.hidden-section.mt-3 - | Niste vpisali tujih jezikov. + = t('Niste vpisali tujih jezikov.') diff --git a/express/views/utilities/entry-preview-mixin.pug b/express/views/utilities/entry-preview-mixin.pug index 2840d50..cf0e095 100644 --- a/express/views/utilities/entry-preview-mixin.pug +++ b/express/views/utilities/entry-preview-mixin.pug @@ -3,7 +3,7 @@ mixin entryPreview .dictionary-content-top.mx-0 .mb-1.d-flex.align-items-start.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info TERMIN: + span.smaller-gray-info #{ t('TERMIN:') } .flex-grow-1.col-sm-8 span#selected-term.bold-blue-subheading.text-break!= entryData.term //- .headword-preview.mt-1.d-flex.align-items-start.row @@ -25,38 +25,38 @@ mixin entryPreview if (structure.hasDomainLabels && !!selectedDomainLabelsForEntryString) #preview-domain-secondary.preview-domain-secondary.mt-1.d-flex.align-items-start.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info PODROČNA OZNAKA: + span.smaller-gray-info #{ t('PODROČNA OZNAKA:') } .flex-grow-1.col-sm-8 span#selected-domain-secondary.text-uppercase.text-break!= selectedDomainLabelsForEntryString if (structure.hasLabel && entryData.label) #preview-label.preview-label.mt-1.d-flex.align-items-start.row .d-sm-block.preview-column-width.col-sm-4 - span.smaller-gray-info POJASNILO: + span.smaller-gray-info #{ t('POJASNILO:') } .flex-grow-1.col-sm-8 span#selected-label.text-break!= entryData.label if (structure.hasDefinition && entryData.definition) #preview-definition.preview-definition.mt-1.d-flex.align-items-start.row .col-sm-4.d-flex.preview-column-width - span.smaller-gray-info DEFINICIJA: + span.smaller-gray-info #{ t('DEFINICIJA:') } .flex-grow-1.col-sm-8 span#selected-definition.fw-bold!= entryData.definition if (structure.hasSynonyms && entryData.synonym && entryData.synonym.join(", ").length) #preview-synonym.preview-synonym.mt-1.d-flex.align-items-start.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info SINONIM: + span.smaller-gray-info #{ t('SINONIM:') } .flex-grow-1.col-sm-8 span#selected-synonyms.small-blue-text.text-break!= entryData.synonym.join(', ') //- TODO POVEZANI TERMIN - if (structure.hasLinks && entryData.link) + if (structure.hasLinks && Array.isArray(entryData.links) && entryData.links.length) #preview-linked-term.preview-linked-terms.mt-1.d-flex.align-items-start.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info POVEZANI TERMIN: + span.smaller-gray-info #{ t('POVEZANI TERMIN:') } .flex-grow-1.col-sm-8 - span#linked-terms.text-break.text-break!= entryData.link + span#linked-terms.text-break.text-break!= entryData.links.map(entry => entry.link).join('
') if (structure.hasOther && entryData.other) #preview-other-field.mt-1.d-flex.align-items-start.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info DRUGO: + span.smaller-gray-info #{ t('DRUGO:') } .flex-grow-1.col-sm-8 span#selected-other-field.text-break!= entryData.other - const isLastBreakPointRequired = (structure.hasImages && entryData.image && entryData.image.join(', ').length) || (structure.hasAudio && entryData.audio && entryData.audio.join(', ').length) || (structure.hasVideo && entryData.video && entryData.video.join(', ').length) @@ -70,28 +70,28 @@ mixin entryPreview if (structure.hasForeignLanguages && entry.term && entry.term.join(", ").length) .preview-foreign-terms.mt-1.d-flex.align-items-start.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info TERMIN: + span.smaller-gray-info #{ t('TERMIN:') } .selected-label.flex-grow-1.col-sm-8 span.preview-f-term.bold-blue-subheading.text-break!= entry.term.join(', ') if (structure.hasForeignDefinitions && entry.definition) .preview-foreign-definition.mt-1.d-flex.align-items-start.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info DEFINICIJA: + span.smaller-gray-info #{ t('DEFINICIJA:') } .selected-label.flex-grow-1.col-sm-8 - span.preview-f-def.text-break #{ entry.definition } + span.preview-f-def.text-break!= entry.definition if (structure.hasForeignSynonyms && entry.synonym && entry.synonym.join(", ").length) .preview-foreign-synonyms.mt-1.d-flex.align-items-start.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info SINONIM: + span.smaller-gray-info #{ t('SINONIM:') } .selected-label.flex-grow-1.col-sm-8 - span.preview-f-synonym.text-break #{ entry.synonym.join(", ") } + span.preview-f-synonym.text-break!= entry.synonym.join(', ') hr( class=index === entryData.foreign_entries.length - 1 && !isLastBreakPointRequired ? 'd-none' : '' ) if (structure.hasImages && entryData.image && entryData.image.join(", ").length) #preview-images.preview-images.mt-1.d-flex.align-items-start.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info SLIKA: + span.smaller-gray-info #{ t('SLIKA:') } .flex-grow-1.col-sm-8 a#selected-images.text-decoration-none.text-break( href=entryData.image @@ -99,7 +99,7 @@ mixin entryPreview if (structure.hasAudio && entryData.audio && entryData.audio.join(", ").length) #preview-audio.preview-audio.mt-1.d-flex.align-items-start.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info ZVOK: + span.smaller-gray-info #{ t('ZVOK:') } .flex-grow-1.col-sm-8 a#selected-audio.text-decoration-none.text-break( href=entryData.audio @@ -107,7 +107,7 @@ mixin entryPreview if (structure.hasVideo && entryData.video && entryData.video.join(", ").length) #preview-video.preview-video.mt-1.d-flex.align-items-start.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info VIDEO: + span.smaller-gray-info #{ t('VIDEO:') } .flex-grow-1.col-sm-8 a#selected-video.text-decoration-none.text-break( href=entryData.video diff --git a/express/views/utilities/entry-preview.pug b/express/views/utilities/entry-preview.pug index 49ddc24..bc6f368 100644 --- a/express/views/utilities/entry-preview.pug +++ b/express/views/utilities/entry-preview.pug @@ -1,9 +1,9 @@ -.bg-white.add-padding-1.rounded-3.me-1.g-0 +.bg-white.add-padding-1.rounded-3.me-1.g-0col-sm-4 .dictionary-content-top.mx-0 .mb-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info TERMIN: - .flex-grow-1.col-sm-4 + span.smaller-gray-info= t('TERMIN:') + .flex-grow-1.col-lg-4 if (structure) span#selected-term.bold-blue-subheading.text-break else @@ -24,42 +24,44 @@ //- #headword-table.collapse.flex-column.my-2 //- .card.card-body //- | tabela + + //- Used on content (edit entry) page if (structure) if (structure.hasDomainLabels) #preview-domain-secondary.preview-domain-secondary.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info PODROČNA OZNAKA: - .flex-grow-1.col-sm-4 + span.smaller-gray-info= t('PODROČNA OZNAKA:') + .flex-grow-1.col-lg-4 span#selected-domain-secondary.text-uppercase.text-break if (structure.hasLabel) #preview-label.preview-label.mt-1.d-flex.align-items-baseline.row .d-sm-block.preview-column-width.col-sm-4 - span.smaller-gray-info POJASNILO: - .flex-grow-1.col-sm-4 + span.smaller-gray-info= t('POJASNILO:') + .flex-grow-1.col-lg-4 span#selected-label.text-break if (structure.hasDefinition) #preview-definition.preview-definition.mt-1.d-flex.align-items-baseline.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info DEFINICIJA: - .flex-grow-1.col-sm-4 + span.smaller-gray-info= t('DEFINICIJA:') + .flex-grow-1.col-lg-4 span#selected-definition.fw-bold.text-break if (structure.hasSynonyms) #preview-synonym.preview-synonym.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info SINONIM: - .flex-grow-1.col-sm-4 + span.smaller-gray-info= t('SINONIM:') + .flex-grow-1.col-lg-4 span#selected-synonyms.small-blue-text.text-break if (structure.hasLinks) #preview-linked-term.preview-linked-terms.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info POVEZANI TERMIN: - .flex-grow-1.col-sm-4 + span.smaller-gray-info= t('POVEZANI TERMIN:') + .flex-grow-1.col-lg-4 span#linked-terms.text-break if (structure.hasOther) #preview-other-field.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info DRUGO: - .flex-grow-1.col-sm-4 + span.smaller-gray-info= t('DRUGO:') + .flex-grow-1.col-lg-4 span#selected-other-field.text-break if (structure.hasForeignLanguages) .preview-languages-container.mt-2 @@ -71,22 +73,22 @@ span.smaller-black-info= language.nameSl .preview-foreign-terms.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info TERMIN: - .selected-label.flex-grow-1.col-sm-4 + span.smaller-gray-info= t('TERMIN:') + .selected-label.flex-grow-1.col-lg-4 span.preview-f-term.bold-blue-subheading( id='foreign[' + language.id + '][term]' ) .preview-foreign-definition.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info DEFINICIJA: - .selected-label.flex-grow-1.col-sm-4 + span.smaller-gray-info= t('DEFINICIJA:') + .selected-label.flex-grow-1.col-lg-4 span.preview-f-def( id='foreign[' + language.id + '][definition]' ) .preview-foreign-synonyms.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info SINONIM: - .selected-label.flex-grow-1.col-sm-4 + span.smaller-gray-info= t('SINONIM:') + .selected-label.flex-grow-1.col-lg-4 span.preview-f-synonym( id='foreign[' + language.id + '][synonym]' ) @@ -94,84 +96,85 @@ if (structure.hasImages) #preview-images.preview-images.mt-1.d-flex.align-items-baseline.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info SLIKA: - .flex-grow-1.col-sm-4 + span.smaller-gray-info= t('SLIKA:') + .flex-grow-1.col-lg-4 span#selected-images.text-break if (structure.hasAudio) #preview-audio.preview-audio.mt-1.d-flex.align-items-baseline.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info ZVOK: - .flex-grow-1.col-sm-4 + span.smaller-gray-info= t('ZVOK:') + .flex-grow-1.col-lg-4 span#selected-audio.text-break if (structure.hasVideo) #preview-video.preview-video.mt-1.d-flex.align-items-baseline.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info VIDEO: - .flex-grow-1.col-sm-4 + span.smaller-gray-info= t('VIDEO:') + .flex-grow-1.col-lg-4 span#selected-video.text-break + //- Used on dictionary-structure page(s) else .preview-domain-secondary.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info PODROČNA OZNAKA: + span.smaller-gray-info= t('PODROČNA OZNAKA:') .flex-grow-1.col-sm-4 span#selected-domain-secondary.text-uppercase DAVKI .preview-label.mt-1.d-flex.align-items-baseline.row .d-sm-block.preview-column-width.col-sm-4 - span.smaller-gray-info POJASNILO: + span.smaller-gray-info= t('POJASNILO:') .flex-grow-1.col-sm-4 span#selected-label v nekaterih davčnih sistemih .preview-definition.mt-1.d-flex.align-items-baseline.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info DEFINICIJA: + span.smaller-gray-info= t('DEFINICIJA:') .flex-grow-1.col-sm-4 - span#selected-definition.fw-bold zmanjševanje davčnih obveznosti z izkoriščanjem pravnih praznin in z uporabo metod ter davčnih shem, ki niso nezakonite + span#selected-definition.fw-bol zmanjševanje davčnih obveznosti z izkoriščanjem pravnih praznin in z uporabo metod ter davčnih shem, ki niso nezakonite .preview-synonym.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info SINONIM: + span.smaller-gray-info= t('SINONIM:') .flex-grow-1.col-sm-4 span#selected-synonyms.small-blue-text agresivno davčno načrtovanje, davčno zaobidenje .preview-linked-terms.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info POVEZANI TERMIN: + span.smaller-gray-info= t('POVEZANI TERMIN:') .flex-grow-1.col-sm-4 span#linked-terms davčna zatajitev .preview-other-field.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info DRUGO: + span.smaller-gray-info= t('DRUGO:') .flex-grow-1.col-sm-4 - span#other-field Drugo + span#other-field= t('Drugo') .preview-languages-container.mt-2 hr .preview-foreign-language - span.smaller-black-info angleščina + span.smaller-black-info= t('angleščina') .preview-foreign-terms.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info TERMIN: + span.smaller-gray-info= t('TERMIN:') .selected-label.flex-grow-1.col-sm-4 span.preview-f-term.bold-blue-subheading tax avoidance .preview-foreign-definition.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info DEFINICIJA: + span.smaller-gray-info= t('DEFINICIJA:') .selected-label.flex-grow-1.col-sm-4 span.preview-f-def the use of legal methods to reduce the amount of income tax that an individual or business owes .preview-foreign-synonyms.mt-1.d-flex.align-items-baseline.row .d-sm-block.col-sm-4.preview-column-width - span.smaller-gray-info SINONIM: + span.smaller-gray-info= t('SINONIM:') .selected-label.flex-grow-1.col-sm-4 span.preview-f-synonym aggresive tax planning hr .preview-images.mt-1.d-flex.align-items-baseline.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info SLIKA: + span.smaller-gray-info= t('SLIKA:') .flex-grow-1.col-sm-4 - span#selected-images slika + span#selected-images= t('slika') .preview-audio.mt-1.d-flex.align-items-baseline.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info ZVOK: + span.smaller-gray-info= t('ZVOK:') .flex-grow-1.col-sm-4 - span#selected-audio zvok + span#selected-audio= t('zvok') .preview-video.mt-1.d-flex.align-items-baseline.row .col-sm-4.d-sm-block.preview-column-width - span.smaller-gray-info VIDEO: + span.smaller-gray-info= t('VIDEO:') .flex-grow-1.col-sm-4 - span#selected-video video + span#selected-video= t('video') diff --git a/express/views/utilities/generic-main-panel-header.pug b/express/views/utilities/generic-main-panel-header.pug new file mode 100644 index 0000000..31beff7 --- /dev/null +++ b/express/views/utilities/generic-main-panel-header.pug @@ -0,0 +1,15 @@ +mixin topHeaderContent(headerContent) + #chevrons-left.d-flex + .header-container-divider-left + h2#site-header-title= headerContent.h2 + h1#site-heading= headerContent.h1 + span#text-description.page-description.pe-0!= headerContent.description + if (headerContent.helpLink) + a.ms-2.page-description(href=headerContent.helpLink.linkHref)= headerContent.helpLink.linkText + +mixin mainHeader(headerContent, footerIncluded = true) + #offset-padding.header-section-root.no-side-menu.consultancy-padding-main.flex-grow-0.p-squeeze-lg.mx-xxl-auto.gray-bg.pb-3 + .d-flex.justify-content-between.flex-wrap.flex-row.row + .header-container-left-side.d-flex.col-sm-12 + +topHeaderContent(headerContent) + hr#header-row.mt-1.ms-0.mb-0 diff --git a/express/views/utilities/help-side-nav.pug b/express/views/utilities/help-side-nav.pug index 86d0584..953e49d 100644 --- a/express/views/utilities/help-side-nav.pug +++ b/express/views/utilities/help-side-nav.pug @@ -1,66 +1,76 @@ .help-nav .help-nav-mobile - .mt-0.help-nav-title Pomoč + .mt-0.help-nav-title= t('helpPageTitleHelp', { ns: 'extended' }) button#nav-button.nav-button - img#burger-menu-img(src="/images/burger-menu-button-icon.svg" alt="Meni") + img#burger-menu-img( + src="/images/burger-menu-button-icon.svg" + alt=t('Meni') + ) .nav-title.d-xl-none - span.nav-title.ms-0 Pomoč + span.nav-title.ms-0= t('helpPageTitleHelp', { ns: 'extended' }) - nav.d-block.navbar.flex-column.py-0.slidable + nav.d-block.navbar.flex-column.py-0.slidable.scroller-style nav#help-nav-scrollspy.d-block.nav.nav-pills.help-nav-content.scroller-style .help-links-subgroup - a.help-collapsible.subarea-link.nav-link(href="#help-general") Splošno o portalu - .help-links-subgroup - a.help-collapsible.subarea-link.nav-link(href="#help-search") Iskanje + a.help-collapsible.subarea-link.nav-link(href="#help-general")= t('helpPageTitleAbout', { ns: 'extended' }) nav.d-block.collapsed-content.nav-pills.nav.d-flex.flex-column - a.nav-link.ms-2(href="#help-basic-search") Osnovno iskanje - a.nav-link.ms-2(href="#help-advanced-search") Napredno iskanje + a.nav-link.ms-2(href="#help-registration")= t('helpPageTitleRegistration', { ns: 'extended' }) .help-links-subgroup - a.help-collapsible.subarea-link.nav-link(href="#help-extraction") Luščenje - nav.d-block.collapsed-content.nav-pills.nav.flex-column - a.nav-link.ms-2(href="#help-specialized-corpuses") Specializirani korpusi - a.nav-link.ms-2(href="#help-my-corpuses") Lastni uporabniški korpus - a.nav-link.ms-2(href="#help-corpus-oss") Korpus OSS - a.nav-link.ms-2(href="#help-stop-lists") Seznam neželenih besed - a.nav-link.ms-2(href="#help-extraction-areas") Področja - a.nav-link.ms-2(href="#help-terminology-candidates") Terminološki kandidati + a.help-collapsible.subarea-link.nav-link(href="#help-search")= t('helpPageTitleSearchIndex', { ns: 'extended' }) + nav.d-block.collapsed-content.nav-pills.nav.d-flex.flex-column + a.nav-link.ms-2(href="#help-basic-search")= t('helpPageTitleSearchBasic', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-advanced-search")= t('helpPageTitleSearchAdvanced', { ns: 'extended' }) .help-links-subgroup - a.help-collapsible.subarea-link.nav-link(href="#help-editor") Urejanje + a.help-collapsible.subarea-link.nav-link(href="#help-extraction")= t('helpPageTitleExtractionIndex', { ns: 'extended' }) nav.d-block.collapsed-content.nav-pills.nav.flex-column - a.nav-link.ms-2(href="#help-new-dict") Nov slovar - a.nav-link.ms-2(href="#help-edit-dict") Urejanje lastnosti slovarja - a.nav-link.ms-2(href="#help-users-dict") Uporabniki - a.nav-link.ms-2(href="#help-structure-dict") Struktura slovarskega sestavka - a.nav-link.ms-2(href="#help-comments-dict") Komentiranje - a.nav-link.ms-2(href="#help-content-dict") Urejanje vsebine slovarja - a.nav-link.ms-2(href="#help-import-dict") Uvoz podatkov + a.nav-link.ms-2(href="#help-specialized-corpuses")= t('helpPageTitleExtractionSpecializedCorpora', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-my-corpuses")= t('helpPageTitleExtractionPersonalCorpus', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-corpus-oss")= t('helpPageTitleExtractionOssCorpus', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-stop-lists")= t('helpPageTitleExtractionStopTerms', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-extraction-areas")= t('helpPageTitleExtractionDomains', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-terminology-candidates")= t('helpPageTitleExtractionTermCandidates', { ns: 'extended' }) + .help-links-subgroup + a.help-collapsible.subarea-link.nav-link(href="#help-editor")= t('helpPageTitleEditingIndex', { ns: 'extended' }) + nav.d-block.collapsed-content.nav-pills.nav.flex-column + a.nav-link.ms-2(href="#help-new-dict")= t('helpPageTitleEditingNewDictionary', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-edit-dict")= t('helpPageTitleEditingDictionaryProperties', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-users-dict")= t('helpPageTitleEditingUsers', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-structure-dict")= t('helpPageTitleEditingStructure', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-comments-dict")= t('helpPageTitleEditingComments', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-content-dict")= t('helpPageTitleEditingDictionaryContent', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-import-dict")= t('helpPageTitleEditingImportingData', { ns: 'extended' }) a#help-nav-small-font.help-collapsible.subarea-link.nav-link.ms-2.pt-1( href="#help-content-add" - ) Dodajanje slovarskih sestavkov + )= t('helpPageTitleEditingEntriesIndex', { ns: 'extended' }) nav.d-block.collapsed-content.nav-pills.nav.flex-column - a.nav-link.ms-3(href="#help-content-headword") Oblike, naglasi, izgovor - a.nav-link.ms-3(href="#help-content-domains") Področne oznake - a.nav-link.ms-3(href="#help-content-label") Pojasnilo - a.nav-link.ms-3(href="#help-content-def") Definicja - a.nav-link.ms-3(href="#help-content-syn") Sinonimi - a.nav-link.ms-3(href="#help-content-links") Povezani termini - a.nav-link.ms-3(href="#help-content-other") Drugo - a.nav-link.ms-3(href="#help-content-flang") Tuji jeziki (elementi) - a.nav-link.ms-3(href="#help-content-history") Zgodovina urejanja - a.nav-link.ms-3(href="#help-content-comments") Komentarji - + //- a.nav-link.ms-3(href="#help-content-headword") Oblike, naglasi, izgovor + a.nav-link.ms-3(href="#help-content-domains")= t('helpPageTitleEditingEntriesDomainLabels', { ns: 'extended' }) + a.nav-link.ms-3(href="#help-content-label")= t('helpPageTitleEditingEntriesLabel', { ns: 'extended' }) + a.nav-link.ms-3(href="#help-content-def")= t('helpPageTitleEditingEntriesDefinition', { ns: 'extended' }) + a.nav-link.ms-3(href="#help-content-syn")= t('helpPageTitleEditingEntriesSynonyms', { ns: 'extended' }) + a.nav-link.ms-3(href="#help-content-links")= t('helpPageTitleEditingEntriesRelatedTerms', { ns: 'extended' }) + a.nav-link.ms-3(href="#help-content-flang")= t('helpPageTitleEditingEntriesOtherLanguages', { ns: 'extended' }) + a.nav-link.ms-3(href="#help-content-other")= t('helpPageTitleEditingEntriesOther', { ns: 'extended' }) + a.nav-link.ms-3(href="#help-content-history")= t('helpPageTitleEditingEntriesEditingHistory', { ns: 'extended' }) + a.nav-link.ms-3(href="#help-content-comments")= t('helpPageTitleEditingEntriesComments', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-export-dict")= t('helpPageTitleEditingEntriesDataExport', { ns: 'extended' }) .help-links-subgroup - a.help-collapsible.subarea-link.nav-link(href="#help-consulting") Svetovanje + a.help-collapsible.subarea-link.nav-link(href="#help-consulting")= t('helpPageTitleConsultancyIndex', { ns: 'extended' }) nav.d-block.collapsed-content.nav-pills.nav.flex-column - a.nav-link.ms-2(href="#help-consult-questions") Pošiljanje vprašanj - a.nav-link.ms-2(href="#help-consult-principles") Terminološka načela - a.nav-link.ms-2(href="#help-consult-link") Povezava - a.nav-link.ms-2(href="#help-consult-answers") Objava odgovorov + a.nav-link.ms-2(href="#help-consult-questions")= t('helpPageTitleConsultancyQuestions', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-consult-principles")= t('helpPageTitleConsultancyTerminologyPrinciples', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-consult-link")= t('helpPageTitleConsultancyLink', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-consult-answers")= t('helpPageTitleConsultancyAnswers', { ns: 'extended' }) .help-links-subgroup - a.help-collapsible.subarea-link.nav-link(href="#help-administration") Administracija + a.help-collapsible.subarea-link.nav-link(href="#help-administration")= t('helpPageTitleAdministrationIndex', { ns: 'extended' }) nav.d-block.collapsed-content.nav-pills.nav.flex-column - a.nav-link.ms-2(href="#help-administration-settings") Osnovne nastavitve - a.nav-link.ms-2(href="#help-administration-link") Povezave s portali - a.nav-link.ms-2(href="#help-administration-dictionaries") Slovarji - a.nav-link.ms-2(href="#help-administration-list") Seznam slovarjev - a.nav-link.ms-2(href="#help-administration-basics") Osnovni podatki + a.nav-link.ms-2(href="#help-administration-settings")= t('helpPageTitleAdministrationBasicSettingsIndex', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-administration-settings-portal")= t('helpPageTitleAdministrationBasicSettingsPortal', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-administration-settings-dictionaries")= t('helpPageTitleAdministrationBasicSettingsDictionaries', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-administration-settings-consulting")= t('helpPageTitleAdministrationBasicSettingsConsultancy', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-administration-link")= t('helpPageTitleAdministrationLinksToOtherPortalsIndex', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-administration-link-list")= t('helpPageTitleAdministrationLinksToOtherPortalsList', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-administration-link-dictionaries")= t('helpPageTitleAdministrationLinksToOtherPortalsDictionaries', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-administration-dictionaries")= t('helpPageTitleAdministrationDictionaries', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-administration-users")= t('helpPageTitleAdministrationUsers', { ns: 'extended' }) + a.nav-link.ms-2(href="#help-administration-subfileds")= t('helpPageTitleAdministrationSecondaryDomains', { ns: 'extended' }) diff --git a/express/views/utilities/help-text.pug b/express/views/utilities/help-text.pug deleted file mode 100644 index acfd294..0000000 --- a/express/views/utilities/help-text.pug +++ /dev/null @@ -1,196 +0,0 @@ -h1 Pomoč - -h2#help-general Splošno o portalu - -p Terminološki portal je storitev, ki je bila razvita v okviru projekta #[a(href="https://www.slovenscina.eu//" target="_blank") Razvoj slovenščine v digitalnem okolju]. -p Storitev iskanja po terminološkem portalu je uporabnikom na voljo brez registracije. Za uporabo drugih storitev - #[a(href="https://terminoloski.slovenscina.eu/luscenje" target="_blank") luščenja], #[a(href="https://terminoloski.slovenscina.eu/slovarji/moji" target="_blank") urejanja] terminoloških slovarjev, komentiranje in zastavljanje terminoloških vprašanj v #[a(href="https://terminoloski.slovenscina.eu/svetovanje" target="_blank") svetovalnici] pa je ob prvem obisku potrebna registracija, ob vseh naslednjih pa prijava. Registrirate ali prijavite se lahko se lahko na prvi strani portala desno zgoraj (zavihek Prijava). - -h3#help-registration Registracija - -p Registrirate se lahko na prvi strani portala #[a(href="https://terminoloski.slovenscina.eu" target="_blank") desno zgoraj]. Vnesti morate veljaven elektronski naslov, na katerega boste dobili povezavo za potrditev računa. Če povezave niste dobili, preverite tudi vsiljeno pošto. - -p Vaša registracija bo veljavna, ko boste potrdili, da ste se seznanili s #[a(href="politika-zasebnosti" target="_blank") Politiko zasebnosti] in sprejeli #[a(href="pogoji-uporabe" target="_blank") Pogoje uporabe]. - -h2#help-search Iskanje - -h3#help-basic-search Osnovno iskanje - -p Uporabnik lahko z vpisom iskalnega pogoja v iskalno okno išče po terminih ali delih terminov, ki ga zanimajo. Z vnosom iskalnega niza in potrditvijo se bodo prikazali zadetki, ki ustrezajo iskalnemu pogoju. Zadetki so razvrščeni tako, da so najprej prikazani popolni zadetki med iztočnicami, sledijo zadetki, ki ustrezajo delu iztočnice in nato zadetki, ki jih najdemo v drugem delu slovarskega sestavka. Vsi slovarski sestavki, ki so vključeni v terminološki portal, imajo iztočnico v slovenščini. Nekateri imajo ustreznik v tujem jeziku ali definicijo, nekateri pa oboje in še druge povezane podatke. - -p Iskalni pogoj lahko sestavlja niz črk in znak *, ki nadomešča poljubno število znakov. Terminološki portal je povezan tudi s terminološkimi slovarji na spletišču #[a(href="https://isjfr.zrc-sazu.si/sl/terminologisce" target="_blank") Terminologišče], ki ga upravlja #[a(href="https://isjfr.zrc-sazu.si/sl" target="_blank") Inštitut za slovenski jezik Frana Ramovša ZRC SAZU], ter terminološkimi slovarji na slovarskem portalu #[a(href="https://www.termania.net" target="_blank") Termania], ki ga upravlja podjetje #[a(href="https://www.amebis.si/" target="_blank") Amebis]. - -p Seznam zadetkov vsebuje termin, oznako za definicijo, če jo zadetek ima in izpisane ustreznike v vseh tujih jezikih. S klikom na posamezni termin se odpre celoten slovarski sestavek. - -p Slika: -img(src="/images/Iskalni_zadetki.png" alt="Iskalni zadetki") - -p Filtri omogočajo dodatno razvrščanje zadetkov po #[b jezikih], #[b področjih], #[b slovarjih] in tudi #[b viru], če želite preveriti zadetke povezanega termiološkega portala. - -p Slika: -img(src="/images/Filtri.png" alt="Filtri") - -h3#help-advanced-search Napredno iskanje - -p Napredno iskanje se odpre s klikom na puščico na desnem robu iskalnega okna. Poleg iskalnega niza se lahko v spustnem meniju izbere še izvorni jezik (za zadetke v terminoloških slovarjih drugih portalov), ciljni jezik, področje, slovar in tudi vir. - -p S pritiskom na štiri majhne kvadratke se odpre polje s posebnimi znaki. - -p Slika: -img(src="/images/Napredno_iskanje.png" alt="Napredno_iskanje") - -h2#help-extraction Luščenje - -p Vsak začetek terminološkega dela je zbiranje besedil posameznega strokovnega področja. Na osnovi zbranih besedil se oblikuje specializirani korpus, ki je osnova za luščenje terminoloških kandidatov, torej besed in besednih zvez, ki so v primerjavi s splošnim jezikom značilne za izbrano strokovno področje. Luščenje na terminološkem portalu lahko uporabijo samo registrirani uporabniki. Vsak uporabnik lahko shrani do 5 korpusov in rezultatov luščenja. - -h3#help-specialized-corpuses Specializirani korpusi - -p Specializirani korpusi so zbirke besedil, ki sodijo na določeno strokovno področje in jih sestavljajo sodobna strokovna besedila, ki so uravnotežena po zvrsteh. Najprimernejša besedilna zvrst za luščenje terminologije so učbeniki, saj imajo načeloma vključeno potrebno področno terminologijo in vsebujejo tudi definicije. Ostale besedilne zvrsti, od zaključnih del, zlasti doktorskih disertacij, magistrskih del, tudi diplomskih del, do znanstvenih monografij in znanstvene periodike, so seveda potrebne in zaželene, se pa je treba zavedati, da so lahko precej ožje specializirani in lahko vsebujejo temu primerno manj raznolike področne terminologije, zato je treba poskrbeti, da besedila obravnavajo strokovna področja in podpodročja, ki bi jih radi vključi v slovar. Če nimamo lastnih besedil, lahko poiščemo besedila tudi v korpusu OSS, pri čemer moramo izbrati področje, navadno pa tudi ožje področje in leto izdida, da zamejimo število zadetkov. - -h3#help-my-corpuses Lastni uporabniški korpus - -p Besedila lahko zbere tudi uporabnik sam in ne uporabi besedil, ki so že jezikoslovno označena. Svetujemo, da so vsa besedila shranjena v besedilnem formatu (.txt), ker so rezultati luščenja veliko boljši. Tudi pri lastnem uporabniškem korpusu je treba paziti na pokritost področja, seveda pa se lahko na tak način dopolni terminologija, za katero uporabnik oceni, da je v korpusu ni, vendar oceni, da jo je treba vključiti. - -h3#help-corpus-oss Korpus OSS - -p Korpus OSS združuje vsa besedila, ki so dostopna na #[a(href="https://openscience.si/" target="_blank") Nacionalnem portalu odprte znanosti]. V korpus OSS so vključeni tudi metapodatki o besedilih, zlasti področje, vrsta besedila, leto izida, kar omogoča uporabniku ožanje nabora besedil. Vsa besedila so jezikoslovno označena in segmentirana, zato bo luščenje terminoloških kandidatov hitrejše. - -h3#help-stop-lists Seznami neželenih besed - -p Luščenje bo lahko bolj učinkovito, če boste pripravili tudi seznam neželenih besed, t. i. "stop sezname". Nanje lahko uvsrtite splošne termine, npr. poročilo, tabela, količnik, ali slovnične besede, ki se zaradi svoje pogostosti v korpusu kljub predhodnemu označevanju še vedno pojavijo med zadetki. - -h3#help-extraction-areas Področja - -p Klasifikacija področij na terminološkem portalu temelji na dveh uveljavljenih klasifikacijah, in sicer #[a(href="https://www.arrs.si/sl/gradivo/sifranti/sif-cerif-cercs.asp" target="_blank") Evropski klasifikaciji raziskovalne dejavnosti CERIF] ter klasifikaciji #[a(href="https://eur-lex.europa.eu/browse/eurovoc.html?locale=sl" target="_blank") Eurovoc]. Za lažjo izbiro področja smo pripravili tabelo, ki omogoča pretvorbo med klasifikacijami. - -p #[a(href="pomoc/pdf" target="_blank") Tabela] - -h3#help-terminology-candidates Terminološki kandidati - -p Uporabnik po končanem luščenju dobi obvestilo na e-naslov, ki ga je vnesel ob registraciji, da se je postopek končal. Rezultat luščenja je seznam besed in besednih zvez, ki so v izbranih besedilih v primerjavi s splošnim jezikom značilni in se imenujejo terminološki kandidati. Na kakovost luščenja vplivajo izbrana besedila, in tudi t. i. stop-seznami, s katerimi aplikacija izloči tiste termine ali besede, ki so uvrščene na seznam. S čim bolj natančno pripravljenim seznamom lahko izboljšate seznam izluščenih kandidatov. Svetujemo vam, da sezname shranite v obliki .txt. - -h2#help-editor Urejanje - -p Urejanje je storitev, v kateri lahko registrirani uporabnik sam tvori nove vire. Ureja lahko seznam terminoloških kandidatov, ki jih uvozi #[dodatipovezavo] po koncu luščenja ali pripravi svoj seznam ter ga uvozi v urejevalnik. Pred začetkom mora izpolniti nekatere podatke o slovarju, da lahko začne z delom. Slovar lahko ureja sam ali pa k delu povabi še druge registrirane uporabnike. Pri oblikovanju novih terminoloških virov si lahko pomagate s #[a(href="pomoc/guidance-pdf" target="_blank") smernicami]. - -h3#help-new-dict Nov slovar - -p Z izbiro gumba Nov slovar se odpre stran za vnos osnovnih podatkov o slovarju. Obvezna polja so označena z zvezdico. Področja je mogoče izbrati na spustnem seznamu. Če se vam zdi področje preširoko, lahko dodate tudi podpodročje. Tudi podpodročja so na voljo v spustnem seznamu, zato svetujemo, da najprej preverite, ali je že zapisano, sicer pa bo administrator portala potrdil vaš na novo zapisani predlog. Zanesljivost terminološkega vira se poveča tudi z vpisanimi metapodatki, zato svetujemo, da izpolnite vsa polja in na kratko opišete vsebino in način dela. Z izbiro sestavin slovarskega sestavka lahko vidite, katera polja bodo na voljo v urejevalniku. - -h3#help-edit-dict Urejanje lastnosti slovarja - -p Odpre se navigacijsko okno in polja, kamor je treba vnesti nekaj metapodatkov. Bolj dokumentirani viri so bolj zanesljivi, zato svetujemo, da izpolnite vsa polja. - -p V zavihku Napredno lahko uporabnik izbriše tudi vse dotlej izdelane slovarske sestavke in ohrani metapodatke, lahko pa izbirše tudi celoten slovar. Obe dejanji sta nepovratni. - -p Objava slovarskih sestavkov #[PREVERITI] pomeni, da se pošlje obvestilo administratorju portala, ki preveri, če vir ustreza formalnim zahtevam (ali imajo vsi slovarski sestavki termin v slovenskem jeziku in še definicijo in/ali ustreznik v vsaj enem tujem jeziku), objavi slovarske sestavke. - -h3#help-users-dict Uporabniki - -p Poleg osnovnih podatkov o slovarju v zavihku Uporabniki lahko dodamo nove sodelavce in jim dodelimo uporabniške vloge. Te so namenjene različnim pravicam spreminjanja in urejanja slovarja. V istem zavihku z izbiro gumba Slovar je odprt pošljemo sporočilo administratorju, da želimo slovar objaviti. - -h3#help-structure-dict Struktura slovarskega sestavka - -p Z izbiro sestavin slovarskega sestavka lahko vidite, katera polja bodo na voljo v urejevalniku. Obvezne sestavine so termin v slovenskem jeziku in vsaj še definicija ali ustreznik v enem tujem jeziku. Vsak slovar mora imeti torej najmanj dve vrsti podatkov. Na voljo je več možnosti, pri čemer je smiselno, da imajo izbrane sestavine, npr. ustreznike v hrvaščini pretežno vsi vključeni slovarski sestavki, saj je tako slovar bolj zanesljiv. - -h3#help-comments-dict Komentiranje - -p Komentiranje je namenjeno sporazumevanju med avtorji slovarja in hkrati komunikaciji med uporabniki in avtorji. Vsak uporabnik, ki je administrator slovarja, se lahko odloči, ali bodo komentarji vidni vsem ali le avtorjem. - -h3#help-content-dict Vsebina slovarja - -p Osrednji del urejevalnika je stran s polji za vnos podatkov v slovarskem sestavku. Dokler ni izpolnjen pogoj vnosa dveh vrst podatkov, urejevalnik sporoča, da je slovarski sestavek neveljaven. - -p Slika: -img(src="/images/Neveljavno.png" alt="Neveljavno") - -h3#help-import-dict Uvoz podatkov - -p Shranjeni seznam lahko tudi uvozite. #[Dopolniti] - -h3#help-content-add Dodajanje novih slovarskih sestavkov - -p Nov slovarski sestavek dodamo z izbiro gumba #[b Nov]. Če podatkov ne shranimo in takoj izberemo gumb Nov, se vnos ne shrani. - -h4#help-content-headword Oblike, naglasi, izgovor - -p Terminološki portal je povezan z Leksikalno bazo. Z izbiro aktivne povezave, se boste preselili v novo okno, kjer boste lahko preverili, potrdili ali popravili ponujene podatke. Nekateri podatki v leksikalni bazi so bili pregledani in potrjeni, nekateri pa so strojno ustvarjeni. Pri teh bo zelo dragoceno, če jih boste lahko potrdili ali predlagali popravek. - -h4#help-content-domains Področne oznake - -p Področne oznake so namenjene podrobnejšemu razvrščanju pojmov v manjše pojmovne skupine in niso obvezne, svetujemo jih pri slovarjih, ki obsegajo večjo količino terminov, npr. prek 1000. Področne oznake so povsem poljubne in jih avtorji lahko oblikujejo po svojih željah. - -h4#help-content-label Pojasnilo - -p Pojasnilo je namenjeno zapisu okoliščin, ki sicer niso del pojma, olajšajo pa umestitev pojma. Takšna so npr. časovna pojasnila (#[i do leta 2007], #[i v 19. stoletju], #[i med letoma 1988 in 1992] in natančnejša področna pojasnila (#[i v kazenskem procesnem pravu], #[i v biodinamični pridelavi], #[i pri reševanju na vodi]). - -h4#help-content-def Definicija - -p Definicija umesti pojem v pojmovni sistem in določi nadrejeni pojem ali pojmovno skupino, npr. vse davke opisujemo na enak način in začnemo definicijo z #[i davek], nato pa opišemo značilnosti pojma v razmerju do drugih pojmov v skupini, zlasti osnovne značilnosti in razlike, npr. #[i davek na zapuščino] je davek, ki obdavčuje prenos premoženja umrle osebe na pravne naslednike. - -h4#help-content-syn Sinonimi - -p V nekaterih primerih za en pojem obstajata dve poimenovanji, v nekaterih primerih celo več, zato jih je smiselno zabeležiti. Kljub vsemu pa je to podatek povezan s pojmom, zato je to zapisano pri osnovnem pojmu. Za #[i dokazno sredstvo] se v slovenščini uporabljata tudi termina #[i dokaz] in #[i dokazilo], zato sta zapisana kot sinonimni poimenovanji pri #[i dokaznem sredstvu]. - -h4#help-content-links Povezani termini - -p Pri oblikovanju pojmovnega sistema zabeležimo tudi termine, ki označujejo sorodni pojem, na katerega želimo opozoriti, zato jih izberemo iz nabora že urejenih slovarskih sestavkov. Tako npr. lahko povežemo vse vrste davkov. Pri vključevanju povezav je treba pomisliti na uporabnika, ki vsebine predstavljenega strokovnega področja ne pozna, zato je treba premisliti, katere termine želimo povezati in tako omogočiti popolnejšo informacijo. - -h4#help-content-flang Tuji jeziki - -p V tujem jeziku lahko poleg ustreznika zapišemo tudi definicijo in morebitne znane sinonime. Praviloma kot ustreznik izberemo najpogostejši tuji termin, druge, manj pogoste, pa navedemo kot sinonime v tujem jeziku. Definicija naj bi ustrezala definiciji v slovenščini. Če je povsem drugačna, zlasti, če je citirana iz drugega vira, lahko povzroči dvom o samem pojmu, zato svetujemo, da dodatne informacije dopolnite v polju Drugo. - -h4#help-content-other Drugo - -p V ta del lahko vključite vse dodatne podatke o terminu in pojmu, npr. primere rabe, vir, od koder ste črpali definicije, morebitne pomisleke. - -h4#help-content-history Zgodovina urejanja - -p Proces urejanja slovarja je postopek, ki traja več časa. Odločitve, ki jih sprejmemo, se lahko tudi spremenijo in pogosto ponovno povrnejo na začetno stanje. Urejevalnik nudi vpogled v pretekle shranjene različice. Z izbiro datuma v zaviku urejanje lahko opazujemo spremembe. - -h4#help-content-comments Komentarji - -p Komentiranje je namenjeno sporazumevanju med avtorji slovarja. Z njimi se lahko sporazumeva tudi skupina avtorjev, ki je sicer prostorko oddaljena in sodeluje prek urejevalnika na terminološkem portalu. - -h2#help-consulting Svetovanje - -p Svetovanje je namenjeno registriranim uporabnikom. Pomaga pri terminoloških problemih na področjih, kjer še ni izdelanih slovarskih virov, ali pa odgovora ne najdemo. Svetovanje izvajajo sodelavci #[a(href="https://isjfr.zrc-sazu.si/sl/terminologisce" target="_blank") Terminološke sekcije Inštituta za slovenski jezik Frana Ramovša ZRC SAZU]. - -h3#help-consult-questions Pošiljanje vprašanj - -p Pri postavljanju vprašanj o najprimernejšem terminu so zelo pomembni posredovani podatki. Dovolj podatkov o terminološkem problemu omogoči svetovalcem, da svetujejo, katera rešitev bi bila najbolj ustrezna z vidika terminološke vede. Zato je zelo pomembno, da pri zastavljanju vprašanja v obrazec vnesete čim več podatkov o terminu, predvsem kaj ta pomeni, zelo koristni pa so tudi podatki o besedilih, v katerih se pojavlja, morebitne že obstoječe poimenovalne rešitve (ko več terminov označuje isti pojem), tujejezični ustrezniki itd. - -h3#help-consult-principles Terminološka načela - -p Najpomembnejše terminološko načelo je #[b načelo ustaljenosti], po katerem ima prednost termin, ki se v strokovnih besedilih najpogosteje uporablja; drugo pomembno terminološko načelo je #[b načelo gospodarnosti], po katerem imajo prednost krajši termini; tretje terminološko načelo je #[b načelo jezikovnosistemske ustreznosti], kar pomeni, da mora biti termin praviloma v skladu z jezikovnim sistemom jezika kot celote. Pomembno je tudi #[b jezikovnokulturno načelo], po katerem imajo prednost termini domačega izvora. Terminološka načela niso namenjena temu, da bi jih uporabljali mehanično, zato je vedno treba premisliti, kateremu terminološkemu načelu dati prednost v konkretnem primeru. - -h3#help-consult-link Povezava - -p Vključite v vprašanje tudi morebitne spletne povezave, kjer je uporabljen termin, po katerem sprašujete. - -h3#help-consult-answers Objava odgovorov - -p Odgovor bo objavljen v Terminološki svetovalnici Inštituta za slovenski jezik Frana Ramovša ZRC SAZU in prikazan tudi na Slovenskem terminološkem portalu. Na e-naslov, ki ste ga navedli ob registraciji, boste prejeli odgovor, preden bo objavljen. - -h2#help-administration Administracija - -p Portal je administriran. Sledi opis funkcij in pravic, ki jih ima administrator. - -h3#help-administration-settings Osnovne nastavitve - -p Administratorski del portala omogoča dodeljevanje pooblastil izbranim uporabnikom, ki jih administrator določi za skrbnike slovarjev in/ali svetovalnice. - -h3#help-administration-link Povezave s portali - -p Svoj terminološki portal lahko povežete z drugimi terminološkimi portali in ob soglasju administratorjev teh portalov prikazujete tudi njihove terminološke slovarje. Pri tem morate vnesti podatke o portalu, predvsem pa paziti tudi na podvojene podatke in jih primerno označiti. Dvočrkovne oznake portalov so specifične in unikatne za vsak posamezni portal. - -h3#help-administration-dictionaries Slovarji - -p Med terminološkimi slovarji na povezanem portalu lahko izberete vse slovarje, lahko pa samo nekatere, zlasti slovarje tistih področij, ki jih na vašem terminološkem portalu ni. - -h3#help-administration-list Seznam slovarjev - -p Urejate lahko podatke posameznega terminološkega slovarja ali pa izbrišete nekatere slovarje. - -h3#help-administration-basics Osnovni podatki - -p Vse podatke o terminološkem slovarju lahko dopolnite ali popravite, dodate lahko tudi podatek o ISSN oznaki, kar povečuje zanesljivost posameznega terminološkega vira. diff --git a/express/views/utilities/help-text_en.pug b/express/views/utilities/help-text_en.pug new file mode 100644 index 0000000..1260b7d --- /dev/null +++ b/express/views/utilities/help-text_en.pug @@ -0,0 +1,224 @@ +h1= t('helpPageTitleHelp', { ns: 'extended' }) + +h2#help-general= t('helpPageTitleAbout', { ns: 'extended' }) + +p The Terminology Portal was developed as part of the #[a(href="https://www.slovenscina.eu/" target="_blank") Development of Slovene in a Digital Environment] Project. +p The search function of the Terminology Portal in available to all users without registration. To use other functions - #[a(href="/luscenje" target="_blank") exctraction], #[a(href="/slovarji/moji" target="_blank") editing] of terminology dictionaries, commenting, and taking advantage of the offered #[a(href="/svetovanje" target="_blank") consultations] related to the terminology issues - users need to register before first use and sing in every time they want to use these functions. To register or sign in go to the Terminology Portal home page and find the Registration link in the upper right corner. The Terminology Portal is managed by the Amebis company and the Research Centre of the Slovenian Academy of Sciences and Arts, whose employees will answer your questions sent to #[a(href="mailto:info@terminoloski.slovenscina.eu") info@terminoloski.slovenscina.eu]. + +p The Terminology Portal and the sample collections of the Slovenian Terminology Portal have been created by the associates of the following research institutions and companies: +p #[a(href="https://amebis.si" target="_blank") Amebis]: Miro Romih, Anton Romšak, Luka Romih, Jure Artiček, Miha Stele, Aljaž Grilc +p #[a(href="https://kt.ijs.si/" target="_blank") Jožef Stefan Institute]: Senja Pollak, Hanh Thi Hong Tran, Vid Podpečan, Matej Martinc, Andraž Repar, Marko Pranjić, Nada Lavrač, Andraž Pelicon, Tomaž Erjavec +p #[a(href="https://www.ff.uni-lj.si/" target="_blank") Faculty of Arts, University of Ljubljana]: Špela Vintar +p #[a(href="https://www.fri.uni-lj.si/sl" target="_blank") Faculty of Computer and Information Science, University of Ljubljana]: Boštjan Slivnik, Danijel Skočaj, Marko Robnik Šikonja, Vladimir Batagelj (otherwise from the Faculty of Mathematics and Physics, University of Ljubljana), Ivan Bratko, Peter Rogelj (otherwise from the Faculty of Mathematics, Natural Sciences and Information Technologies, University of Primorska), +p #[a(href="https://www.fu.uni-lj.si/" target="_blank") Faculty of Public Administration, University of Ljubljana]: Polonca Kovač, Maja Klun, Jernej Podlipnik (otherwise a lawyer and a lecturer), Andreja Kostelec (otherwise independent researcher and lecturer), Nika Hudej (otherwise consultant for the Constitutional Court of the Republic of Slovenia) +p #[a(href="https://feri.um.si/" target="_blank") Faculty of Electrical Engineering and Computer Science, University of Maribor]: Marko Ferme, Kristjan Žagar, Ivan Kovačič, Klemen Kac, Milan Ojsteršek, Damjan Strnad, Matjaž Divjak, Marko Bizjak, Matjaž Debevec, Ines Kožuh, Irena Lovrenčič Držanič +p #[a(href="https://www.isjfr.zrc-sazu.si/" target="_blank") Fran Ramovš Institute of the Slovenian Language at the Research Centre of the Slovenian Academy of Sciences and Arts]. Mateja Jemec Tomazin, Simon Atelšek, Tanja Fajfar, Karmen Nemec, Jera Sitar, Mitja Trojar, Mojca Žagar Karer + +h3#help-registration= t('helpPageTitleRegistration', { ns: 'extended' }) + +p To register or sign in, go to the #[a(href="/" target="_blank") upper right corner] of the Terminology Portal home page. You have to enter a valid e-mail address, where we can send a link to confirm your account. If you do not get the link, check the junk folder. + +p Your registration will be completed when you confirm that you have read the #[a(href="/politika-zasebnosti" target="_blank") Privacy Policy] and agree to the #[a(href="/pogoji-uporabe" target="_blank") Terms of Use]. + +h2#help-search= t('helpPageTitleSearchIndex', { ns: 'extended' }) + +h3#help-basic-search= t('helpPageTitleSearchBasic', { ns: 'extended' }) + +p By entering a search query into the search window, users can search by terms or parts of therms they are interested in. Entering the search string and confirming it will return the results that match the search term. The results are sorted so that perfect matches in the headword are shown at the top, followed by partial matches in the headword, and finally by matches in other parts of the dictionary entry. All dictionary entries on the Terminology Portal have headwords in Slovenian. Some of the dictionary entries offer equivalents in other languages or a definition, while others offer both as well as other related information. + +p The search query can include a string of letters and the * character, which can replace any number of letters. The Terminology Portal is also connected to the terminology dictionaries hosted at the #[a(href="https://isjfr.zrc-sazu.si/sl/terminologisce" target="_blank") Terminologišče] website managed by the #[a(href="https://isjfr.zrc-sazu.si/sl" target="_blank") Fran Ramovš Institute of the Slovenian Language at the Research Centre of the Slovenian Academy of Sciences and Arts] and the terminology dictionaries at the #[a(href="https://www.termania.net" target="_blank") Termania] dictionary portal managed by the #[a(href="https://www.amebis.si/" target="_blank") Amebis] company. + +p The list of results shows the term in Slovenian, the definition label if the entry includes one, and the equivalents in all languages that are available. Clicking on the term opens the entires dictionary entry. + +p Image: +img(src="/images/Iskalni_zadetki.png" alt="Search results") + +p Filters allow the users to filter the results by #[b language], #[b domains], #[b dictionaries] and by #[b source], which allows the users to see the results from connected terminology portals. + +p Image: +img(src="/images/Filtri.png" alt="Filters") + +h3#help-advanced-search= t('helpPageTitleSearchAdvanced', { ns: 'extended' }) + +p To open the advanced search option, click the arrow to the right of the search window. In addition to the search query, advanced search allows users to select the source language (for search results from terminology dictionaries on other terminology portals), target language, domain, dictionary, and source. + +p Clicking on the four small squares opens up a window with special characters. + +p Image: +img(src="/images/Napredno_iskanje.png" alt="Advanced search") + +h2#help-extraction= t('helpPageTitleExtractionIndex', { ns: 'extended' }) + +p Every terminological endeavor usually starts by collecting texts related to the specific domain. For further machine processing, it is best to use plain text in the .txt format. A specialized corups of different collected texts is created, which is the basis for extraction of term candidates. Term candidates are words and phrases which are typically used in the selected domain rather than in general language and are suitable for creation of a terminology resource. The extraction function of the Terminology portal can only be used by registered users. The extraction of term candidates is done on a remote server. The user is notified via e-mail when the extraction is successfully completed. Each registered user can save up to 5 personal user corpora and extraction results. + +h3#help-specialized-corpuses= t('helpPageTitleExtractionSpecializedCorpora', { ns: 'extended' }) + +p Specialized corpora are collections of domain-specific texts with a balanced representation of text types. Terminology resources are usually created from contemporary texts. The best texts for extraction of term candidates are handbooks, where the terminology of the domain is usually presented in a systematic way and the terms are often also defined. Other text types, such as graduate degrees, especially doctoral dissertations, Master’s degrees, but also Bachelor’s degrees, as well as scientific monographs and scientific journals, are of course necessary and desired, but one must be aware that they often have a very narrow focus and thus contain less divers domain-specific terminology. It is up to the user to make sure that the selected texts cover all the primary and secondary domains they want to include in the dictionary. If you do not have your own texts, you can search for texts in the OSS Corpus, where you have to select the domain, and usually also provide key words and filter by document type and year of publication to limit the number of results and get more precise results. + +h3#help-my-corpuses= t('helpPageTitleExtractionPersonalCorpus', { ns: 'extended' }) + +p The users can decide to collect their own texts rather than use the texts in the OSS Corups, which have already been annotated with linguistic metadata. We recommend saving all your texts in a plain text format (.txt) as this leads to better extraction results. Using other formats, especially (.pdf), can lead to errors when the document is converted into plain text, which causes the extraction to fail. When creating a personal corups, the user should ensure the selected texts sufficiently represent the domain. This allows the users to improve terminology they believe is missing from the OSS Corpus but needs to be included into the terminology resource they are editing. + +h3#help-corpus-oss= t('helpPageTitleExtractionOssCorpus', { ns: 'extended' }) + +p The OSS Corpus includes all the texts available at the #[a(href="https://openscience.si/" target="_blank") Open Science Slovenia Portal]. The advantage of this corpus is that all the texts have already been annotated with linguistic metadata and segmented, which make the process of extracting term candidates faster. The OSS Corpus also includes textual metadata, such as domain, text type, year of publication, and key words, allowing the user to filter the selection of texts. + +h3#help-stop-lists= t('helpPageTitleExtractionStopTerms', { ns: 'extended' }) + +p The extraction results will be more relevant if the user also prepares a list of unwanted words. the so-called "stop list". These lists can include general terms, such as npr. #[i report], #[i table], #[i quotient], or allow you to exclude the most common domain-related terms, which would be included into the terminology resource anyway and would otherwise make it to the top of the results, so that you can find less common terms. + +h3#help-extraction-areas= t('helpPageTitleExtractionDomains', { ns: 'extended' }) + +p The domain classification used on the Slovenian Terminology Portal has been created specifically for this Portal, but is based on two standard classifications: the #[a(href="https://www.arrs.si/sl/gradivo/sifranti/sif-cerif-cercs.asp" target="_blank") Common European Research Classification Scheme (CERIF)] and the #[a(href="https://eur-lex.europa.eu/browse/eurovoc.html?locale=sl" target="_blank") Eurovoc] classification. To help users select the domains, we created a classification conversion table. + +p #[a(href="pomoc/pdf" target="_blank") Table] + +h3#help-terminology-candidates= t('helpPageTitleExtractionTermCandidates', { ns: 'extended' }) + +p After the extraction is completed, the user will receive a notification about the successful extraction to the e-mail address used for registration. The result of the extraction is a list of words and phrases that are more typical for the selected texts compared to the general use. These words and phrases are called term candidates. The quality of the extraction depends on the selected texts as well as on stop lists, which tell the application which terms or words to exclude from the results. By preparing a detailed stop list, the user can improve the resulting list of term candidates. We recommend saving the lists in a .txt format. + +h2#help-editor= t('helpPageTitleEditingIndex', { ns: 'extended' }) + +p The editing function allows registered users to create new terminology resources. The users can edit a list of term candidates that are imported from the #[a(href="/luscenje" target="_blank") Extractor] after the extraction is completed or prepare their own list of terms and import it into the Editor. Before starting, the user must provide certain information about the dictionary. The dictionary can be edited by a single user or other registered users can be invited to collaborate. When creating new terminology resources, the user can follow the #[a(href="pomoc/guidance-pdf" target="_blank") Guidelines for independent creation of terminology resources]. + +h3#help-new-dict= t('helpPageTitleEditingNewDictionary', { ns: 'extended' }) + +p Select the New dictionary button to start the process by providing basic information about the dictionary. Fields marked with * are mandatory. Domains can be selected from a drop-down menu. If the user feels the selected domain is to broad, secondary domains can be added. Secondary domains can also be selected from a drop-down menu. If you cannot find your secondary domain on the menu, write down your suggestion and it will be approved by the administrator. The reliability of a terminology resource is also based on the provided metadata, which is why we recommend that you fill in all the fields and provide a short description of the content and the methodology. By selecting dictionary entry fields, the user decides which fields will be available in the Editor. + +h3#help-edit-dict= t('helpPageTitleEditingDictionaryProperties', { ns: 'extended' }) + +p A navigation window with metadata fields opens up. Better documented resources are more reliable, which is why we recommend filling in all the fields. + +p The Advanced tab allows the user to delete all previously created dictionary entries while saving the metadata or to delete the entire dictionary. Both of these actions are irreversible. + +p Publishing dictionary entries notifies the Terminology Portal Administrator, who checks whether the resource meets the formal requirements (all dictionary entries should have the term in Slovenian as well as at least a definition and/or equivalent in at least one other language) and publishes the dictionary entries. + +h3#help-users-dict= t('helpPageTitleEditingUsers', { ns: 'extended' }) + +p In addition to adding basic dictionary metadata, the user can also add new collaborators and define their user rights under the Users tab. The registered user who creates a new dictionary automatically has all editing and administration rights. The users with #[i editing rights] can edit almost all data, but cannot delete the dictionary. The users with the terminology and language review rights can only change information in the definition and equivalent fields. Different levels of dictionary administration and editing rights make it easier to trace change history and make decision. By clicking the #[i Publish the dictionary] button in the same tab, the user notifies the Portal Administrator that the dictionary is ready to be published. + +h3#help-structure-dict= t('helpPageTitleEditingStructure', { ns: 'extended' }) + +p By selecting dictionary entry fields, the user decides which fields will be available in the Editor. Mandatory elements of all dictionary entries are the term in Slovenian an at least the definition in Slovenian or the equivalent in at least one other language. This means that every dictionary must contain at least two types of data. Several options are available, but it is recommended that the majority of the dictionary entries in the same dictionary include all selected elements (e.g. equivalents in Coratian), as this makes the dictionary more reliable. + +p In addition to the term in Slovenian and the definition, the user can also add domain labels, which can be selected before starting the editing process or be added at a latter time. Qualifiers, e.g. time- or domain-related qualifiers, should only be added to terms where that makes sense. + +h3#help-comments-dict= t('helpPageTitleEditingComments', { ns: 'extended' }) + +p Comments allow the authors of the dictionary to communicate while the dictionary is being created. After publication, they also allow the users to communicate with the authors. Every user with dictionary administration rights can decide whether the comments will only be visible to the dictionary authors or to all portal users. + +h3#help-content-dict= t('helpPageTitleEditingDictionaryContent', { ns: 'extended' }) + +p The main part of the Editor is a page with fields for adding information to the dictionary entry. Until at least two types of information are entered, the Editor returns a "Dictionary entry invalid" message. + +p Image: +img(src="/images/Neveljavno.png" alt="Invalid") + +h3#help-import-dict= t('helpPageTitleEditingImportingData', { ns: 'extended' }) + +p The user can import a list of term candidates in the Import from folder tab. If you already have a list of terms, make sure that is saved in one of the appropriate formats. During this process you can also delete all dictionary entries you have edited so far. If you only want to add terms to the existing dictionary entries, make sure the #[i Delete existing dictionary entries] box in not checked. + +p Term candidates can also be imported from the Extractor. To do this, select the correct extraction and determine which of the extracted candidates should be included. + +h3#help-content-add= t('helpPageTitleEditingEntriesIndex', { ns: 'extended' }) + +p To add a new dictionary entry, click the #[b New] button. If you click the New button without saving the data, the entry will not be saved. Fill in all the selected fields. Until at least two elements are filled in, the dictionary entries will be marked as invalid. + +h4#help-content-domains= t('helpPageTitleEditingEntriesDomainLabels', { ns: 'extended' }) + +p Domain labels can be used for a more detailed classification of terms into smaller conceptual groups. Domain labels are not mandatory, but we recommend using then in dictionaries with a large number of entries, e.g. in dictionaries with more than 1000 entries. Domain labels are completely optional. The authors can adjust them for each separate dictionary. + +h4#help-content-label= t('helpPageTitleEditingEntriesLabel', { ns: 'extended' }) + +p Qualifiers allows the user to write down the circumstances which are not part of the term, but which make it easier to contextualize it. Authors can use, for example, time-related qualifiers (#[i until 2007], #[i in 19th centurry], #[i between the years 1988 and 1992]) or more detailed domain-related qualifiers (#[i in criminal law], #[i in bio-dynamic farming], #[i in water rescue]). + +h4#help-content-def= t('helpPageTitleEditingEntriesDefinition', { ns: 'extended' }) + +p Definition places the term into the conceptual system and determines the parent term or the conceptual group. For example, all taxes are described in the same way, starting the definition with the term #[i tax] then describing the characteristics of the term in relation to other terms in the group, especially its basic characteristics and differences, e.g. #[i inheritance tax] is a tax levied on transfer of assets of a deceased persons to legal heirs. + +h4#help-content-syn= t('helpPageTitleEditingEntriesSynonyms', { ns: 'extended' }) + +p When there are two or more terms describing the same concept, it makes sense to note that down. As this information is related to the concept, it should be written down together with the main term. In Slovenian, the terms #[i dokaz] and #[i dokazilo] are used for #[i dokazno sredstvo], which is why they are written as synonyms for the term #[i dokazno sredstvo]. + +h4#help-content-links= t('helpPageTitleEditingEntriesRelatedTerms', { ns: 'extended' }) + +p When creating a conceptual system, we should also note the terms that denote a related concept we want to draw attention to, which can be selected from the list of already created dictionary entries. While synonyms can be entered freely, related terms must have their own dictionary entries. This option allows us to connect, for example, all types of taxes, all fire extinguishing agents, all types of decisions, etc. When adding related terms, the authors should consider the perspective of a dictionary user who is not as familiar with the domain. The authors should carefully consider which terms should be related to create a more complete picture rather than simply adding relations for the sake of it, especially if the terms are not on the same level. + +h4#help-content-flang= t('helpPageTitleEditingEntriesOtherLanguages', { ns: 'extended' }) + +p In addition to the equivalent, the definition and any synonyms can also be added in other languages. In general, the most often used term in the other language should be put down as the equivalent, while less often used terms should be noted as synonyms. The definitions in other languages should be equivalent to the definition in Slovenian. If the definition is different, especially if it is cited from another source, this can cause doubts about the concept. We recommend putting any additional information, such as definitions from other sources and citations, into the Other field. + +h4#help-content= t('helpPageTitleEditingEntriesOther', { ns: 'extended' }) + +p This field can be used for any additional information about the term and the concept, e.g. examples of use, the source of the definition, and any other considerations. This field has intentionally been undefined, so that it can be use to include any additional information which is not part of the structured elements. + +h4#help-content-history= t('helpPageTitleEditingEntriesEditingHistory', { ns: 'extended' }) + +p The process of editing a dictionary takes a long time. The decisions we make can be changed and we often return to the starting point. The Editor allows the user see previously saved versions. By selecting the date in the Editing tab, we can observe the changes. The user can see up to 10 saved versions of each individual entry. When the user saves a new version, the oldest version is deleted. The corrections and changes of the dictionary entry will only be saved if the user clicks the Save button. + +h4#help-content-comments= t('helpPageTitleEditingEntriesComments', { ns: 'extended' }) + +p Comments allow the communication between the authors of the dictionary. In the comments for individual terms, the authors can write down their questions and suggest solutions, which is especially helpful when a group of authors collaborate virtually. After the dictionary is made public, all other users can also make comments. The authors can decide whether to publish the entire communication or just the first question and the final answer. + +h3#help-export-dict= t('helpPageTitleEditingEntriesDataExport', { ns: 'extended' }) + +p You can export the saved list of edited dictionary entries in the Export tab. You can select all dictionary entries or only certain ones, e.g. only valid ones, only the ones with terminology or language review, only the edited ones, or different combinations of the above. You can choose between different export formats, such as .xml, .csv, .tsv and .txt. You can save up to five exports of each terminology resource. + +h2#help-consulting= t('helpPageTitleConsultancyIndex', { ns: 'extended' }) + +p The answers to the terminology questions published at the #[a(href="https://isjfr.zrc-sazu.si/sl/terminologisce/svetovanje" target="_blank") Terminology counselling] can be found in the Consultancy. Registered users can also ask new terminology questions. Terminology consulting can help clarify terminology questions for domains with no dictionary resources or when answers cannot be found in the existing resources because new concepts or new methods have developed in the domain. The Consultancy is ran by the #[a(href="https://isjfr.zrc-sazu.si/sl/terminologisce" target="_blank") Terminology section of the Fran Ramovš Institute of the Slovenian Language at the Research Centre of the Slovenian Academy of Sciences and Arts]. The user will receive an answer to the e-mail address used for registration. + +h3#help-consult-questions= t('helpPageTitleConsultancyQuestions', { ns: 'extended' }) + +p When asking about the most appropriate term, it is important to include enough information. Sending enough information about a terminological issue allows the consultants to propose the best solution form the perspective of the terminology science. When asking a question, it is important to put as much information about the term as possible into the contact form, especially an explanation of the meaning of the term in the specific domain, as well as the information on any texts where the term occurs, any already existing solution (when several terms exists for the same concept), any equivalents in other languages, etc. + +h3#help-consult-principles= t('helpPageTitleConsultancyTerminologyPrinciples', { ns: 'extended' }) + +p The most important terminology principle is the #[b principle of established use], which gives preference to the term that is the most often used in specialized and scientific texts. The next important terminology principle is #[b the principle of economy], which gives preference to shorter terms. And the third terminology principle is the #[b language-system adequacy principle], which gives preference to terms that can be placed within the language system. It is also important to take into consideration the #[b linguistic culture principle], which gives preference to the terms of domestic origin before words of foreign origin. Terminology principles are not meant to be used mechanically. When considering a specific terminology issue, it should always be carefully considered which terminology principles to apply. + +h3#help-consult-link= t('helpPageTitleConsultancyLink', { ns: 'extended' }) + +p When posting a terminology question, you should also include any links with examples of use of the term in question. Texts where the term is defined or used in a typical manner are especially useful. + +h3#help-consult-answers= t('helpPageTitleConsultancyAnswers', { ns: 'extended' }) + +p The answers will be published in the Terminological counselling of the Fran Ramovš Institute of the Slovenian Language at the Research Centre of the Slovenian Academy of Sciences and Arts, and also on the Slovenian Terminology Portal. The user will be sent the answer to the e-mail address used for registration before the answer will be published online. + +h2#help-administration= t('helpPageTitleAdministrationIndex', { ns: 'extended' }) + +p The Terminology Portal is administered. Below is the description of functions and rights or the Administrator and co-workers. Terminology Portal users without these rights cannot see the Administration tab. + +h3#help-administration-settings= t('helpPageTitleAdministrationBasicSettingsIndex', { ns: 'extended' }) + +p The Administration part of the Portal allows the Portal Administrator to give rights to selected users who are appointed as Dictionary Administrators and Consultancy Administrators. Dictionary Administrator can approve content to be published on the public part of the Portal, add users (editors of individual terminology resources also have this right), add descriptions, and communicate with the editors of individual terminology resources. Consultancy Administrator for the portals that are not linked to the Terminological counselling of the Fran Ramovš Institute of the Slovenian Language at the Research Centre of the Slovenian Academy of Sciences and Arts assignes the terminology questions to individual consultants and publishes the answers to the public part of the portal. + +h4#help-administration-settings-portal= t('helpPageTitleAdministrationBasicSettingsPortal', { ns: 'extended' }) +p You have to name your portal and write a brief description, which will show on the home page of the portal. The two-letter label is used for data exchange with other portals and for source differentiation. The Slovenian Terminology Portal uses the label TP. + +h4#help-administration-settings-dictionaries= t('helpPageTitleAdministrationBasicSettingsDictionaries', { ns: 'extended' }) +p Under in this tab, you can determine the characteristics of the terminology resources on the portal, specifically the minimal number of entries required to publish a dictionary, the option to approve the publication of new terminology dictionaries, as well as the number of versions and exports that can be saved by individual users. These settings apply to all terminology resources on the portal. + +h4#help-administration-settings-consulting= t('helpPageTitleAdministrationBasicSettingsConsultancy', { ns: 'extended' }) +p In the Consultancy tab, you can select what kind of consulting services you will offer as part of your terminology portal. A link to the Terminological counselling of the Fran Ramovš Institute of the Slovenian Language at the Research Centre of the Slovenian Academy of Sciences and Arts directs all questions to their consultants. You can also decide to select your own Consultancy, which will be manage and edit by you. To do this, you must enter a valid e-mail where you will collect the terminology questions and the URL of the website where the answers will be published. + +h3#help-administration-link= t('helpPageTitleAdministrationLinksToOtherPortalsIndex', { ns: 'extended' }) + +p You can link your terminology portal to other terminology portals and - with approval of the administrators of these portals - show their terminology dictionaries in your results. To to that, you must add the portal data and make sure to mark any duplicate results. The two-letter portal label is specific and unique to each portal. + +h4#help-administration-link-list= t('helpPageTitleAdministrationLinksToOtherPortalsList', { ns: 'extended' }) +p Links to other portals are posted to a special list, which can be edited here. When adding a new link, you must add the name and the two-letter label of the portal you are linking your portal with, as well as a valid URL. + +h4#help-administration-link-dictionaries= t('helpPageTitleAdministrationLinksToOtherPortalsDictionaries', { ns: 'extended' }) +p You can search terminology dictionaries on the connected portal and select the ones you whish to add to the search results on your portal. You can select all the dictionaries or only a few. All you have to do is save your selection. All your choices must be confirmed by the administrator of the linked portal. + +h3#help-administration-dictionaries= t('helpPageTitleAdministrationDictionaries', { ns: 'extended' }) + +p Here, the Portal Administrator and Dictionary Administrator can edit the details of terminology dictionaries. They can change the dictionary status to make it visible to public, add new users and assign them rights, add domain labels, or even delete the entire dictionary or delegate it to another user for editing. + +h3#help-administration-users= t('helpPageTitleAdministrationUsers', { ns: 'extended' }) + +p In this tab, the Administrator can assign administration roles for the portal. Administrator can assign new user roles to individual users and edit their data. + +h3#help-administration-subfileds= t('helpPageTitleAdministrationSecondaryDomains', { ns: 'extended' }) + +p Secondary domains are used for more detailed classification of terminology dictionaries. Here, the Administrator can approve new secondary domains suggested by the users. The Administrator should verify whether the new secondary domains are described properly and named in accordance with the convention. diff --git a/express/views/utilities/help-text_sl.pug b/express/views/utilities/help-text_sl.pug new file mode 100644 index 0000000..4c087ab --- /dev/null +++ b/express/views/utilities/help-text_sl.pug @@ -0,0 +1,227 @@ +h1= t('helpPageTitleHelp', { ns: 'extended' }) + +h2#help-general= t('helpPageTitleAbout', { ns: 'extended' }) + +p Terminološki portal je storitev, ki je bila razvita v okviru projekta #[a(href="https://www.slovenscina.eu/" target="_blank") Razvoj slovenščine v digitalnem okolju]. +p Storitev iskanja po terminološkem portalu je uporabnikom na voljo brez registracije. Za uporabo drugih storitev - #[a(href="/luscenje" target="_blank") luščenja], #[a(href="/slovarji/moji" target="_blank") urejanja] terminoloških slovarjev, komentiranje in zastavljanje terminoloških vprašanj v #[a(href="/svetovanje" target="_blank") svetovalnici] pa je ob prvem obisku potrebna registracija, ob vseh naslednjih pa prijava. Registrirate ali prijavite se lahko se lahko na prvi strani portala desno zgoraj (zavihek Prijava). Terminološki portal upravljajo sodelavci podjetja #[a(href="https://amebis.si" target="_blank") Amebis] in #[a(href="https://isjfr.zrc-sazu.si/sl/terminologisce" target="_blank") Terminološke sekcije Inštituta za slovenski jezik ZRC SAZU], ki vam bodo odgovorili tudi na vprašanja, ki jih boste zastavili na #[a(href="mailto:info@terminoloski.slovenscina.eu") info@terminoloski.slovenscina.eu]. + +p Razvoj terminološkega portala sta koordinirala #[a(href="mailto:info@terminoloski.slovenscina.eu") Mateja Jemec Tomazin] in #[a(href="mailto:info@terminoloski.slovenscina.eu") Miro Romih]. + +p Pri izdelavi terminološkega portala in vzorčnih zbirk na Slovenskem terminološkem portalu so sodelovali sodelavci naslednjih raziskovalnih ustanov in podjetij: +p #[a(href="https://amebis.si" target="_blank") Amebis]: Miro Romih, Anton Romšak, Luka Romih, Jure Artiček, Miha Stele, Aljaž Grilc +p #[a(href="https://www.zrc-sazu.si/sl" target="_blank") ZRC SAZU]: Mateja Jemec Tomazin, Simon Atelšek, Tanja Fajfar, Karmen Nemec, Jera Sitar, Mitja Trojar, Mojca Žagar Karer +p #[a(href="https://kt.ijs.si/" target="_blank") Institut Jožef Stefan]: Senja Pollak, Hanh Thi Hong Tran, Vid Podpečan, Matej Martinc, Andraž Repar, Marko Pranjić, Nada Lavrač, Andraž Pelicon, Tomaž Erjavec +p #[a(href="https://www.ff.uni-lj.si/" target="_blank") UL FF]: Špela Vintar +p #[a(href="https://www.fri.uni-lj.si/sl" target="_blank") UL FRI]: Boštjan Slivnik, Danijel Skočaj, Marko Robnik Šikonja, Vladimir Batagelj (sicer UL FMF), Ivan Bratko, Peter Rogelj (sicer UP FAMNIT) +p #[a(href="https://www.fu.uni-lj.si/" target="_blank") UL FU]: Polonca Kovač, Maja Klun, Jernej Podlipnik (sicer odvetnik in predavatelj), Andreja Kostelec (sicer samostojna raziskovalka in predavateljica), Nika Hudej (sicer svetovalka US RS) +p #[a(href="https://feri.um.si/" target="_blank") UM FERI]: Marko Ferme, Kristjan Žagar, Ivan Kovačič, Klemen Kac, Milan Ojsteršek, Damjan Strnad, Matjaž Divjak, Marko Bizjak, Matjaž Debevec, Ines Kožuh, Irena Lovrenčič Držanič + +h3#help-registration= t('helpPageTitleRegistration', { ns: 'extended' }) + +p Registrirate se lahko na prvi strani portala #[a(href="/" target="_blank") desno zgoraj]. Vnesti morate veljavni elektronski naslov, na katerega boste dobili povezavo za potrditev računa. Če povezave niste dobili, preverite tudi vsiljeno pošto. + +p Vaša registracija bo veljavna, ko boste potrdili, da ste se seznanili s #[a(href="/politika-zasebnosti" target="_blank") Politiko zasebnosti] in sprejeli #[a(href="/pogoji-uporabe" target="_blank") Pogoje uporabe]. + +h2#help-search= t('helpPageTitleSearchIndex', { ns: 'extended' }) + +h3#help-basic-search= t('helpPageTitleSearchBasic', { ns: 'extended' }) + +p Uporabnik lahko z vpisom iskalnega pogoja v iskalno okno išče po terminih ali delih terminov, ki ga zanimajo. Z vnosom iskalnega niza in potrditvijo se bodo prikazali zadetki, ki ustrezajo iskalnemu pogoju. Zadetki so razvrščeni tako, da so najprej prikazani popolni zadetki med iztočnicami, sledijo zadetki, ki ustrezajo delu iztočnice in nato zadetki, ki so v drugem delu slovarskega sestavka. Vsi slovarski sestavki, ki so vključeni v terminološki portal, imajo termin v slovenščini. Nekateri imajo dodan termin v tujem jeziku ali definicijo, nekateri pa oboje in še druge povezane podatke. + +p Iskalni pogoj lahko sestavlja niz črk in znak *, ki nadomešča poljubno število znakov. Terminološki portal je povezan tudi s terminološkimi slovarji na spletišču #[a(href="https://isjfr.zrc-sazu.si/sl/terminologisce" target="_blank") Terminologišče], ki ga upravlja #[a(href="https://isjfr.zrc-sazu.si/sl" target="_blank") Inštitut za slovenski jezik Frana Ramovša ZRC SAZU], ter terminološkimi slovarji na slovarskem portalu #[a(href="https://www.termania.net" target="_blank") Termania], ki ga upravlja podjetje #[a(href="https://www.amebis.si/" target="_blank") Amebis]. + +p Seznam zadetkov vsebuje termin, oznako za definicijo, če jo zadetek ima, in izpisane termine v vseh tujih jezikih. Termini so izpisani v krepkem tisku, njihovi sinonimi pa v navadnem tisku. S klikom na posamezni termin se odpre celoten slovarski sestavek. + +p Slika: +img(src="/images/Iskalni_zadetki.png" alt="Iskalni zadetki") + +p Filtri omogočajo dodatno razvrščanje zadetkov po #[b jezikih], #[b področjih], #[b slovarjih] in tudi #[b viru], če želite preveriti zadetke povezanega termiološkega portala. + +p Slika: +img(src="/images/Filtri.png" alt="Filtri") + +h3#help-advanced-search= t('helpPageTitleSearchAdvanced', { ns: 'extended' }) + +p Napredno iskanje se odpre s klikom na puščico na desnem robu iskalnega okna. Poleg iskalnega niza se lahko v spustnem meniju izbere še jezik iskanja (pri čemer bodo termini v tujih jezikih navedeni v skupini #[i ISKANI NIZ JE BIL NAJDEN #[b V DRUGI VSEBINI] SLOVARSKIH SESTAVKOV]), ciljni jezik, področje, slovar in tudi vir. + +p S pritiskom na štiri majhne kvadratke se odpre polje s posebnimi znaki. + +p Slika: +img(src="/images/Napredno_iskanje.png" alt="Napredno iskanje") + +h2#help-extraction= t('helpPageTitleExtractionIndex', { ns: 'extended' }) + +p Vsak začetek terminološkega dela je povezan z zbiranjem besedil s posameznega strokovnega področja. Za strojno obdelavo so najprimernejša gola besedila, torej v formatu .txt. Na osnovi različnih zbranih besedil se oblikuje specializirani korpus, ki je osnova za luščenje terminoloških kandidatov, torej besed in besednih zvez, ki so v primerjavi s splošnim jezikom značilne za izbrano strokovno področje in so primerne za oblikovanje terminološkega vira. Luščenje na terminološkem portalu lahko uporabljajo samo registrirani uporabniki. Luščenje terminoloških kandidatov poteka na oddaljenem strežniku, uporabnik pa je o uspešno zaključenem luščenju obveščen po elektronski pošti. Vsak registrirani uporabnik lahko shrani do 5 lastnih uporabniških korpusov in rezultatov luščenja. + +h3#help-specialized-corpuses= t('helpPageTitleExtractionSpecializedCorpora', { ns: 'extended' }) + +p Specializirani korpusi so zbirke besedil, ki sodijo na določeno strokovno področje in jih sestavljajo strokovna besedila, ki so uravnotežena po zvrsteh. Za terminološke vire praviloma izbiramo sodobna besedila. Najprimernejša besedilna zvrst za luščenje terminoloških kandidatov so učbeniki, saj imajo načeloma sistematično vključeno potrebno področno terminologijo in zelo pogosto vsebujejo tudi definicije. Ostale besedilne zvrsti, od zaključnih del, zlasti doktorskih disertacij, magistrskih del, tudi diplomskih del, do znanstvenih monografij in znanstvene periodike, so seveda potrebne in zaželene, se pa je treba zavedati, da so lahko precej ožje specializirane in lahko vsebujejo temu primerno manj raznoliko področno terminologijo. Uporabnik mora zato poskrbeti, da izbrana besedila obravnavajo vsa strokovna področja in podpodročja, ki bi jih radi vključil v slovar. Če nimamo lastnih besedil, lahko poiščemo besedila tudi v korpusu OSS, pri čemer moramo izbrati področje, navadno pa tudi ključne besede, vrsto dokumentov in leto izida, da zamejimo število zadetkov in s tem dobimo natančnejše rezultate. + +h3#help-my-corpuses= t('helpPageTitleExtractionPersonalCorpus', { ns: 'extended' }) + +p Besedila lahko zbere tudi uporabnik sam in ne uporabi besedil, ki so že predhodno jezikoslovno označena in so vključena v korpus OSS. Svetujemo, da so vsa besedila shranjena v besedilnem formatu (.txt), ker so rezultati luščenja veliko boljši. Pri drugih formatih, zlasti pri (.pdf), lahko pride do napak pri pretvorbi v golo besedilo, zato se luščenje prekine. Tudi pri lastnem uporabniškem korpusu je treba zagotoviti vsebinsko pokritost področja, na tak način se lahko dopolni terminologija, za katero uporabnik oceni, da je v korpusu OSS ni, vendar jo je treba vključiti v terminološki vir, ki ga ureja. + +h3#help-corpus-oss= t('helpPageTitleExtractionOssCorpus', { ns: 'extended' }) + +p Korpus OSS združuje vsa besedila, ki so dostopna na #[a(href="https://openscience.si/" target="_blank") Nacionalnem portalu odprte znanosti]. Prednost korpusa je, da so vsa besedila že jezikoslovno označena in segmentirana, zato je postopek luščenja terminoloških kandidatov hitrejši. V korpus OSS so vključeni tudi metapodatki o besedilih, zlasti področje, vrsta besedila, leto izida in ključne besede, kar uporabniku omogoča ožanje nabora besedil. Po korpusu OSS lahko uporabniki iščejo tudi v konkordančnikih, dostopnih na #[a(href="https://www.clarin.si/info/konkordance/" target="_blank") Clarin.si]. + +h3#help-stop-lists= t('helpPageTitleExtractionStopTerms', { ns: 'extended' }) + +p Luščenje bo lahko bolj učinkovito, če bo uporabnik pripravil tudi seznam neželenih besed, t. i. "stop sezname". Nanje je smiselno uvrstiti splošne termine, npr. #[i poročilo], #[i tabela], #[i količnik], ali termine, ki se zaradi svoje temeljnosti v vsakem primeru uvrščajo v terminološki vir, zato bi bili na vrhu seznamov, uporabnika pa zanimajo manj pogosti termini. + +h3#help-extraction-areas= t('helpPageTitleExtractionDomains', { ns: 'extended' }) + +p Klasifikacija področij na terminološkem portalu je pripravljena posebej za Slovenski terminološki portal, vendar temelji na dveh uveljavljenih klasifikacijah, in sicer #[a(href="https://www.arrs.si/sl/gradivo/sifranti/sif-cerif-cercs.asp" target="_blank") Evropski klasifikaciji raziskovalne dejavnosti CERIF] ter klasifikaciji #[a(href="https://eur-lex.europa.eu/browse/eurovoc.html?locale=sl" target="_blank") Eurovoc]. Za lažjo izbiro področja smo pripravili tabelo, ki omogoča pretvorbo med klasifikacijami. + +p #[a(href="pomoc/pdf" target="_blank") Tabela] + +h3#help-terminology-candidates= t('helpPageTitleExtractionTermCandidates', { ns: 'extended' }) + +p Uporabnik po končanem luščenju dobi obvestilo na e-naslov, ki ga je vnesel ob registraciji, da se je postopek končal. Rezultat luščenja je seznam besed in besednih zvez, ki so v izbranih besedilih v primerjavi s splošnim jezikom značilne in se imenujejo terminološki kandidati. Na kakovost luščenja vplivajo izbrana besedila in tudi seznami s stop termini, s katerimi aplikacija izloči tiste termine ali besede, ki so uvrščene na seznam. S čim bolj natančno pripravljenim seznamom lahko izboljšate seznam izluščenih terminoloških kandidatov. Svetujemo vam, da sezname shranite v obliki .txt. + +h2#help-editor= t('helpPageTitleEditingIndex', { ns: 'extended' }) + +p Urejanje je storitev, v kateri lahko registrirani uporabnik sam tvori nove terminološke vire. Ureja lahko seznam terminoloških kandidatov, ki jih uvozi iz #[a(href="/luscenje" target="_blank") luščilnika] po koncu luščenja ali pripravi svoj seznam terminov ter ga uvozi v urejevalnik. Pred začetkom mora izpolniti nekatere podatke o slovarju, da lahko začne z delom. Slovar lahko ureja sam ali pa k delu povabi še druge registrirane uporabnike. +p Pri oblikovanju novih terminoloških virov si lahko uporabnik pomaga s #[a(href="pomoc/pdf" target="_blank") Smernicami za samotojno izdelavo terminoloških virov]. + +h3#help-new-dict= t('helpPageTitleEditingNewDictionary', { ns: 'extended' }) + +p Z izbiro gumba Nov slovar se odpre stran za vnos osnovnih podatkov o slovarju. Obvezna polja so označena z zvezdico. Področja je mogoče izbrati na spustnem seznamu. Če se vam zdi področje preširoko, lahko dodate tudi podpodročje. Tudi podpodročja so na voljo v spustnem seznamu, zato svetujemo, da najprej preverite, ali je že zapisano, sicer pa bo administrator portala potrdil vaš na novo zapisani predlog. Zanesljivost terminološkega vira se poveča tudi z vpisanimi metapodatki, zato svetujemo, da izpolnite vsa polja in na kratko opišete vsebino in način dela. Z izbiro sestavin slovarskega sestavka lahko vidite, katera polja bodo na voljo v urejevalniku. + +h3#help-edit-dict= t('helpPageTitleEditingDictionaryProperties', { ns: 'extended' }) + +p Odpre se navigacijsko okno in polja, kamor je treba vnesti nekaj metapodatkov. Bolj dokumentirani viri so bolj zanesljivi, zato svetujemo, da izpolnite vsa polja. + +p V zavihku Napredno lahko uporabnik izbriše tudi vse dotlej izdelane slovarske sestavke in ohrani metapodatke, lahko pa izbirše tudi celoten slovar. Obe dejanji sta nepovratni. + +p Objava slovarskih sestavkov pomeni, da se pošlje obvestilo administratorju portala, ki preveri in, če vir ustreza formalnim zahtevam (ali imajo vsi slovarski sestavki termin v slovenskem jeziku in še definicijo in/ali ustreznik v vsaj enem tujem jeziku), objavi slovarske sestavke. + +h3#help-users-dict= t('helpPageTitleEditingUsers', { ns: 'extended' }) + +p Poleg osnovnih podatkov o slovarju v zavihku Uporabniki lahko dodamo nove sodelavce in jim dodelimo uporabniške vloge. Registrirani uporabnik, ki odpre nov slovar, pridobi vse pravice urejanja in spreminjanja. Uporabniki, ki imajo pravico #[i urejanja], lahko spreminjajo skoraj vse podatke, ne morejo pa slovarja izbrisati. Uporabniki, ki imajo pravico strokovno in jezikovno pregledovati, lahko spreminjajo samo podatke v definiciji in pri ustreznikih. Različne pravice spreminjanja in urejanja slovarja olajšajo sledljivost sprememb in sprejemanje odločitev. V istem zavihku z izbiro gumba #[i Slovar odprt] določimo in hkrati pošljemo sporočilo administratorju, da želimo slovar objaviti. + +h3#help-structure-dict= t('helpPageTitleEditingStructure', { ns: 'extended' }) + +p Z izbiro sestavin slovarskega sestavka lahko vidite, katera polja bodo na voljo v urejevalniku. Obvezne sestavine so termin v slovenskem jeziku in vsaj še definicija v slovenskem jeziku ali ustreznik v enem tujem jeziku. Vsak slovar mora imeti torej najmanj dve vrsti podatkov. Na voljo je več možnosti, pri čemer je smiselno, da imajo izbrane sestavine, npr. ustreznike v hrvaščini, pretežno vsi vključeni slovarski sestavki, saj je tako slovar bolj zanesljiv. + +p Poleg termina v slovenščini in definicije lahko dodate področne oznake, ki jih določite pred začetkom urejanja, seveda pa dopolnite lahko tudi pozneje med urejanjem. Pojasnila, npr. časovna, področna, je smiselno dodajati samo določenim terminom. + +h3#help-comments-dict= t('helpPageTitleEditingComments', { ns: 'extended' }) + +p Komentiranje je namenjeno sporazumevanju med avtorji slovarja, dokler se slovar izdeluje, pozneje pa tudi komunikaciji med uporabniki in avtorji. Vsak uporabnik, ki je administrator slovarja, se lahko odloči, ali bodo komentarji vidni vsem uporabnikom portala ali le avtorjem slovarja. + +h3#help-content-dict= t('helpPageTitleEditingDictionaryContent', { ns: 'extended' }) + +p Osrednji del urejevalnika je stran s polji za vnos podatkov v slovarskem sestavku. Dokler ni izpolnjen pogoj vnosa dveh vrst podatkov, urejevalnik sporoča, da je slovarski sestavek neveljaven. + +p Slika: +img(src="/images/Neveljavno.png" alt="Neveljavno") + +h3#help-import-dict= t('helpPageTitleEditingImportingData', { ns: 'extended' }) + +p Shranjeni seznam terminoloških kandidatov lahko tudi uvozite v zavihku Uvoz iz datoteke. Če imate seznam terminov že shranjen, morate preveriti, ali je v enem od ustreznih formatov. Pri tem lahko tudi izbrišete vse slovarske sestavke, ki ste jih uredili dotlej. Če želite termine samo dodati obstoječim slovarskim sestavkom, preverite, da niste označili #[i Izbriši obstoječe slovarske sestavke]. + +p Terminološke kandidate lahko uvozite tudi iz luščilnika, pri čemer morate izbrati ustrezno luščenje in zapisati, katere od vseh izluščenih kandidatov želite vključiti. + +h3#help-content-add= t('helpPageTitleEditingEntriesIndex', { ns: 'extended' }) + +p Nov slovarski sestavek dodamo z izbiro gumba #[b Nov]. Če podatkov ne shranimo in takoj izberemo gumb Nov, se vnos ne shrani. Izpolnimo vsa polja, ki smo jih izbrali. Dokler nista izpolnjeni vsaj dve vrsti podatkov, bodo slovarski sestavki označeni kot neveljavni. + +h4#help-content-domains= t('helpPageTitleEditingEntriesDomainLabels', { ns: 'extended' }) + +p Področne oznake so namenjene podrobnejšemu razvrščanju pojmov v manjše pojmovne skupine in niso obvezne, svetujemo jih pri slovarjih, ki obsegajo večjo količino terminov, npr. prek 1000. Področne oznake so povsem poljubne in jih avtorji lahko oblikujejo po svojih željah za vsak slovar posebej. + +h4#help-content-label= t('helpPageTitleEditingEntriesLabel', { ns: 'extended' }) + +p Pojasnilo je namenjeno zapisu okoliščin, ki sicer niso del pojma, olajšajo pa umestitev pojma. Takšna so npr. časovna pojasnila (#[i do leta 2007], #[i v 19. stoletju], #[i med letoma 1988 in 1992] in natančnejša področna pojasnila (#[i v kazenskem procesnem pravu], #[i v biodinamični pridelavi], #[i pri reševanju na vodi]). + +h4#help-content-def= t('helpPageTitleEditingEntriesDefinition', { ns: 'extended' }) + +p Definicija umesti pojem v pojmovni sistem in določi nadrejeni pojem ali pojmovno skupino, npr. vse davke opisujemo na enak način in začnemo definicijo z #[i davek], nato pa opišemo značilnosti pojma v razmerju do drugih pojmov v skupini, zlasti osnovne značilnosti in razlike, npr. #[i davek na zapuščino] je davek, ki obdavčuje prenos premoženja umrle osebe na pravne naslednike. + +h4#help-content-syn= t('helpPageTitleEditingEntriesSynonyms', { ns: 'extended' }) + +p V nekaterih primerih za en pojem obstajata dve poimenovanji, v nekaterih primerih celo več, zato jih je smiselno zabeležiti. Kljub vsemu pa je to podatek, povezan s pojmom, zato je zapisan pri osnovnem pojmu. Za #[i dokazno sredstvo] se v slovenščini uporabljata tudi termina #[i dokaz] in #[i dokazilo], zato sta zapisana kot sinonimni poimenovanji pri #[i dokaznem sredstvu]. + +h4#help-content-links= t('helpPageTitleEditingEntriesRelatedTerms', { ns: 'extended' }) + +p Pri oblikovanju pojmovnega sistema zabeležimo tudi termine, ki označujejo sorodni pojem, na katerega želimo opozoriti, zato jih izberemo iz nabora že urejenih slovarskih sestavkov. Za razliko od sinonimov, ki jih lahko vpisujemo prosto, morajo povezani termini biti samostojni slovarski sestavki. Tako npr. lahko povežemo vse vrste davkov, vsa gasilna sredstva, vse vrste odločb ipd. Pri vključevanju povezav je treba pomisliti na uporabnika, ki vsebine predstavljenega strokovnega področja ne pozna tako dobro, zato je treba premisliti, katere termine želimo povezati in tako omogočiti popolnejšo informacijo, ne pa zgolj dodajati povezav, zlasti če niso na istih ravneh. + +h4#help-content-flang= t('helpPageTitleEditingEntriesOtherLanguages', { ns: 'extended' }) + +p V tujem jeziku lahko poleg ustreznika zapišemo tudi definicijo in morebitne znane tuje sinonime. Praviloma kot ustreznik izberemo najpogostejši tuji termin, druge, manj pogoste, pa navedemo kot sinonime v tujem jeziku. Definicija naj bi ustrezala definiciji v slovenščini. Če je povsem drugačna, zlasti, če je citirana iz drugega vira, lahko povzroči dvom o samem pojmu, zato svetujemo, da dodatne informacije, npr. definicije iz drugih virov, citate, navedete v polju Drugo. + +h4#help-content= t('helpPageTitleEditingEntriesOther', { ns: 'extended' }) + +p V ta del lahko vključite vse dodatne podatke o terminu in pojmu, npr. primere rabe, vir, od koder ste črpali definicije, morebitne pomisleke. Polje ima namenoma odprto vsebino, da ga lahko uporabijo vsi, ki so v strukturi pogrešali namenski prostor za informacijo, ki so jo želeli vključiti. + +h4#help-content-history= t('helpPageTitleEditingEntriesEditingHistory', { ns: 'extended' }) + +p Proces urejanja slovarja je postopek, ki traja več časa. Odločitve, ki jih sprejmemo, se lahko tudi spremenijo in pogosto ponovno povrnejo na začetno stanje. Urejevalnik nudi vpogled v pretekle shranjene različice. Z izbiro datuma v zaviku urejanje lahko opazujemo spremembe. Uporabnik lahko opazuje do 10 shranjenih različic posameznega slovarskega sestavka. Ko shrani naslednjo, se najstarejša različica izbriše. Popravki in spremembe slovarskega sestavka so shranjeni samo, če pritisnemo na gumb Shrani. + +h4#help-content-comments= t('helpPageTitleEditingEntriesComments', { ns: 'extended' }) + +p Komentiranje je namenjeno sporazumevanju med avtorji slovarja. V komentarjih pri posameznem terminu si lahko zapišejo pomisleke in predlagajo rešitve, kar je uporabno zlasti v primerih, ko skupina avtorjev sodeluje na daljavo. Po odprtju slovarja javnosti lahko komentarje pošiljajo tudi drugi uporabniki. Avtorji se lahko odločijo, ali bodo objavili celotno komunikacijo ali samo osnovno vprašanje in končni odgovor. + +h3#help-export-dict= t('helpPageTitleEditingEntriesDataExport', { ns: 'extended' }) + +p Shranjeni seznam urejenih slovarskih sestavkov lahko tudi izvozite v zavihku Izvoz. Pri tem lahko izberete vse slovarske sestavke ali pa samo nekatere, npr. samo tiste, ki so veljavni, ki so strokovno ali jezikovno pregledani, urejeni oz. njihove različne kombinacije. Izbirate lahko med več formati izvoza, in sicer .xml, .csv, .tsv in .txt. Shranite lahko do pet izvozov posameznega terminološkega vira. + +h2#help-consulting= t('helpPageTitleConsultancyIndex', { ns: 'extended' }) + +p V svetovalnici so zbrani vsi odgovori na terminološka vprašanja, ki so objavljeni v #[a(href="https://isjfr.zrc-sazu.si/sl/terminologisce/svetovanje" target="_blank") Terminološki svetovalnici]. Nova terminološka vprašanja pa lahko zastavijo registrirani uporabniki. Terminološko svetovanje lahko pomaga pri terminoloških problemih na področjih, kjer še ni izdelanih slovarskih virov, ali pa odgovora v njih ne najdemo, ker so se pojavili novi pojmi, nove metode ipd. Svetovanje izvajajo sodelavci #[a(href="https://isjfr.zrc-sazu.si/sl/terminologisce" target="_blank") Terminološke sekcije Inštituta za slovenski jezik Frana Ramovša ZRC SAZU]. Terminološki odgovor boste prejeli tudi na e-naslov, ki ste ga navedli ob registraciji. + +h3#help-consult-questions= t('helpPageTitleConsultancyQuestions', { ns: 'extended' }) + +p Pri postavljanju vprašanj o najprimernejšem terminu so zelo pomembni posredovani podatki. Dovolj podatkov o terminološkem problemu omogoči svetovalcem, da svetujejo, katera rešitev bi bila najbolj ustrezna z vidika terminološke vede. Zato je zelo pomembno, da pri zastavljanju vprašanja v obrazec vnesete čim več podatkov o terminu, predvsem kaj ta označuje na določenem strokovnem področju, zelo koristni pa so tudi podatki o besedilih, v katerih se pojavlja, morebitne že obstoječe poimenovalne rešitve (ko več terminov označuje isti pojem), tujejezični ustrezniki itd. + +h3#help-consult-principles= t('helpPageTitleConsultancyTerminologyPrinciples', { ns: 'extended' }) + +p Najpomembnejše terminološko načelo je #[b načelo ustaljenosti], po katerem ima prednost termin, ki se v strokovnih in znanstvenih besedilih najpogosteje uporablja; drugo pomembno terminološko načelo je #[b načelo gospodarnosti], po katerem imajo prednost krajši termini; tretje terminološko načelo je #[b načelo jezikovnosistemske ustreznosti], kar pomeni, da ima prednost termin, ki ga lahko umestimo v celotni jezikovni sistem. Pomembno je tudi #[b jezikovnokulturno načelo], po katerem imajo prednost termini domačega izvora pred tujkami. Terminološka načela niso namenjena temu, da bi jih uporabljali mehanično, zato je vedno treba premisliti, kateremu terminološkemu načelu dati prednost v konkretnem terminološkem problemu. + +h3#help-consult-link= t('helpPageTitleConsultancyLink', { ns: 'extended' }) + +p V terminološko vprašanje vključite tudi morebitne spletne povezave, kjer je uporabljen termin, po katerem sprašujete. Zlasti koristna so besedila, kjer je termin definiran ali predstavljen v tipični rabi. + +h3#help-consult-answers= t('helpPageTitleConsultancyAnswers', { ns: 'extended' }) + +p Odgovor bo objavljen v Terminološki svetovalnici Inštituta za slovenski jezik Frana Ramovša ZRC SAZU in prikazan tudi na Slovenskem terminološkem portalu. Na e-naslov, ki ste ga navedli ob registraciji, boste prejeli odgovor, preden bo objavljen in prikazan javno. + +h2#help-administration= t('helpPageTitleAdministrationIndex', { ns: 'extended' }) + +p Portal je administriran. Sledi opis funkcij in pravic, ki jih ima administrator in njegovi sodelavci. Uporabniki terminološkega portala, ki nimajo teh pooblastil, zavihka Administracija ne vidijo. + +h3#help-administration-settings= t('helpPageTitleAdministrationBasicSettingsIndex', { ns: 'extended' }) + +p Administratorski del portala omogoča dodeljevanje pooblastil izbranim uporabnikom, ki jih administrator določi za skrbnike slovarjev ter skrbnike svetovalnice. Skrbnik slovarjev lahko dovoljuje objavo podatkov na zunanjem delu portala, dodaja uporabnike, kar je sicer pravica urednika posameznega terminološkega vira, dodaja opise in komunicira z uredniki posamznega terminološkega vira. Skrbnik svetovalnice na portalih, ki niso povezani s Terminološko svetovalnico ISJFR ZRC SAZU, dodeluje prispela terminološka vprašanja posameznim svetovalcem in jih objavlja na zunanjem delu portala. + +h4#help-administration-settings-portal= t('helpPageTitleAdministrationBasicSettingsPortal', { ns: 'extended' }) +p Svoj portal morate poimenovati in o njem napisati nekaj osnovnih značilnosti, ki se bodo prikazovale na naslovni strani portala. Dvočrkovna oznaka je namenjena izmenjavi podatkov z drugimi portali in prepoznavanju virov. Slovenski terminološki portal nosi oznako TP. + +h4#help-administration-settings-dictionaries= t('helpPageTitleAdministrationBasicSettingsDictionaries', { ns: 'extended' }) +p v tem zavihku lahko določite lastnosti terminoloških virov na portalu, zlasti minimalno število sestavkov, ki so pogoj za objavo, možnosti potrjevanja objave novih terminoloških slovarjev, število različic in izvozov slovarja, ki jih lahko hrani posamezni uporabnik. Te nastavitve veljajo za vse terminološke vire na portalu. + +h4#help-administration-settings-consulting= t('helpPageTitleAdministrationBasicSettingsConsultancy', { ns: 'extended' }) +p V zavihku Svetovalnica izberete, katero svetovalnje boste nudili na terminološkem portalu. Povezava s Terminološko svetovalnico ISJFR ZRC SAZU vam omogoča, da se vsa vprašanja usmerijo k njihovim svetovalcem, če pa izberete lastno svetovalnico, pa jo urejate in vodite sami. Vnesti morate veljaven e-naslov, na katerem se zbirajo terminološka vprašanja in naslov spletnega mesta, kjer so zbrani odgovori. + +h3#help-administration-link= t('helpPageTitleAdministrationLinksToOtherPortalsIndex', { ns: 'extended' }) + +p Svoj terminološki portal lahko povežete z drugimi terminološkimi portali in ob soglasju administratorjev teh portalov prikazujete tudi njihove terminološke slovarje. Pri tem morate vnesti podatke o portalu, predvsem pa paziti tudi na podvojene podatke in jih primerno označiti. Dvočrkovne oznake portalov so specifične in unikatne za vsak posamezni portal. + +h4#help-administration-link-list= t('helpPageTitleAdministrationLinksToOtherPortalsList', { ns: 'extended' }) +p Povezave s portali imate predstavljene na posebnem seznamu, ki ga lahko urejate na tem mestu. Pri dodajanju novih povezav je treba dodati ime in dvočrkovno oznako portala, s katerim se povezujete, seveda pa je treba dodati tudi veljaven URL. + +h4#help-administration-link-dictionaries= t('helpPageTitleAdministrationLinksToOtherPortalsDictionaries', { ns: 'extended' }) +p Na povezanem portalu lahko poiščete in izberete tiste terminološke slovarje, s katerimi želite dopolniti iskalne zadetke na svojem portalu. Lahko izberete vse slovarje ali le nekatere. Svoje izbire morate shraniti. Vse vaše izbire mora potrditi tudi administrator povezanega portala. + +h3#help-administration-dictionaries= t('helpPageTitleAdministrationDictionaries', { ns: 'extended' }) + +p Administrator in skrbnik slovarjev lahko na tem mestu urejata vse podrobnosti o terminoloških slovarjih. Pri tem lahko spremenita status, da postane viden na javnem delu portala, doda nove uporabnike in jim določi pravice, dopolni področne oznake, lahko pa tudi celoten slovar izbriše ali ga dodeli za urejanje drugemu uporabniku. + +h3#help-administration-users= t('helpPageTitleAdministrationUsers', { ns: 'extended' }) + +p Zavihek je namenjen določanju administratorskih in skrbniških vlog na portalu. Administrator lahko posameznim uporabnikom dodaja nove uporabniške vloge in ureja njihove podatke. + +h3#help-administration-subfileds= t('helpPageTitleAdministrationSecondaryDomains', { ns: 'extended' }) + +p Podpodročja so namenjena podrobnejšemu razvrščanju terminoloških slovarjev. Administrator lahko na tem mestu potrjuje nova podpodročja, ki so jih predlagali uporabniki. Administrator naj na tem mestu preverja, če so nova podpodročja primerno opisana in poimenovana v skladu z drugimi. diff --git a/express/views/utilities/keyboard.pug b/express/views/utilities/keyboard.pug index 1fa7792..35ba5da 100644 --- a/express/views/utilities/keyboard.pug +++ b/express/views/utilities/keyboard.pug @@ -9,4 +9,3 @@ mixin keyboard(width=null, height=null, offsetX=null, offsetY=null, keys= ["č", each key in keys span.m-1.d-flex.align-content- button.kbd-key= key - //- script(nonce=cspNonce src="/javascripts/keyboard.js") // added in the layout.pug diff --git a/express/views/utilities/modal-alert-mixin.pug b/express/views/utilities/modal-alert-mixin.pug index f1feaf1..5016eff 100644 --- a/express/views/utilities/modal-alert-mixin.pug +++ b/express/views/utilities/modal-alert-mixin.pug @@ -1,4 +1,4 @@ -mixin alertModal(modalId="alert-modal", acceptBtn="Uporabi", cancelBtn="Zapri", acceptBtnId= "accept-btn", cancelBtnId= "cancel-btn", alertMainText = "Ali res želite izbrisati vnos? Dejanja ni mogoče razveljaviti.", alertContentTextId = "modal-alert-text") +mixin alertModal(modalId="alert-modal", acceptBtn=t("Uporabi"), cancelBtn=t("Zapri"), acceptBtnId= "accept-btn", cancelBtnId= "cancel-btn", alertMainText = t("Ali res želite izbrisati vnos? Dejanja ni mogoče razveljaviti."), alertContentTextId = "modal-alert-text") .modal.fade( id=modalId tabindex="-1" @@ -8,7 +8,7 @@ mixin alertModal(modalId="alert-modal", acceptBtn="Uporabi", cancelBtn="Zapri", .modal-dialog.modal-lg .modal-content .modal-header - h5#modal-alert-label.modal-title.modal-title-blue Pozor + h5#modal-alert-label.modal-title.modal-title-blue #{ t('Pozor') } button.btn-close( type="button" data-bs-dismiss="modal" diff --git a/express/views/utilities/modal-alert.pug b/express/views/utilities/modal-alert.pug index bcc06a0..510238f 100644 --- a/express/views/utilities/modal-alert.pug +++ b/express/views/utilities/modal-alert.pug @@ -6,7 +6,7 @@ .modal-dialog.modal-lg .modal-content .modal-header - h5#modal-alert-label.modal-title.modal-title-blue Pozor + h5#modal-alert-label.modal-title.modal-title-blue= t('Pozor') button.btn-close( type="button" data-bs-dismiss="modal" @@ -16,13 +16,13 @@ .d-flex.modal-alert.me-3 img(src="/images/alert-circle-modal.svg") .d-flex.modal-alert-content - p#alert-text.normal-gray Ali želite zbrisati ta vnos? + p#alert-text.normal-gray= t('Ali želite zbrisati ta vnos?') .modal-footer.d-flex.justify-content-between.align-items-center button#modal-cancel-btn.btn.btn-secondary( type="button" data-bs-dismiss="modal" - ) Prekliči + )= t('Prekliči') button#modal-use-btn.btn.btn-primary( type="button" data-bs-dismiss="modal" - ) Potrdi + )= t('Potrdi') diff --git a/express/views/utilities/modal-reset-password-info.pug b/express/views/utilities/modal-reset-password-info.pug new file mode 100644 index 0000000..239deed --- /dev/null +++ b/express/views/utilities/modal-reset-password-info.pug @@ -0,0 +1,25 @@ +#reset-pass-info.modal.fade( + tabindex="-1" + aria-labelledby="modal-alert-label" + aria-hidden="true" +) + .modal-dialog.modal-lg + .modal-content + .modal-header + h5#modal-alert-label.modal-title.modal-title-blue #{ t('Obvestilo') } + button.btn-close( + type="button" + data-bs-dismiss="modal" + aria-label="Close" + ) + .modal-body.d-flex.align-items-center + .d-flex.modal-alert.me-3 + img(src="/images/alert-circle-modal.svg") + .d-flex.modal-alert-content + p#alert-text.normal-gray #{ t('Na vaš elektronski naslov smo vam posredovali povezavo za ponastavitev gesla. Prosimo preverite svoj elektronski predal.') } + + .modal-footer.d-flex.justify-content-center.align-items-center + button#modal-fp-info-close.btn.btn-primary( + type="button" + data-bs-dismiss="modal" + ) #{ t('Zapri') } diff --git a/express/views/utilities/modal-reset-password-success.pug b/express/views/utilities/modal-reset-password-success.pug new file mode 100644 index 0000000..9c03a44 --- /dev/null +++ b/express/views/utilities/modal-reset-password-success.pug @@ -0,0 +1,25 @@ +#reset-pass-info.modal.fade( + tabindex="-1" + aria-labelledby="modal-alert-label" + aria-hidden="true" +) + .modal-dialog.modal-lg + .modal-content + .modal-header + h5#modal-alert-label.modal-title.modal-title-blue #{ t('Obvestilo') } + button.btn-close( + type="button" + data-bs-dismiss="modal" + aria-label="Close" + ) + .modal-body.d-flex.align-items-center + .d-flex.modal-alert.me-3 + img(src="/images/alert-circle-modal.svg") + .d-flex.modal-alert-content + p#alert-text.normal-gray #{ t('Uspešno ste ponastavili svoje geslo.') } + + .modal-footer.d-flex.justify-content-center.align-items-center + button#modal-fp-info-close.btn.btn-primary( + type="button" + data-bs-dismiss="modal" + ) #{ t('Zapri') } diff --git a/express/views/utilities/modal-reset-password.pug b/express/views/utilities/modal-reset-password.pug new file mode 100644 index 0000000..1e126be --- /dev/null +++ b/express/views/utilities/modal-reset-password.pug @@ -0,0 +1,29 @@ +#reset-pass-modal.modal.fade( + tabindex="-1" + aria-labelledby="modal-reset-pass-label" + aria-hidden="true" +) + .modal-dialog.modal-lg + .modal-content + .modal-header + h5#modal-alert-label.modal-title.modal-title-blue #{ t('Pozabljeno geslo') } + button.btn-close( + type="button" + data-bs-dismiss="modal" + aria-label="Close" + ) + .modal-body.d-flex.align-items-center.flex-column + .description #{ t('Ste pozabili geslo? Napišite svoje uporabniško ime ali elektronski naslov, ki ste ga uporabili ob registraciji. Na ta naslov vam bomo poslali sporočilo, s pomočjo katerega boste lahko vnesli novo geslo.') } + input#forgot-pass-input.d-block.mt-3.form-control( + type="text" + placeholder=t('Uporabniško ime ali elektronski naslov') + ) + .modal-footer.d-flex.justify-content-between.align-items-center + button#modal-fp-cancel-btn.btn.btn-secondary( + type="button" + data-bs-dismiss="modal" + ) #{ t('Prekliči') } + button#modal-fp-use-btn.btn.btn-primary( + type="button" + data-bs-dismiss="modal" + ) #{ t('Potrdi') } diff --git a/express/views/utilities/modal-response.pug b/express/views/utilities/modal-response.pug index 6728b38..26c22f5 100644 --- a/express/views/utilities/modal-response.pug +++ b/express/views/utilities/modal-response.pug @@ -1,4 +1,4 @@ -mixin responseModal(responseId="response-modal", understandBtn = "Razumem", understandBtnId = "understand-btn", understandMainText = "Ali res želite izbrisati vnos? Dejanja ni mogoče razveljaviti.") +mixin responseModal(responseId="response-modal", understandBtn = t("Razumem"), understandBtnId = "understand-btn", understandMainText = t("Ali res želite izbrisati vnos? Dejanja ni mogoče razveljaviti."), secondaryText = '') .modal.fade( id=responseId tabindex="-1" @@ -8,15 +8,16 @@ mixin responseModal(responseId="response-modal", understandBtn = "Razumem", unde .modal-dialog.modal-lg .modal-content .modal-header - h5#modal-alert-label.modal-title.modal-title-blue Obvestilo + h5#modal-alert-label.modal-title.modal-title-blue #{ t('Obvestilo') } button.btn-close( type="button" data-bs-dismiss="modal" aria-label="Close" ) .modal-body.d-flex.justify-content-between.align-items-center - .d-flex.modal-alert-content + .modal-alert-content p#response-modal-text.normal-gray= understandMainText + p.normal-gray= secondaryText .modal-footer.d-flex.justify-content-end.align-items-center button.btn.btn-secondary( id=understandBtnId diff --git a/express/views/utilities/pager.pug b/express/views/utilities/pager.pug index 91c3989..2d0a275 100644 --- a/express/views/utilities/pager.pug +++ b/express/views/utilities/pager.pug @@ -1,24 +1,34 @@ //- TODO 1. remove placeholders, 2. consider solving it with mixin? mixin pager(currentPage = 1, id="pagination") - .pager(id=id) - button.first-page.me-3.no-bg-and-borders(disabled=currentPage === 1) - img(src="/images/chevron-double-left.svg" alt="") - button.previous-page.me-3.no-bg-and-borders(disabled=currentPage === 1) - img(src="/images/chevron-left.svg" alt="") - form.me-3.no-bg-and-border - input.pager-input.me-1.text-gray-1( + .pager.d-flex(id=id) + button.d-flex.first-page.px-2.py-1.align-self-center.no-bg-and-borders( + disabled=currentPage === 1 + ) + img.align-self-center.align-self-center.d-flex( + src="/images/chevron-double-left.svg" + alt="" + ) + button.d-flex.previous-page.px-2.py-1.align-self-center.align-self-center.no-bg-and-borders( + disabled=currentPage === 1 + ) + img.align-self-center.d-flex(src="/images/chevron-left.svg" alt="") + form.mx-2.no-bg-and-border.d-flex + input.d-flex.align-self-center.pager-input.me-1.text-gray-1( name="page" value=currentPage disabled=!numberOfAllPages || numberOfAllPages <= 1 ) - span.me-1.text-gray-1= ' / ' - span.pages-total.text-gray-1= numberOfAllPages > 0 ? numberOfAllPages : 1 - button.next-page.me-3.no-bg-and-borders( + span.d-flex.align-self-center.me-1.text-gray-1= ' / ' + span.d-flex.align-self-center.pages-total.text-gray-1= numberOfAllPages > 0 ? numberOfAllPages : 1 + button.d-flex.next-page.px-2.py-1.align-self-center.align-self-center.no-bg-and-borders( disabled=!numberOfAllPages || currentPage >= numberOfAllPages ) - img(src="/images/chevron-right-2.svg" alt="") - button.last-page.no-bg-and-borders( + img.align-self-center.d-flex(src="/images/chevron-right-2.svg" alt="") + button.d-flex.px-2.py-1.align-self-center.align-self-center.last-page.no-bg-and-borders( disabled=!numberOfAllPages || currentPage >= numberOfAllPages ) - img(src="/images/chevron-double-right.svg" alt="") + img.align-self-center.d-flex( + src="/images/chevron-double-right.svg" + alt="" + ) diff --git a/express/views/utilities/response-pug-wrapper/domainLabelLister.pug b/express/views/utilities/response-pug-wrapper/domainLabelLister.pug new file mode 100644 index 0000000..9a3a8b0 --- /dev/null +++ b/express/views/utilities/response-pug-wrapper/domainLabelLister.pug @@ -0,0 +1,28 @@ +each result in results + tr + input(type="hidden" name="domainLabelId" value=result.id) + th(scope="row") + if result.isVisible + input.form-check.checkbox-table( + type="checkbox" + name="isVisible" + checked + disabled + ) + else + input.form-check.checkbox-table( + type="checkbox" + name="isVisible" + disabled + ) + td.tdata-area= result.name + td.buttons-group + .table-buttons + button.p-0.table-button-grp.me-3.edit-row-btn(type="button") + img(src="/images/u_edit-alt.svg" alt="") + button.p-0.table-button-grp.delete-row-btn( + type="button" + data-bs-target="#alert-modal" + data-bs-toggle="modal" + ) + img(src="/images/red-trash-icon.svg" alt="") diff --git a/express/views/utilities/response-pug-wrapper/secondaryDomainLister.pug b/express/views/utilities/response-pug-wrapper/secondaryDomainLister.pug new file mode 100644 index 0000000..1b7891e --- /dev/null +++ b/express/views/utilities/response-pug-wrapper/secondaryDomainLister.pug @@ -0,0 +1,29 @@ +each result in results + tr + input(type="hidden" name="secondaryDomainId" value=result.id) + th(scope="row") + if result.isApproved + input.form-check.checkbox-table( + type="checkbox" + name="isApproved" + checked + disabled + ) + else + input.form-check.checkbox-table( + type="checkbox" + name="isApproved" + disabled + ) + td.tdata-area= result.nameSl + td.tdata-translation= result.nameEn + td.buttons-group + .table-buttons + button.p-0.table-button-grp.me-3.edit-row-btn(type="button") + img(src="/images/u_edit-alt.svg" alt="") + button.p-0.table-button-grp.delete-row-btn( + data-bs-target="#alert-modal" + data-bs-toggle="modal" + type="button" + ) + img(src="/images/red-trash-icon.svg" alt="") diff --git a/express/views/utilities/result-detail-side-menu.pug b/express/views/utilities/result-detail-side-menu.pug index 48fbf16..55542c6 100644 --- a/express/views/utilities/result-detail-side-menu.pug +++ b/express/views/utilities/result-detail-side-menu.pug @@ -6,17 +6,17 @@ mixin result_detail(data) src="/images/burger-menu-button-icon.svg" alt="Meni" ) - span#nav-title.nav-title(class=data.termId ? 'd-none-on-big' : '')= data.termId ? '' : 'O slovarju' + span#nav-title.nav-title(class=data.termId ? 'd-none-on-big' : '')= data.termId ? '' : t('O slovarju') nav - ul.rdsm.ps-2rem.pt-0.slidable.pt-3( - class=data.termId ? 'termin-offset-up-wide' : '' + ul.rdsm.ps-2rem.pt-0.slidable.scroller-style.pt-3( + class=data.termId ? 'termin-offset-up-wide' : 'bottom-offset-dict' ) if data.termId a.d-flex.link-back.mb-3.text-decoration-none(href="/") img(src="/images/chevrons-left.svg") - span.nav-title.ms-2.text-header-description-gray.fw-700 Nazaj na zadetke + span.nav-title.ms-2.text-header-description-gray.fw-700 #{ t('Nazaj na zadetke') } if data.termId - li.text-gray-1.fw-500 Slovar + li.text-gray-1.fw-500 #{ t('Slovar') } - //const prevHref=data.termId || data.dictHref const prevHref=data.dictHref @@ -33,14 +33,14 @@ mixin result_detail(data) li.text-gray-1.fw-500= data.authorLabel .text-header-description-gray.fw-400.author-full-name= data.fullAuthorName if data.areas - li.text-gray-1.fw-500 Področje + li.text-gray-1.fw-500 #{ t('Področje') } .text-header-description-gray.fw-400.csv-areas= data.areas if data.subareas - li.text-gray-1.fw-500 Podpodročje + li.text-gray-1.fw-500 #{ t('Podpodročje') } .text-header-description-gray.fw-400.specific-csv-areas= data.subareas if !data.termId if data.languages - li.text-gray-1.fw-500 Jeziki + li.text-gray-1.fw-500 #{ t('Jeziki') } .text-header-description-gray.fw-400= data.languages if data.time_modified li.text-gray-1.fw-500 Spremenjen @@ -49,9 +49,9 @@ mixin result_detail(data) li.text-gray-1.fw-500 Število slovarskih sestavkov .text-header-description-gray.fw-400= data.count_entries if data.issn - li.text-gray-1.fw-500 ISSN + li.text-gray-1.fw-500 #{ t('ISSN') } .text-header-description-gray.fw-400= data.issn - li.text-gray-1.fw-500 Vir + li.text-gray-1.fw-500 #{ t('Vir') } .portal-info //- span.portal-code= data.portalCode span.text-header-description-gray.fw-400= data.portalName diff --git a/express/views/utilities/side-navigation-dictionary-mixin.pug b/express/views/utilities/side-navigation-dictionary-mixin.pug index ed4ebd1..439e2cf 100644 --- a/express/views/utilities/side-navigation-dictionary-mixin.pug +++ b/express/views/utilities/side-navigation-dictionary-mixin.pug @@ -1,3 +1,5 @@ +//- TODO MARK FOR DELETION + mixin sideNavigation(sideNavigationData) .admin-nav .admin-nav-mobile @@ -7,7 +9,7 @@ mixin sideNavigation(sideNavigationData) alt="Meni" ) span.nav-title - ul.admin-nav-content.slidable + ul.admin-nav-content.slidable.scroller-style block admin-nav-content p.side-nav-title Urejanje li.admin-nav-item @@ -20,15 +22,15 @@ mixin sideNavigation(sideNavigationData) p Lastnosti ul.sub-links li - a(href="#" class=sideNavigationData.classNameImeOpis) Ime in opis + a(href="#" class=sideNavigationData.classNameImeOpis) ${ t('Ime in opis') } li - a(href="#" class=sideNavigationData.classNameUporabniki) Uporabniki + a(href="#" class=sideNavigationData.classNameUporabniki) ${ t('Uporabniki') } li - a(href="#" class=sideNavigationData.classNameStruktura) Struktura + a(href="#" class=sideNavigationData.classNameStruktura) ${ t('Struktura') } li - a(href="#" class=sideNavigationData.classNameNapredno) Napredno + a(href="#" class=sideNavigationData.classNameNapredno) ${ t('Napredno') } li - a(href="#" class=sideNavigationData.classNameKomentarji) Komentarji + a(href="#" class=sideNavigationData.classNameKomentarji) ${ t('Komentarji') } li.admin-nav-item a(href="#") img(src="/images/book.svg" alt="") diff --git a/express/views/utilities/table-wrapper.pug b/express/views/utilities/table-wrapper.pug index d88b5c8..11e2e41 100644 --- a/express/views/utilities/table-wrapper.pug +++ b/express/views/utilities/table-wrapper.pug @@ -1,4 +1,5 @@ //-extends TEMPLATE-demo +//- TODO MARK FOR DELETION block table-wrapper table.styled-table diff --git a/express/views/utilities/unordered-list-mixin.pug b/express/views/utilities/unordered-list-mixin.pug index 80e2903..c6bd43e 100644 --- a/express/views/utilities/unordered-list-mixin.pug +++ b/express/views/utilities/unordered-list-mixin.pug @@ -1,3 +1,5 @@ +//- TODO MARK FOR DELETION + mixin listItemClickable(value='', listClassString='', anchorClassString='') li(class=classString) a(class=anchorClassString) = value diff --git a/postgres/init/003_data-domain-secondary.sql b/postgres/init/003_data-domain-secondary.sql index 91aa549..a81ce34 100644 --- a/postgres/init/003_data-domain-secondary.sql +++ b/postgres/init/003_data-domain-secondary.sql @@ -1,155 +1,389 @@ INSERT INTO domain_secondary (name_sl, name_en, approved) VALUES - ('aerobatika', 'aerobatics', TRUE), - ('aerobika', 'aerobics', TRUE), - ('aikido', 'aikido', FALSE), - ('airsoft', 'airsoft', FALSE), - ('alpinizem', 'mountaineering', TRUE), - ('alpska kombinacija', 'alpine combination', FALSE), - ('alpsko smučanje', 'alpine skiing', FALSE), - ('ameriški nogomet', 'American football', FALSE), - ('atletika', 'athletics', TRUE), - ('badminton', 'badminton', TRUE), - ('balinanje', 'bowling', FALSE), - ('balonarstvo', 'ballooning', FALSE), - ('bejzbol', 'baseball', FALSE), - ('biatlon', 'biathlon', TRUE), - ('bilijard', 'billiards', FALSE), - ('bob', 'bob', FALSE), - ('bodibilding', 'bodybuilding', FALSE), - ('boks', 'boxing', TRUE), - ('borilni športi', 'martial arts', FALSE), - ('bowling', 'bowling', FALSE), - ('bungee jumping', 'bungee jumping', FALSE), - ('cestno kolesarstvo', 'road cycling', FALSE), - ('curling', 'curling', FALSE), - ('deskanje', 'surfing', TRUE), - ('deskanje na snegu', 'snowboarding', FALSE), - ('dirka s čolni', 'boat race', FALSE), - ('dirka z avtomobili', 'car race', FALSE), - ('dirka z motorji', 'motorcycle race', FALSE), - ('dirkališčno kolesarstvo (velodrom)', 'racing cycling (velodrome)', FALSE), - ('dresurno jahanje', 'dressage riding', TRUE), - ('drsanje in rolanje', 'skating and rollerblading', FALSE), - ('dviganje uteži', 'weight lifting', TRUE), - ('džu džitsu', 'ju jitsu', FALSE), - ('ekstremni športi', 'extreme sports', FALSE), - ('floorball', 'floorball', FALSE), - ('formula 1', 'formula 1', FALSE), - ('gimnastika', 'gymnastics', TRUE), - ('golf', 'golf', TRUE), - ('gorništvo', 'mountaineering', FALSE), - ('gorsko kolesarstvo', 'mountain biking', FALSE), - ('hitra hoja', 'brisk walking', TRUE), - ('hitrostno drsanje', 'speed skating', TRUE), - ('hitrostno potapljanje in plavanje s plavutmi', 'speed diving and fin swimming', FALSE), - ('hokej na kotalkah', 'roller hockey', FALSE), - ('hokej na ledu', 'ice hockey', TRUE), - ('hokej na rolerjih', 'roller hockey', FALSE), - ('hokej na travi', 'field hockey', FALSE), - ('jadralno letalstvo', 'gliding', FALSE), - ('jadralno padalstvo', 'paragliding', FALSE), - ('jadralstvo', 'sailing', FALSE), - ('jamarstvo', 'caving', FALSE), - ('judo', 'judo', TRUE), - ('kajakaštvo', 'kayaking', TRUE), - ('karate', 'karate', TRUE), - ('karting', 'karting', FALSE), - ('kasaštvo', 'kasachstvo', FALSE), - ('kegljanje', 'bowling', FALSE), - ('kendo', 'kendo', FALSE), - ('kmečki biljard', 'peasant billiards', FALSE), - ('kockboxing', 'kockboxing', FALSE), - ('kolesarstvo', 'cycling', TRUE), - ('košarka', 'basketball', TRUE), - ('kriket', 'cricket', FALSE), - ('kros', 'cross', FALSE), - ('kung-fu', 'kung fu', FALSE), - ('lacrosse', 'lacrosse', FALSE), - ('lokostrelstvo', 'archery', TRUE), - ('maraton', 'marathon', TRUE), - ('met diska', 'with disk', TRUE), - ('met kladiva', 'hammer throw', TRUE), - ('met kopja', 'javelin throw', TRUE), - ('metanje podkve', 'throwing a horseshoe', FALSE), - ('meti', 'target', FALSE), - ('modelarstvo', 'modeling', FALSE), - ('motokros', 'motocross', TRUE), - ('motorizirani športi', 'motorized sports', FALSE), - ('namizni nogomet', 'table football', FALSE), - ('namizni tenis', 'table tennis', FALSE), - ('nogomet', 'football', TRUE), - ('nordijska kombinacija', 'Nordic combination', TRUE), - ('nordijsko smučanje', 'Nordic skiing', FALSE), - ('odbojka', 'volleyball', TRUE), - ('odbojka na mivki', 'beach volleyball', TRUE), - ('orientacija', 'orientation', FALSE), - ('padalstvo', 'parachuting', FALSE), - ('paeyball', 'paeyball', FALSE), - ('paintball', 'paintball', FALSE), - ('paralelni slalom', 'parallel slalom', FALSE), - ('pasja dirka', 'dog race', FALSE), - ('petanke', 'petanque', FALSE), - ('petelinja bitka', 'rooster battle', FALSE), - ('pikado', 'darts', FALSE), - ('plavanje', 'swimming', TRUE), - ('podvodni hokej', 'underwater hockey', FALSE), - ('polaganje rok', 'laying hands', FALSE), - ('polo', 'polo', FALSE), - ('potapljaštvo', 'diving', FALSE), - ('preskakovanje ovir', 'jumping over obstacles', FALSE), - ('ritmična gimnastika', 'rhythmic gymnastics', TRUE), - ('rokoborba', 'wrestling', TRUE), - ('rokomet', 'handball', TRUE), - ('rolanje', 'rollerblading', FALSE), - ('rolkanje', 'skateboarding', FALSE), - ('rugby', 'rugby', TRUE), - ('sabljanje', 'fencing', TRUE), - ('sankanje', 'sledding', TRUE), - ('savate', 'savate', FALSE), - ('sinhrono plavanje', 'synchronous swimming', TRUE), - ('skok ob palici', 'pole vault', TRUE), - ('skok v višino', 'high jump', TRUE), - ('skoki', 'jumps', FALSE), - ('skoki v vodo', 'jumps into the water', FALSE), - ('skoki v vodo z velikih višin', 'jumping into the water from great heights', FALSE), - ('skupinski športi', 'group sports', FALSE), - ('skvoš', 'squash', FALSE), - ('slalom', 'slalom', TRUE), - ('smuk', 'smuk', TRUE), - ('smučanje', 'skiing', TRUE), - ('smučanje in zimski športi', 'skiing and winter sports', FALSE), - ('smučarski skoki', 'ski jumping', TRUE), - ('softball', 'softball', FALSE), - ('streljanje', 'shooting', TRUE), - ('sumo', 'sumo', FALSE), - ('superveleslalom', 'super giant slalom', TRUE), - ('surfanje', 'surfing', FALSE), - ('suvanje krogle', 'pushing the ball', TRUE), - ('taekwondo', 'taekwondo', FALSE), - ('tek na dolge proge', 'running long distances', FALSE), - ('tek na smučeh', 'cross-country skiing', TRUE), - ('tek na srednje proge', 'only on the middle lanes', FALSE), - ('tek čez ovire', 'running over obstacles', TRUE), - ('teki', 'teki', FALSE), - ('tekma kamel', 'camel match', FALSE), - ('telemark smučanje', 'telemark skiing', FALSE), - ('tenis', 'tennis', TRUE), - ('trampolin', 'trampoline', FALSE), - ('triatlon', 'triathlon', TRUE), - ('troskok', 'troskok', TRUE), - ('turno smučanje', 'ski touring', FALSE), - ('ultimate frizbi', 'ultimate frisbee', FALSE), - ('vaterpolo', 'water polo', TRUE), - ('veleslalom', 'giant slalom', TRUE), - ('vertikalno rolkanje', 'vertical skateboarding', FALSE), - ('vodni športi', 'water sports', FALSE), - ('zunanji športi', 'outdoor sports', FALSE), - ('športi moči', 'power sports', FALSE), - ('športi s tarčami', 'target sports', FALSE), - ('športi z loparji', 'racket sports', FALSE), - ('športi z živalmi', 'animal sports', FALSE), - ('športna akrobatika', 'sports acrobatics', FALSE), - ('športna gimnastika', 'gymnastics', FALSE), - ('športno plezanje', 'sport climbing', TRUE), - ('šprint', 'sprint', FALSE); + ('Aerodinamika', 'Aerodynamics', TRUE), + ('Akustika', 'Acustics', TRUE), + ('Algebra', 'Algebra', TRUE), + ('Algoritmi', 'Algorithms', TRUE), + ('Alpinizem', 'Mountain climbing', TRUE), + ('Alternativno zdravljenje', 'Alternative medicine', TRUE), + ('Analizna kemija', 'Analytical chemistry', TRUE), + ('Anatomija', 'Anatomy', TRUE), + ('Anesteziologija', 'Anesthesiology', TRUE), + ('Anorganska kemija', 'Inorganic chemistry', TRUE), + ('Antična mitologija', 'Ancient mythology', TRUE), + ('Aritmetika', 'Arithemetics', TRUE), + ('Atletika', 'Athletics', TRUE), + ('Atomska fizika', 'Atomic physics', TRUE), + ('Avtomatika', 'Automatics', TRUE), + ('Avtomobilizem', 'Motorsport', TRUE), + ('Avtorsko pravo', 'Copyright law', TRUE), + ('Babištvo', 'Midwifery', TRUE), + ('Badminton', 'Badminton', TRUE), + ('Balet', 'Ballet', TRUE), + ('Balonarstvo', 'Ballooning', TRUE), + ('Bančni sistem', 'Banking system', TRUE), + ('Baseball', 'Baseball', TRUE), + ('Besediloslovje', 'Text linguistics', TRUE), + ('Biatlon', 'Biathlon', TRUE), + ('Biljard', 'Billiard', TRUE), + ('Biodinamika', 'Biodynamics', TRUE), + ('Bioetika', 'Bioethics', TRUE), + ('Bolezni', 'Diseases', TRUE), + ('Borilni športi', 'Martial arts', TRUE), + ('Budizem', 'Buddhism', TRUE), + ('Cerkvena glasba', 'Church music', TRUE), + ('Cerkveno pravo', 'Ecclesiastical law', TRUE), + ('Cestni promet', 'Road traffic', TRUE), + ('Civilna zaščita', 'Civil protection service', TRUE), + ('Civilno pravo', 'Civil law', TRUE), + ('Časopisi', 'Journals', TRUE), + ('Čevljarstvo', 'Shoemaking', TRUE), + ('Čipkarstvo', 'Lacemaking', TRUE), + ('Členonožci', 'Arthropod', TRUE), + ('Čutno zaznavanje', 'Sensory perception', TRUE), + ('Daljnovodi', 'Transmission lines', TRUE), + ('Davčno pravo', 'Tax law', TRUE), + ('Davki in carine', 'Taxes and Customs', TRUE), + ('Dedno pravo', 'Law of inheritance', TRUE), + ('Delovno pravo', 'Labour law', TRUE), + ('Denar, valute', 'Money, currencies', TRUE), + ('Dentalna medicina', 'Dental medicine', TRUE), + ('Dermatologija', 'Dermatology', TRUE), + ('Diagnostika', 'Diagnostics', TRUE), + ('Dialektologija', 'Dialectology', TRUE), + ('Dietetika', 'Dietetics', TRUE), + ('Digitalizacija', 'Digitalization', TRUE), + ('Digitalna fotografija', 'Digital photograpy', TRUE), + ('Dinamika tekočin', 'Fluid dynamics', TRUE), + ('Diplomacija', 'Diplomacy', TRUE), + ('Divje živali', 'Wildlife', TRUE), + ('Domače živali', 'Domesticated animals', TRUE), + ('Drsanje', 'Skating', TRUE), + ('Družinsko pravo', 'Family law', TRUE), + ('Duševni razvoj', 'Mental development', TRUE), + ('Duševno zdravje', 'Mental health', TRUE), + ('Dvoživke', 'Amphibian', TRUE), + ('Ekološka pridelava', 'Organic growing', TRUE), + ('Ekosistemi', 'Ecosystems', TRUE), + ('Eksperimentalna kemija', 'Experimental chemistry', TRUE), + ('Elektrarne', 'Power plant', TRUE), + ('Električna energija', 'Electric power', TRUE), + ('Električna mobilnost', 'Electric mobility', TRUE), + ('Elektrika', 'Electricity', TRUE), + ('Elektroenergetski sistemi', 'Electricity system', TRUE), + ('Elektronika', 'Electronics', TRUE), + ('Etimologija', 'Ethymology', TRUE), + ('Evolucija', 'Evolution', TRUE), + ('Farmacevtska biologija', 'Pharmaceutical biology', TRUE), + ('Farmacevtska kemija', 'Pharmaceutical chemistry', TRUE), + ('Farmacevtska tehnologija', 'Pharmaceutical technology', TRUE), + ('Farmakologija', 'Pharmacology', TRUE), + ('Filozofija prava', 'Philosophy of law', TRUE), + ('Finančno pravo', 'Finance law', TRUE), + ('Fitomedicina', 'Phytomedicine', TRUE), + ('Fizikalna kemija', 'Physical chemistry', TRUE), + ('Fiziologija', 'Phisiology', TRUE), + ('Fiziologija rastlin', 'Plant physiology', TRUE), + ('Fluidna tehnika', 'Fluid power equipment', TRUE), + ('Fonetika', 'Phonetics', TRUE), + ('Formula 1', 'Formula 1', TRUE), + ('Fotografske tehnike', 'Photographic Techniques', TRUE), + ('Fotogrametrija', 'Photogrammetry', TRUE), + ('Frazeologija', 'Phraseology', TRUE), + ('Frizerstvo', 'Hair dressing', TRUE), + ('Gasilstvo', 'Firefighting', TRUE), + ('Gastronomija', 'Gastronomy', TRUE), + ('Genetika', 'Genetics', TRUE), + ('Genski inženiring', 'Genetic engineering', TRUE), + ('Geodetske mreže', 'Geodesic grids', TRUE), + ('Geometrija', 'Geometry', TRUE), + ('Gimnastika', 'Gymnastics', TRUE), + ('Ginekologija', 'Gynecology', TRUE), + ('Glodalci', 'Rodentia', TRUE), + ('Golf', 'Golf', TRUE), + ('Gospodarske družbe', 'Companies and corporations', TRUE), + ('Gospodarsko pravo', 'Commercial law', TRUE), + ('Grafika', 'Graphics', TRUE), + ('Hidravlika', 'Hydraulics', TRUE), + ('Higiena', 'Hygiene', TRUE), + ('Hinduizem', 'Hinduism', TRUE), + ('Hokej na ledu', 'Ice hockey', TRUE), + ('Hokej na travi', 'Field hockey', TRUE), + ('Humana ekologija', 'Human ecology', TRUE), + ('Humanitarno pravo', 'Humanitarian law', TRUE), + ('Industrija', 'Industry', TRUE), + ('Infrastruktura', 'Infrastructure', TRUE), + ('Inšpekcija', 'Inspection', TRUE), + ('Inteligentni sistemi', 'Intelligent Systems', TRUE), + ('Interna medicina', 'Internal medicine', TRUE), + ('Internet', 'Internet', TRUE), + ('Islam', 'Islam', TRUE), + ('Jadranje', 'Sailing', TRUE), + ('Jahanje', 'Riding', TRUE), + ('Jamarstvo', 'Caving', TRUE), + ('Jamstvo', 'Surety, guarentee', TRUE), + ('Javne finance', 'Public finance', TRUE), + ('Javni promet', 'Public traffic', TRUE), + ('Javno zdravje', 'Public health', TRUE), + ('Jazz, blues', 'Jazz, blues', TRUE), + ('Jeklarstvo', 'Steel industry', TRUE), + ('Jezikovno modeliranje', 'Language modelling', TRUE), + ('Judovstvo', 'Judaism', TRUE), + ('Kajak, kanu', 'Kayak, canoe', TRUE), + ('Kamnoseštvo', 'Stone-cutting', TRUE), + ('Kartiranje', 'Mapping', TRUE), + ('Kartografske projekcije', 'Geographical projection', TRUE), + ('Kazensko pravo', 'Criminal law', TRUE), + ('Kegljanje', 'Ninepin bowling', TRUE), + ('Kibernetika', 'Cybernetics', TRUE), + ('Kibernetsko in informacijsko delovanje ', 'Cyber and information operations', TRUE), + ('Kiparstvo', 'Sculpture', TRUE), + ('Kirurgija', 'Surgery', TRUE), + ('Klinična farmacija', 'Clinical pharmacy', TRUE), + ('Klinična psihologija', 'Clinical psychology', TRUE), + ('Kolesarjenje', 'Cycling', TRUE), + ('Komorna glasba', 'Chamber music', TRUE), + ('Konfucionizem', 'Confucianism', TRUE), + ('Konkurenčno pravo', 'Competition law', TRUE), + ('Konservatorstvo in restavratorstvo', 'Conservation and restoration', TRUE), + ('Konstrukcije', 'Constructions', TRUE), + ('Konzerviranje', 'Preservation', TRUE), + ('Kopenska vojska ', 'Army', TRUE), + ('Korpusno jezikoslovje', 'Corpus linguistics', TRUE), + ('Košarka', 'Basketball', TRUE), + ('Kovaštvo', 'Blacksmithery', TRUE), + ('Kozmetika', 'Cosmetics', TRUE), + ('Kriptovalute', 'Cryptocurrency', TRUE), + ('Kristalografija', 'Crystalography', TRUE), + ('Krojenje in šivanje', 'Dressmaking', TRUE), + ('Krščanstvo', 'Christianity', TRUE), + ('Krvodajalstvo', 'Blood donation', TRUE), + ('Laserji', 'Lasers', TRUE), + ('Lekarništvo', 'Pharmacy', TRUE), + ('Lekiskologija', 'Lexicology', TRUE), + ('Literarno prevajanje', 'Literary translation', TRUE), + ('Ljudska glasba', 'Folk music', TRUE), + ('Ljudsko pripovedništvo', 'Fairy and folk tales', TRUE), + ('Lokalne oblasti', 'Local governement', TRUE), + ('Lokostrelstvo', 'Archery', TRUE), + ('Lončarstvo', 'Pottery', TRUE), + ('Lov', 'Hunting', TRUE), + ('Lutkarstvo', 'Puppetry', TRUE), + ('Male živali', 'Small animals', TRUE), + ('Marketing', 'Marketing', TRUE), + ('Matematična analiza', 'Mathematical analysis', TRUE), + ('Matematična logika', 'Mathematical logic', TRUE), + ('Matematična statistika', 'Mathematical statistics', TRUE), + ('Medicinska sociologija', 'Medical sociology', TRUE), + ('Medicinski pripomočki', 'Medical devices', TRUE), + ('Mednarodno pravo', 'International law', TRUE), + ('Mehanika', 'Mechanics', TRUE), + ('Mehatronika', 'Mechatronics', TRUE), + ('Mehkužci', 'Mollusca', TRUE), + ('Merilni instrumenti', 'Measuring instruments', TRUE), + ('Mikrodelci', 'Ultra-fine particles', TRUE), + ('Mineralogija', 'Mineralogy', TRUE), + ('Mizarstvo', 'Joinery', TRUE), + ('Mobilne naprave', 'Mobile devices', TRUE), + ('Modno oblikovanje', 'Fashion design', TRUE), + ('Mostovi', 'Bridges', TRUE), + ('Motociklizem', 'Motorcycling', TRUE), + ('Motorna vozila', 'Motor vehicles', TRUE), + ('Naftni derivati', 'Petroleum derivatives', TRUE), + ('Nakit', 'Jewellery', TRUE), + ('Namizne igre', 'Tabletop games', TRUE), + ('Namizni tenis', 'Table tennis', TRUE), + ('Naravne nesreče', 'Natural disasters', TRUE), + ('Naravne znamenitosti', 'Natural sites of special interest', TRUE), + ('Narodnozabavna glasba', 'Pop folk music', TRUE), + ('Neformalno izobraževanje', 'Nonformal learning', TRUE), + ('Nefrologija', 'Nephrology', TRUE), + ('Nega telesa', 'Body care', TRUE), + ('Nevronske mreže', 'Neural network', TRUE), + ('Nogomet', 'Football', TRUE), + ('Noše', 'Costumes', TRUE), + ('Notranja politika', 'Domestic policy', TRUE), + ('Nova duhovna gibanja', 'New Age', TRUE), + ('Numerične metode', 'Numerical methods', TRUE), + ('Numizmatika', 'Numismatics', TRUE), + ('Običaji', 'Folk traditon', TRUE), + ('Oblačila', 'Clothing', TRUE), + ('Obligacijsko pravo', 'Obligation law', TRUE), + ('Obnovljivi viri energije', 'Renewable energy sources', TRUE), + ('Obveščevalno-varnostno delovanje ', 'Military intelligence', TRUE), + ('Odbojka', 'Volleyball', TRUE), + ('Odprti dostop', 'Open access', TRUE), + ('Odškodninsko pravo', 'Tort Law', TRUE), + ('Oftalmologija', 'Ophthalmology', TRUE), + ('Oglarstvo', 'Charcoal making', TRUE), + ('Oglaševanje', 'Advertising', TRUE), + ('Okoljsko pravo', 'Environmental law', TRUE), + ('Okrasne rastline', 'Ornamental plants', TRUE), + ('Opera', 'Opera', TRUE), + ('Operacijski sistemi', 'Operating system', TRUE), + ('Optika', 'Optycs', TRUE), + ('Organizacijska psihologija', 'Organisational psychology', TRUE), + ('Organska kemija', 'Organic chemistry', TRUE), + ('Orientacija', 'Orientation', TRUE), + ('Ortopedija', 'Orthopedics', TRUE), + ('Osnovno šolstvo', 'Basic education', TRUE), + ('Padalstvo', 'Parachuting', TRUE), + ('Pajki', 'Araneae', TRUE), + ('Paliativna oskrba', 'Palliative care', TRUE), + ('Parapsihologija', 'Parapsychology', TRUE), + ('Parlament', 'Parliament', TRUE), + ('Pasme', 'Breeds', TRUE), + ('Patologija', 'Pathology', TRUE), + ('Periferne naprave', 'Peripheral devices', TRUE), + ('Planinstvo', 'Hiking', TRUE), + ('Plavanje', 'Swimming', TRUE), + ('Plazilci', 'Reptilia ', TRUE), + ('Pletilstvo', 'Knitting', TRUE), + ('Plezanje', 'Climbing', TRUE), + ('Podatkovne baze', 'Databases', TRUE), + ('Podatkovno rudarjenje', 'Data mining', TRUE), + ('Podjetništvo', 'Entrepreneurship', TRUE), + ('Pohodništvo', 'Trekking', TRUE), + ('Poklicno izobraževanje', 'Vocational education', TRUE), + ('Policija', 'Police', TRUE), + ('Politične stranke', 'Political parties', TRUE), + ('Politični sistemi', 'Political systems', TRUE), + ('Poljedelstvo', 'Agriculture', TRUE), + ('Pomorski promet', 'Maritime traffic', TRUE), + ('Pomorsko pravo', 'Maritime law', TRUE), + ('Pop, rock glasba', 'Pop, rock music', TRUE), + ('Potapljanje', 'Diving', TRUE), + ('Požari', 'Fires', TRUE), + ('Pragmatika', 'Pragmatics', TRUE), + ('Pravo EU', 'EU law', TRUE), + ('Pravopis', 'Ortography', TRUE), + ('Pravorečje', 'Orthoepy ', TRUE), + ('Prazniki', 'Holidays and feasts', TRUE), + ('Predori', 'Tunnels', TRUE), + ('Predšolska vzgoja', 'Preschool education', TRUE), + ('Procesno pravo', 'Procedural law', TRUE), + ('Procesno strojništvo', 'Process engineering', TRUE), + ('Procesorji', 'Processors', TRUE), + ('Programiranje', 'Programming', TRUE), + ('Programska oprema', 'Software', TRUE), + ('Programski jeziki', 'Programming languages', TRUE), + ('Promocija zdravja', 'Health promotion', TRUE), + ('Prostorski razvoj', 'Spatial development', TRUE), + ('Prostorsko planiranje', 'Spatial planning', TRUE), + ('Prostorsko urejanje', 'Site planning', TRUE), + ('Protozoologija', 'Protozoology', TRUE), + ('Prva pomoč', 'First aid', TRUE), + ('Prvo posredovanje', 'First respond', TRUE), + ('Psihiatrija', 'Psychiatry', TRUE), + ('Psihoterapija', 'Psychotherapy', TRUE), + ('Ptice', 'Aves', TRUE), + ('Računalniška grafika', 'Computer graphics', TRUE), + ('Računalniška omrežja', 'Computer networks', TRUE), + ('Računalniška simulacija', 'Computer simulation', TRUE), + ('Računalniške igre', 'Computer games', TRUE), + ('Računalniški virusi', 'Computer worms', TRUE), + ('Radio', 'Radio', TRUE), + ('Raki', 'Crustacea', TRUE), + ('Rastlinske vrste', 'Plant species', TRUE), + ('Razpoznava govora', 'Speech recognition', TRUE), + ('Razvojna psihologija', 'Developmental psychology', TRUE), + ('Rečni promet', 'River traffic', TRUE), + ('Rejne živali', 'Livestock', TRUE), + ('Rekreacija', 'Recreation', TRUE), + ('Retorika', 'Rhetoric', TRUE), + ('Rezbarstvo', 'Carving', TRUE), + ('Ribe', 'Fish fauna', TRUE), + ('Risanje', 'Drawing', TRUE), + ('Robotika', 'Robotics', TRUE), + ('Rokomet', 'Handball', TRUE), + ('Sabljanje', 'Fencing', TRUE), + ('Sadjarstvo', 'Fruit growing', TRUE), + ('Sankanje', 'Sledding', TRUE), + ('Seksologija', 'Sexology', TRUE), + ('Sesalci', 'Mammalia', TRUE), + ('Siva ekonomija', 'Grey economy', TRUE), + ('Skladnja', 'Syntax', TRUE), + ('Slikarstvo', 'Painting', TRUE), + ('Smučanje', 'Skiing', TRUE), + ('Smučarski skoki', 'Ski jumping', TRUE), + ('Socialna antropologija', 'Social anthropology', TRUE), + ('Socialna etika', 'Social ethics', TRUE), + ('Socialna filozofija', 'Social philosophy', TRUE), + ('Socialna psihologija', 'Social psychology', TRUE), + ('Socialno pravo', 'Social security law', TRUE), + ('Sociologija kulture', 'Sociology of culture', TRUE), + ('Sodstvo', 'Judicial administration', TRUE), + ('Soteskanje', 'Canyoning', TRUE), + ('Specialna pedagogika', 'Special teaching', TRUE), + ('Spletni mediji', 'Digital media', TRUE), + ('Srednje šolstvo', 'Secondary education', TRUE), + ('Stilistika', 'Stylistics', TRUE), + ('Strelstvo', 'Shooting sports', TRUE), + ('Strojno prevajanje', 'Machine translation', TRUE), + ('Strojno učenje', 'Machine learning', TRUE), + ('Stvarno pravo', 'Property law', TRUE), + ('Svetlobna tehnika', 'Lighting engineering', TRUE), + ('Šah', 'Chess', TRUE), + ('Športni ribolov', 'Sport fishing', TRUE), + ('Telekomunikacije', 'Telecommunications', TRUE), + ('Televizija', 'Television', TRUE), + ('Tenis', 'Tennis', TRUE), + ('Teorija števil', 'Number theory', TRUE), + ('Terminologija', 'Terminology', TRUE), + ('Termodinamika', 'Thermodynamics', TRUE), + ('Tiskarstvo', 'Typography', TRUE), + ('Tiskovne agencije', 'Press agencies', TRUE), + ('Tkalstvo', 'Weaving', TRUE), + ('Toksikologija', 'Toxicology', TRUE), + ('Tolmačenje', 'Interpreting', TRUE), + ('Toplota', 'Heat', TRUE), + ('Topografija', 'Topography', TRUE), + ('Transformatorji', 'Transformers', TRUE), + ('Transportno pravo', 'Tranprot law', TRUE), + ('Trg dela', 'Labour market', TRUE), + ('Tržna ekonomija', 'Market economy', TRUE), + ('Ulično gledališče', 'Street theatre', TRUE), + ('Umetna inteligenca', 'Artificial intelligence', TRUE), + ('Uporabna matematika', 'Applied mathematics', TRUE), + ('Uporabna psihologija', 'Applied psychology', TRUE), + ('Uporabna umetnost', 'Aplied arts', TRUE), + ('Uporabniški vmesniki', 'User interfaces', TRUE), + ('Upravno pravo', 'Administrative law', TRUE), + ('Urbani prostor', 'Urban space', TRUE), + ('Usnjarstvo', 'Leathermaking', TRUE), + ('Ustavno pravo', 'Constitutional law', TRUE), + ('Varjenje', 'Welding', TRUE), + ('Varnost hrane', 'Food safety', TRUE), + ('Varstvo tal', 'Soil protection', TRUE), + ('Varstvo voda', 'Water protection', TRUE), + ('Vaterpolo', 'Waterpolo', TRUE), + ('Vesoljsko pravo', 'Space law', TRUE), + ('Vinogradništvo', 'Wine growing', TRUE), + ('Visoko šolstvo', 'Higher education', TRUE), + ('Vlada in ministrstva', 'Government and ministries', TRUE), + ('Vojaška mornarica ', 'Navy', TRUE), + ('Vojaške operacije ', 'Military operations', TRUE), + ('Vojaški kadri in logistika ', 'Military manpower and logistics', TRUE), + ('Vojaško letalstvo ', 'Air foces', TRUE), + ('Vojno pravo', 'Law of war', TRUE), + ('Volitve', 'Elections', TRUE), + ('Vretenčarji', 'Vertebrata', TRUE), + ('Vrtičkarstvo', 'Gardening', TRUE), + ('Vrtnarstvo', 'Commercial gardening', TRUE), + ('Zavarovalno pravo', 'Insurance law', TRUE), + ('Zborovska glasba', 'Choir music', TRUE), + ('Zdravstvena nega', 'Nursing', TRUE), + ('Zdravstvena oskrba', 'Nursing care', TRUE), + ('Zelenjadarstvo', 'Vegetable growing', TRUE), + ('Zeliščarstvo', 'Herbs growing', TRUE), + ('Zemeljski plin', 'Natural gas', TRUE), + ('Zgodovinsko jezikoslovje', 'Historical Linguistics', TRUE), + ('Zlatarstvo', 'Goldsmithery', TRUE), + ('Zračni promet', 'Air traffic', TRUE), + ('Zunanja politika', 'Foreign policy', TRUE), + ('Zveri', 'Carnivora', TRUE), + ('Zvonarstvo', 'Bellfounding', TRUE), + ('Železarstvo', 'Iron industry', TRUE), + ('Železniški promet', 'Rail traffic', TRUE), + ('Živilske tehnologije', 'Food technology', TRUE), + ('Žuželke', 'Insecta', TRUE); diff --git a/postgres/init/006_auth_and_aut.sql b/postgres/init/006_auth_and_aut.sql index c55f0ea..83867c7 100644 --- a/postgres/init/006_auth_and_aut.sql +++ b/postgres/init/006_auth_and_aut.sql @@ -6,10 +6,12 @@ DROP TYPE IF EXISTS user_role_name; DROP TYPE IF EXISTS user_hits_per_page; DROP TYPE IF EXISTS user_status; -CREATE TYPE user_status AS ENUM ('registered', 'active'); +CREATE TYPE user_status AS ENUM ('registered', 'active', 'inactive'); CREATE TYPE user_hits_per_page AS ENUM ('10', '20', '50', '100'); +CREATE TYPE user_language AS ENUM ('sl', 'en'); + CREATE TYPE user_role_name AS ENUM ('portal admin', 'dictionaries admin', 'consultancy admin', 'consultant', 'editor'); CREATE TABLE "user" ( @@ -22,7 +24,8 @@ CREATE TABLE "user" ( bcrypt_hash VARCHAR NOT NULL, time_registered TIMESTAMPTZ NOT NULL DEFAULT NOW(), time_activated TIMESTAMPTZ, - hits_per_page user_hits_per_page NOT NULL DEFAULT '10' + hits_per_page user_hits_per_page NOT NULL DEFAULT '10', + language user_language NOT NULL DEFAULT 'sl' ); CREATE TABLE user_token_activation ( diff --git a/postgres/init/015_instance_settings.sql b/postgres/init/015_instance_settings.sql index 7bbc337..807fe3a 100644 --- a/postgres/init/015_instance_settings.sql +++ b/postgres/init/015_instance_settings.sql @@ -7,9 +7,11 @@ CREATE TABLE instance_settings ( INSERT INTO instance_settings (name, value) VALUES - ('portal_name', 'Terminološki portal'), + ('portal_name_sl', 'Terminološki portal'), + ('portal_name_en', 'Terminology Portal'), ('portal_code', 'XX'), - ('portal_description', 'Terminološki portal je samostojna, odprto dostopna spletna storitev, v katero so vključeni terminološki viri na portalu. Registriranim uporabnikom je na voljo tudi luščilnik terminoloških kandidatov iz specializiranih korpusov, konkordančnik za pregledovanje izbranih besedil, označevalnik terminov v izbranih besedilih, urejevalnik terminoloških virov, terminološka svetovalnica in stran s pomočjo in navodili za uporabo posameznih funkcij portala. Terminološki portal je moderiran.'), + ('portal_description_sl', 'Terminološki portal je samostojna, odprto dostopna spletna storitev, v katero so vključeni terminološki viri na portalu. Registriranim uporabnikom je na voljo tudi luščilnik terminoloških kandidatov iz specializiranih korpusov, konkordančnik za pregledovanje izbranih besedil, označevalnik terminov v izbranih besedilih, urejevalnik terminoloških virov, terminološka svetovalnica in stran s pomočjo in navodili za uporabo posameznih funkcij portala. Terminološki portal je moderiran.'), + ('portal_description_en', 'Terminology portal is an independent openly accessible on-line service that offers the use of terminology resources published on the portal. Registered users can also use an extractor to extract term candidates from specialized corpora, a concordance tool to review selected texts, a term mark-up tool, edit terminology resource with an editor, take advantage of terminology consulting and access a help page with instructions for use of all Portal functions. The Terminology Portal is moderated.'), ('is_extraction_enabled', 'T'), ('is_dictionaries_enabled', 'T'), ('is_consultancy_enabled', 'T'), diff --git a/postgres/init/018_extraction.sql b/postgres/init/018_extraction.sql index e633b40..9e7e66e 100644 --- a/postgres/init/018_extraction.sql +++ b/postgres/init/018_extraction.sql @@ -2,7 +2,7 @@ CREATE TYPE extraction_status AS ENUM ('new', 'in progress', 'failed', 'finished CREATE TYPE extraction_job_type AS ENUM ('doc to conllu', 'conllus to term candidates', 'concordancer', 'oss term candidates'); -CREATE TYPE extraction_job_status AS ENUM ('pending', 'in progress', 'failed', 'finished'); +CREATE TYPE extraction_job_status AS ENUM ('pending', 'skipped', 'in progress', 'failed', 'finished'); CREATE TABLE extraction ( id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, @@ -40,7 +40,11 @@ CREATE TABLE extraction_job ( ), CHECK ( CASE - WHEN job_type = 'concordancer' OR status = 'pending' THEN remote_job_id IS NULL + WHEN + job_type = 'concordancer' OR + status = 'pending' OR + (status = 'failed' AND time_started IS NULL) + THEN remote_job_id IS NULL ELSE remote_job_id IS NOT NULL END ) diff --git a/postgres/init/019_dictionary_export.sql b/postgres/init/019_dictionary_export.sql new file mode 100644 index 0000000..4873384 --- /dev/null +++ b/postgres/init/019_dictionary_export.sql @@ -0,0 +1,19 @@ +CREATE TYPE dictionary_export_status AS ENUM ('pending', 'in progress', 'failed', 'finished'); + +CREATE TYPE dictionary_export_file_format AS ENUM ('xml', 'csv', 'tsv', 'tbx'); + +CREATE TABLE dictionary_export ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + dictionary_id INT NOT NULL REFERENCES dictionary ON DELETE CASCADE, -- Consider indexing this one, since you're making queries with it in a WHERE clause and since deletion of rows from dictionary will require a scan of this table for references. + status dictionary_export_status NOT NULL DEFAULT 'pending', + time_created TIMESTAMPTZ NOT NULL DEFAULT NOW(), + time_started TIMESTAMPTZ, + time_finished TIMESTAMPTZ, + entry_count INT, + is_valid_filter BOOL, + is_published_filter BOOL, + is_terminology_reviewed_filter BOOL, + is_language_reviewed_filter BOOL, + status_filter entry_status, + export_file_format dictionary_export_file_format NOT NULL +); diff --git a/postgres/manual-scripts/termania.sql b/postgres/manual-scripts/termania.sql index dddc04e..53908ad 100644 --- a/postgres/manual-scripts/termania.sql +++ b/postgres/manual-scripts/termania.sql @@ -16,7 +16,7 @@ BEGIN IF linkedTermaniaId IS NULL THEN INSERT INTO public.linked_portal( is_enabled, time_last_synced, code, name, url_update/*, url_index*/) - VALUES (true, NULL, 'AT', 'Amebis/Termania', 'https://api-demo.termania.net/rsdo/1.0/dictionaries/$SOURCE_ID/entries?lastSynced=$SINCE'/*, 'https://api-demo.termania.net/rsdo/1.0/dictionaries'*/) + VALUES (true, NULL, 'TA', 'Termania', 'https://api.termania.net/rsdo/1.0/dictionaries/$SOURCE_ID/entries?lastSynced=$SINCE'/*, 'https://api-demo.termania.net/rsdo/1.0/dictionaries'*/) RETURNING id into linkedTermaniaId; END IF; RAISE NOTICE 'Termania linked portal id = %', linkedTermaniaId; @@ -34,10 +34,9 @@ BEGIN CONSTRAINT importDict_pkey PRIMARY KEY (source_id) ); INSERT INTO importDict (source_id, code, description, author, domain_udk_code) VALUES - ('OBRSLO', 'ATOBRSLO', 'Vojaški slovar študentov obramboslovja', 'Fakulteta za družbene vede', '355'), - ('1000102', 'AT1000102', 'Angleško-slovenski astronomski slovar', 'Raziskovalni program Astrofizika in fizika atmosfere na Fakulteti za matematiko in fiziko Univerze v Ljubljani ', '52'), - ('1000126', 'AT1000126', 'Angleško-slovenski glosar s področja konjeništva', 'Sintia Marič', '796/799'), - ('1000268', 'AT1000268', 'Terminološki slovar elektronskega kajenja ', 'Žiga Krajnc', ''); + ('OBRSLO', 'Obramboslovje', 'Vojaški slovar študentov obramboslovja', 'Fakulteta za družbene vede', '355'), + ('1000102', 'Astronomija', 'Angleško-slovenski astronomski slovar', 'Raziskovalni program Astrofizika in fizika atmosfere na Fakulteti za matematiko in fiziko Univerze v Ljubljani ', '52'), + ('1000126', 'Konjeništvo', 'Angleško-slovenski glosar s področja konjeništva', 'Sintia Marič', '796/799'); FOR zd IN SELECT z.*, d.id AS domain_id FROM importDict AS z diff --git a/postgres/manual-scripts/terminologisce.sql b/postgres/manual-scripts/terminologisce.sql index 13208aa..d70e147 100644 --- a/postgres/manual-scripts/terminologisce.sql +++ b/postgres/manual-scripts/terminologisce.sql @@ -16,7 +16,7 @@ BEGIN IF linkedZrcTermId IS NULL THEN INSERT INTO public.linked_portal( is_enabled, time_last_synced, code, name, url_update/*, url_index*/) - VALUES (true, NULL, 'ZT', 'ZRC-SAZU/Terminologišče', 'https://iskalnik4ts.zrc-sazu.si/rsdo/updates?dictionaryId=$SOURCE_ID&since=$SINCE'/*, 'https://iskalnik4ts.zrc-sazu.si/rsdo/dictionaries'*/) + VALUES (true, NULL, 'TZ', 'Terminologišče', 'https://tsiskalnik.zrc-sazu.si/rsdo/updates?dictionaryId=$SOURCE_ID&since=$SINCE'/*, 'https://tsiskalnik.zrc-sazu.si/rsdo/dictionaries'*/) RETURNING id into linkedZrcTermId; END IF; RAISE NOTICE 'ZrcTerm linked portal id = %', linkedZrcTermId; @@ -33,21 +33,23 @@ BEGIN CONSTRAINT dictionary_pkey PRIMARY KEY (source_id) ); INSERT INTO zrcTermDic (source_id, code, description, domain_udk_code) VALUES - ('farmacija','ZTFARMAC','Farmacevtski terminološki slovar ZRC-SAZU/Terminologišče', '61'), - ('betonske_konstrukcije','ZTBETON','Terminološki slovar betonskih konstrukcij ZRC-SAZU/Terminologišče', '624'), - ('pravo','ZTPRAVO','Pravni terminološki slovar ZRC-SAZU/Terminologišče', '34'), - ('avtomatika','ZTAVTOMAT','Terminološki slovar avtomatike ZRC-SAZU/Terminologišče', '621.3'), - ('urbanizem','ZTURBAN','Urbanistični terminološki slovar ZRC-SAZU/Terminologišče', '711'), - ('umetnost','ZTUMETN','Terminološki slovar uporabne umetnosti – pohištvo, ure, orožje ZRC-SAZU/Terminologišče', '93/94'), - ('tolkala','ZTTOLKAL','Tolkalni terminološki slovar ZRC-SAZU/Terminologišče', '93/94'), - ('botanika','ZTBOTAN','Botanični terminološki slovar ZRC-SAZU/Terminologišče', '58'), - ('smucanje','ZTSMUČ','Slovenski smučarski slovar ZRC-SAZU/Terminologišče', '796/799'), - ('gledalisce','ZTGLED','Gledališki terminološki slovar ZRC-SAZU/Terminologišče', '93/94'), - ('cebelarstvo','ZTCEBEL','Čebelarski terminološki slovar ZRC-SAZU/Terminologišče', '59'), - ('geologija','ZTGEOLOG','Geološki terminološki slovar ZRC-SAZU/Terminologišče', '55'), - ('gemologija','ZTGEMOLOG','Gemološki terminološki slovar ZRC-SAZU/Terminologišče', '622'), - ('geografija','ZTGEOGRAF','Geografski terminološki slovar ZRC-SAZU/Terminologišče', '338'), - ('planinstvo','ZTPLANIN','Planinski terminološki slovar ZRC-SAZU/Terminologišče', '796/799'); + ('avtomatika','Avtomatika','Terminološki slovar avtomatike', '621.3'), + ('betonske_konstrukcije','Betonske konst.','Terminološki slovar betonskih konstrukcij', '624'), + ('botanika','Botanika','Botanični terminološki slovar', '58'), + ('cebelarstvo','Čebelarstvo','Čebelarski terminološki slovar', '59'), + ('davcni','Davki','Davčni terminološki slovar', '336'), + ('farmacija','Farmacija','Farmacevtski terminološki slovar', '61'), + ('gemologija','Gemologija','Gemološki terminološki slovar', '622'), + ('geografija','Geografija','Geografski terminološki slovar', '338'), + ('geologija','Geologija','Geološki terminološki slovar', '55'), + ('gledalisce','Gledališče','Gledališki terminološki slovar', '93/94'), + ('kamnarski','Kamnarstvo','Kamnarski terminološki slovar', '622'), + ('planinski','Planinstvo','Planinski terminološki slovar', '796/799'), + ('pravni','Pravo','Pravni terminološki slovar', '34'), + ('smucanje','Smučanje','Slovenski smučarski slovar', '796/799'), + ('tolkala','Tolkala','Tolkalni terminološki slovar', '93/94'), + ('umetnost','Umetnost','Terminološki slovar uporabne umetnosti – pohištvo, ure, orožje', '93/94'), + ('urbanisticni','Urbanizem','Urbanistični terminološki slovar', '711'); FOR zd IN SELECT z.*, d.id AS domain_id FROM zrcTermDic AS z