mirror of
https://github.com/requarks/wiki.git
synced 2026-08-26 13:17:10 -05:00
feat: locale system groundwork + various improvements
This commit is contained in:
@@ -59,10 +59,14 @@ RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /
|
||||
RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
|
||||
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
# Add ngrok source
|
||||
# Add ngrok Source
|
||||
RUN curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc > /dev/null && \
|
||||
echo "deb https://ngrok-agent.s3.amazonaws.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/ngrok.list
|
||||
|
||||
# Add Localazy Source
|
||||
RUN curl -sS https://dist.localazy.com/debian/pubkey.gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/localazy.gpg && \
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/trusted.gpg.d/localazy.gpg] https://maven.localazy.com/repository/apt/ stable main" | sudo tee /etc/apt/sources.list.d/localazy.list
|
||||
|
||||
# Install the packages we need
|
||||
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && apt-get install -qy \
|
||||
bash \
|
||||
@@ -73,6 +77,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && apt-get install -
|
||||
less \
|
||||
git \
|
||||
gnupg2 \
|
||||
localazy \
|
||||
nano \
|
||||
netcat \
|
||||
ngrok \
|
||||
@@ -81,17 +86,17 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && apt-get install -
|
||||
wget
|
||||
|
||||
# avoid million NPM install messages
|
||||
ENV npm_config_loglevel warn
|
||||
ENV npm_config_loglevel=warn
|
||||
# allow installing when the main user is root
|
||||
ENV npm_config_unsafe_perm true
|
||||
ENV npm_config_unsafe_perm=true
|
||||
# disable NPM funding messages
|
||||
ENV npm_config_fund false
|
||||
ENV npm_config_fund=false
|
||||
|
||||
# Colorize the bash shell
|
||||
RUN sed -i 's/#force_color_prompt=/force_color_prompt=/' /root/.bashrc
|
||||
|
||||
# Fetch wait-for utility
|
||||
ADD https://raw.githubusercontent.com/eficode/wait-for/v2.2.3/wait-for /usr/local/bin/
|
||||
# Copy wait-for utility
|
||||
COPY wait-for.sh /usr/local/bin/wait-for
|
||||
RUN chmod +rx /usr/local/bin/wait-for
|
||||
|
||||
# Copy the startup file
|
||||
@@ -102,3 +107,5 @@ RUN sed -i 's/\r$//' /docker-init.sh && \
|
||||
# Create workspace
|
||||
RUN mkdir -p /workspace
|
||||
WORKDIR /workspace
|
||||
|
||||
ENV NODE_ENV=development
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
#!/bin/sh
|
||||
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2017 Eficode Oy
|
||||
#
|
||||
# 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.
|
||||
|
||||
VERSION="2.2.3"
|
||||
|
||||
set -- "$@" -- "$TIMEOUT" "$QUIET" "$PROTOCOL" "$HOST" "$PORT" "$result"
|
||||
TIMEOUT=15
|
||||
QUIET=0
|
||||
# The protocol to make the request with, either "tcp" or "http"
|
||||
PROTOCOL="tcp"
|
||||
|
||||
echoerr() {
|
||||
if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
exitcode="$1"
|
||||
cat << USAGE >&2
|
||||
Usage:
|
||||
$0 host:port|url [-t timeout] [-- command args]
|
||||
-q | --quiet Do not output any status messages
|
||||
-t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout
|
||||
-v | --version Show the version of this tool
|
||||
-- COMMAND ARGS Execute command with args after the test finishes
|
||||
USAGE
|
||||
exit "$exitcode"
|
||||
}
|
||||
|
||||
wait_for() {
|
||||
case "$PROTOCOL" in
|
||||
tcp)
|
||||
if ! command -v nc >/dev/null; then
|
||||
echoerr 'nc command is missing!'
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
http)
|
||||
if ! command -v wget >/dev/null; then
|
||||
echoerr 'wget command is missing!'
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
TIMEOUT_END=$(($(date +%s) + TIMEOUT))
|
||||
|
||||
while :; do
|
||||
case "$PROTOCOL" in
|
||||
tcp)
|
||||
nc -w 1 -z "$HOST" "$PORT" > /dev/null 2>&1
|
||||
;;
|
||||
http)
|
||||
wget --timeout=1 -q "$HOST" -O /dev/null > /dev/null 2>&1
|
||||
;;
|
||||
*)
|
||||
echoerr "Unknown protocol '$PROTOCOL'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
result=$?
|
||||
|
||||
if [ $result -eq 0 ] ; then
|
||||
if [ $# -gt 7 ] ; then
|
||||
for result in $(seq $(($# - 7))); do
|
||||
result=$1
|
||||
shift
|
||||
set -- "$@" "$result"
|
||||
done
|
||||
|
||||
TIMEOUT=$2 QUIET=$3 PROTOCOL=$4 HOST=$5 PORT=$6 result=$7
|
||||
shift 7
|
||||
exec "$@"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $TIMEOUT -ne 0 -a $(date +%s) -ge $TIMEOUT_END ]; then
|
||||
echo "Operation timed out" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
while :; do
|
||||
case "$1" in
|
||||
http://*|https://*)
|
||||
HOST="$1"
|
||||
PROTOCOL="http"
|
||||
shift 1
|
||||
;;
|
||||
*:* )
|
||||
HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
|
||||
PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
|
||||
shift 1
|
||||
;;
|
||||
-v | --version)
|
||||
echo $VERSION
|
||||
exit
|
||||
;;
|
||||
-q | --quiet)
|
||||
QUIET=1
|
||||
shift 1
|
||||
;;
|
||||
-q-*)
|
||||
QUIET=0
|
||||
echoerr "Unknown option: $1"
|
||||
usage 1
|
||||
;;
|
||||
-q*)
|
||||
QUIET=1
|
||||
result=$1
|
||||
shift 1
|
||||
set -- -"${result#-q}" "$@"
|
||||
;;
|
||||
-t | --timeout)
|
||||
TIMEOUT="$2"
|
||||
shift 2
|
||||
;;
|
||||
-t*)
|
||||
TIMEOUT="${1#-t}"
|
||||
shift 1
|
||||
;;
|
||||
--timeout=*)
|
||||
TIMEOUT="${1#*=}"
|
||||
shift 1
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
--help)
|
||||
usage 0
|
||||
;;
|
||||
-*)
|
||||
QUIET=0
|
||||
echoerr "Unknown option: $1"
|
||||
usage 1
|
||||
;;
|
||||
*)
|
||||
QUIET=0
|
||||
echoerr "Unknown argument: $1"
|
||||
usage 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ! [ "$TIMEOUT" -ge 0 ] 2>/dev/null; then
|
||||
echoerr "Error: invalid timeout '$TIMEOUT'"
|
||||
usage 3
|
||||
fi
|
||||
|
||||
case "$PROTOCOL" in
|
||||
tcp)
|
||||
if [ "$HOST" = "" ] || [ "$PORT" = "" ]; then
|
||||
echoerr "Error: you need to provide a host and port to test."
|
||||
usage 2
|
||||
fi
|
||||
;;
|
||||
http)
|
||||
if [ "$HOST" = "" ]; then
|
||||
echoerr "Error: you need to provide a host to test."
|
||||
usage 2
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
wait_for "$@"
|
||||
+2
-2
@@ -1,13 +1,12 @@
|
||||
extends:
|
||||
- requarks
|
||||
- plugin:vue/strongly-recommended
|
||||
- plugin:cypress/recommended
|
||||
env:
|
||||
node: true
|
||||
jest: true
|
||||
parserOptions:
|
||||
parser: babel-eslint
|
||||
ecmaVersion: 2017
|
||||
ecmaVersion: 2020
|
||||
allowImportExportEverywhere: true
|
||||
globals:
|
||||
document: false
|
||||
@@ -22,3 +21,4 @@ ignorePatterns:
|
||||
- 'repo/**'
|
||||
- 'data/**'
|
||||
- 'logs/**'
|
||||
- 'server/locales/**'
|
||||
|
||||
+4
-1
@@ -21,6 +21,7 @@ npm-debug.log*
|
||||
|
||||
# Config Files
|
||||
/config.yml
|
||||
/localazy.keys.json
|
||||
|
||||
# Data directories
|
||||
/repo
|
||||
@@ -40,4 +41,6 @@ test-results/
|
||||
.scannerwork
|
||||
|
||||
# Localization Resources
|
||||
/server/locales/**/*.yml
|
||||
/server/locales/*.json
|
||||
!/server/locales/en.json
|
||||
!/server/locales/list.json
|
||||
|
||||
Vendored
+2
-1
@@ -11,8 +11,9 @@
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"i18n-ally.localesPaths": [
|
||||
"ux/src/i18n/locales"
|
||||
"server/locales",
|
||||
],
|
||||
"i18n-ally.pathMatcher": "{locale}.json",
|
||||
"i18n-ally.keystyle": "flat",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.enabledFrameworks": [
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"upload": {
|
||||
"folder": "server/locales",
|
||||
"files": "en.json",
|
||||
"type": "json"
|
||||
},
|
||||
|
||||
"download": {
|
||||
"folder": "server/locales",
|
||||
"files": "${lang}.json",
|
||||
"metadataFileJs": "metadata.mjs"
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,8 @@ export default {
|
||||
* Post-Web Boot Sequence
|
||||
*/
|
||||
async postBootWeb() {
|
||||
await WIKI.db.locales.refreshFromDisk()
|
||||
|
||||
await WIKI.db.analytics.refreshProvidersFromDisk()
|
||||
await WIKI.db.authentication.refreshStrategiesFromDisk()
|
||||
await WIKI.db.commentProviders.refreshProvidersFromDisk()
|
||||
|
||||
@@ -163,11 +163,14 @@ export async function up (knex) {
|
||||
})
|
||||
// LOCALES -----------------------------
|
||||
.createTable('locales', table => {
|
||||
table.string('code', 5).notNullable().primary()
|
||||
table.jsonb('strings')
|
||||
table.boolean('isRTL').notNullable().defaultTo(false)
|
||||
table.string('code', 10).notNullable().primary()
|
||||
table.string('name').notNullable()
|
||||
table.string('nativeName').notNullable()
|
||||
table.string('language', 2).notNullable().index()
|
||||
table.string('region', 2)
|
||||
table.string('script', 4)
|
||||
table.boolean('isRTL').notNullable().defaultTo(false)
|
||||
table.jsonb('strings')
|
||||
table.integer('completeness').notNullable().defaultTo(0)
|
||||
table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
|
||||
table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
|
||||
@@ -207,7 +210,7 @@ export async function up (knex) {
|
||||
.createTable('pageLinks', table => {
|
||||
table.increments('id').primary()
|
||||
table.string('path').notNullable()
|
||||
table.string('localeCode', 5).notNullable()
|
||||
table.string('localeCode', 10).notNullable()
|
||||
})
|
||||
// PAGES -------------------------------
|
||||
.createTable('pages', table => {
|
||||
@@ -284,7 +287,7 @@ export async function up (knex) {
|
||||
table.string('fileName').notNullable().index()
|
||||
table.string('hash').notNullable().index()
|
||||
table.enu('type', ['folder', 'page', 'asset']).notNullable().index()
|
||||
table.string('localeCode', 5).notNullable().defaultTo('en').index()
|
||||
table.string('localeCode', 10).notNullable().defaultTo('en').index()
|
||||
table.string('title').notNullable()
|
||||
table.jsonb('meta').notNullable().defaultTo('{}')
|
||||
table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
|
||||
@@ -295,6 +298,13 @@ export async function up (knex) {
|
||||
table.uuid('id').notNullable().primary()
|
||||
table.binary('data').notNullable()
|
||||
})
|
||||
// USER EDITOR SETTINGS ----------------
|
||||
.createTable('userEditorSettings', table => {
|
||||
table.uuid('id').notNullable()
|
||||
table.string('editor').notNullable()
|
||||
table.jsonb('config').notNullable().defaultTo('{}')
|
||||
table.primary(['id', 'editor'])
|
||||
})
|
||||
// USER KEYS ---------------------------
|
||||
.createTable('userKeys', table => {
|
||||
table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
|
||||
@@ -359,7 +369,7 @@ export async function up (knex) {
|
||||
table.uuid('siteId').notNullable().references('id').inTable('sites').index()
|
||||
})
|
||||
.table('pageHistory', table => {
|
||||
table.string('localeCode', 5).references('code').inTable('locales')
|
||||
table.string('localeCode', 10).references('code').inTable('locales')
|
||||
table.uuid('authorId').notNullable().references('id').inTable('users')
|
||||
table.uuid('siteId').notNullable().references('id').inTable('sites').index()
|
||||
})
|
||||
@@ -368,7 +378,7 @@ export async function up (knex) {
|
||||
table.index(['path', 'localeCode'])
|
||||
})
|
||||
.table('pages', table => {
|
||||
table.string('localeCode', 5).references('code').inTable('locales').index()
|
||||
table.string('localeCode', 10).references('code').inTable('locales').index()
|
||||
table.uuid('authorId').notNullable().references('id').inTable('users').index()
|
||||
table.uuid('creatorId').notNullable().references('id').inTable('users').index()
|
||||
table.uuid('ownerId').notNullable().references('id').inTable('users').index()
|
||||
@@ -387,9 +397,6 @@ export async function up (knex) {
|
||||
.table('userKeys', table => {
|
||||
table.uuid('userId').notNullable().references('id').inTable('users')
|
||||
})
|
||||
.table('users', table => {
|
||||
table.string('localeCode', 5).references('code').inTable('locales').notNullable().defaultTo('en')
|
||||
})
|
||||
|
||||
// =====================================
|
||||
// DEFAULT DATA
|
||||
@@ -520,16 +527,6 @@ export async function up (knex) {
|
||||
}
|
||||
])
|
||||
|
||||
// -> DEFAULT LOCALE
|
||||
|
||||
await knex('locales').insert({
|
||||
code: 'en',
|
||||
strings: {},
|
||||
isRTL: false,
|
||||
name: 'English',
|
||||
nativeName: 'English'
|
||||
})
|
||||
|
||||
// -> DEFAULT SITE
|
||||
|
||||
await knex('sites').insert({
|
||||
@@ -544,6 +541,7 @@ export async function up (knex) {
|
||||
footerExtra: '',
|
||||
pageExtensions: ['md', 'html', 'txt'],
|
||||
pageCasing: true,
|
||||
discoverable: false,
|
||||
defaults: {
|
||||
tocDepth: {
|
||||
min: 1,
|
||||
@@ -566,9 +564,10 @@ export async function up (knex) {
|
||||
follow: true
|
||||
},
|
||||
authStrategies: [{ id: authModuleId, order: 0, isVisible: true }],
|
||||
locale: 'en',
|
||||
localeNamespacing: false,
|
||||
localeNamespaces: [],
|
||||
locales: {
|
||||
primary: 'en',
|
||||
active: ['en']
|
||||
},
|
||||
assets: {
|
||||
logo: false,
|
||||
logoExt: 'svg',
|
||||
@@ -700,8 +699,7 @@ export async function up (knex) {
|
||||
timeFormat: '12h',
|
||||
appearance: 'site',
|
||||
cvd: 'none'
|
||||
},
|
||||
localeCode: 'en'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: userGuestId,
|
||||
@@ -718,8 +716,7 @@ export async function up (knex) {
|
||||
timeFormat: '12h',
|
||||
appearance: 'site',
|
||||
cvd: 'none'
|
||||
},
|
||||
localeCode: 'en'
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ export default {
|
||||
Query: {
|
||||
async locales(obj, args, context, info) {
|
||||
let remoteLocales = await WIKI.cache.get('locales')
|
||||
let localLocales = await WIKI.db.locales.query().select('code', 'isRTL', 'name', 'nativeName', 'createdAt', 'updatedAt', 'availability')
|
||||
let localLocales = await WIKI.db.locales.query().select('code', 'isRTL', 'name', 'nativeName', 'createdAt', 'updatedAt', 'completeness')
|
||||
remoteLocales = remoteLocales || localLocales
|
||||
return _.map(remoteLocales, rl => {
|
||||
let isInstalled = _.some(localLocales, ['code', rl.code])
|
||||
@@ -16,8 +16,8 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
translations (obj, args, context, info) {
|
||||
return WIKI.lang.getByNamespace(args.locale, args.namespace)
|
||||
localeStrings (obj, args, context, info) {
|
||||
return WIKI.db.locales.getStrings(args.locale)
|
||||
}
|
||||
},
|
||||
Mutation: {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
extend type Query {
|
||||
locales: [LocalizationLocale]
|
||||
translations(locale: String!, namespace: String!): [Translation]
|
||||
localeStrings(locale: String!): JSON
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
@@ -25,7 +25,7 @@ extend type Mutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type LocalizationLocale {
|
||||
availability: Int
|
||||
completeness: Int
|
||||
code: String
|
||||
createdAt: Date
|
||||
installDate: Date
|
||||
@@ -33,10 +33,7 @@ type LocalizationLocale {
|
||||
isRTL: Boolean
|
||||
name: String
|
||||
nativeName: String
|
||||
region: String
|
||||
script: String
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
type Translation {
|
||||
key: String
|
||||
value: String
|
||||
}
|
||||
|
||||
@@ -65,11 +65,10 @@ type Site {
|
||||
sitemap: Boolean
|
||||
robots: SiteRobots
|
||||
features: SiteFeatures
|
||||
discoverable: Boolean
|
||||
defaults: SiteDefaults
|
||||
uploads: SiteUploads
|
||||
locale: String
|
||||
localeNamespaces: [String]
|
||||
localeNamespacing: Boolean
|
||||
locales: SiteLocales
|
||||
editors: SiteEditors
|
||||
theme: SiteTheme
|
||||
}
|
||||
@@ -98,11 +97,9 @@ type SiteUploads {
|
||||
normalizeFilename: Boolean
|
||||
}
|
||||
|
||||
type SiteLocale {
|
||||
locale: String
|
||||
autoUpdate: Boolean
|
||||
namespacing: Boolean
|
||||
namespaces: [String]
|
||||
type SiteLocales {
|
||||
primary: String
|
||||
active: [String]
|
||||
}
|
||||
|
||||
type SiteEditors {
|
||||
@@ -184,6 +181,7 @@ input SiteUpdateInput {
|
||||
sitemap: Boolean
|
||||
robots: SiteRobotsInput
|
||||
features: SiteFeaturesInput
|
||||
discoverable: Boolean
|
||||
defaults: SiteDefaultsInput
|
||||
uploads: SiteUploadsInput
|
||||
editors: SiteEditorsInput
|
||||
|
||||
@@ -227,6 +227,9 @@
|
||||
"admin.general.defaultTocDepth": "Default ToC Depth",
|
||||
"admin.general.defaultTocDepthHint": "The default minimum and maximum header levels to show in the table of contents.",
|
||||
"admin.general.defaults": "Site Defaults",
|
||||
"admin.general.discoverable": "Make Discoverable in the Wiki Directory",
|
||||
"admin.general.discoverableHint": "Add your wiki to the Wiki Directory so that it can be discovered by other users and wikis.",
|
||||
"admin.general.discovery": "Discovery",
|
||||
"admin.general.displaySiteTitle": "Display Site Title",
|
||||
"admin.general.displaySiteTitleHint": "Should the site title be displayed next to the logo? If your logo isn't square and contain your brand name, turn this option off.",
|
||||
"admin.general.favicon": "Favicon",
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
[
|
||||
{
|
||||
"code": "en",
|
||||
"name": "English",
|
||||
"nativeName": "English",
|
||||
"rtl": false,
|
||||
"completeness": 100
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,31 @@
|
||||
const localazyMetadata = {
|
||||
projectUrl: "https://localazy.com/p/wiki",
|
||||
baseLocale: "en",
|
||||
languages: [
|
||||
{
|
||||
language: "en",
|
||||
region: "",
|
||||
script: "",
|
||||
isRtl: false,
|
||||
name: "English",
|
||||
localizedName: "English",
|
||||
pluralType: (n) => { return (n===1) ? "one" : "other"; }
|
||||
}
|
||||
],
|
||||
files: [
|
||||
{
|
||||
cdnHash: "54b977214afbffe2ffeb07d0ccb03558e75e4408",
|
||||
file: "file.json",
|
||||
path: "",
|
||||
library: "",
|
||||
module: "",
|
||||
buildType: "",
|
||||
productFlavors: [],
|
||||
cdnFiles: {
|
||||
"en#": "https://delivery.localazy.com/_a7797965569058078203416ae5aa/_e0/54b977214afbffe2ffeb07d0ccb03558e75e4408/en/file.json"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default localazyMetadata;
|
||||
@@ -1,4 +1,8 @@
|
||||
import { Model } from 'objection'
|
||||
import { find } from 'lodash-es'
|
||||
import { stat, readFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { DateTime } from 'luxon'
|
||||
|
||||
/**
|
||||
* Locales model
|
||||
@@ -36,6 +40,62 @@ export class Locale extends Model {
|
||||
this.updatedAt = new Date().toISOString()
|
||||
}
|
||||
|
||||
static async refreshFromDisk ({ force = false } = {}) {
|
||||
try {
|
||||
const localesMeta = (await import(`../locales/metadata.mjs`)).default
|
||||
WIKI.logger.info(`Found ${localesMeta.languages.length} locales: [ OK ]`)
|
||||
|
||||
const dbLocales = await WIKI.db.locales.query().select('code', 'updatedAt')
|
||||
|
||||
for (const lang of localesMeta.languages) {
|
||||
// -> Build filename
|
||||
const langFilenameParts = [lang.language]
|
||||
if (lang.region) {
|
||||
langFilenameParts.push(lang.region)
|
||||
}
|
||||
if (lang.script) {
|
||||
langFilenameParts.push(lang.script)
|
||||
}
|
||||
const langFilename = langFilenameParts.join('-')
|
||||
|
||||
// -> Get DB version
|
||||
const dbLang = find(dbLocales, ['code', langFilename])
|
||||
|
||||
// -> Get File version
|
||||
const flPath = path.join(WIKI.SERVERPATH, `locales/${langFilename}.json`)
|
||||
const flStat = await stat(flPath)
|
||||
const flUpdatedAt = DateTime.fromJSDate(flStat.mtime)
|
||||
|
||||
// -> Load strings
|
||||
if (!dbLang || DateTime.fromJSDate(dbLang.updatedAt) < flUpdatedAt || force) {
|
||||
WIKI.logger.debug(`Loading locale ${langFilename} into DB...`)
|
||||
const flStrings = JSON.parse(await readFile(flPath, 'utf8'))
|
||||
await WIKI.db.locales.query().insert({
|
||||
code: langFilename,
|
||||
name: lang.name,
|
||||
nativeName: lang.localizedName,
|
||||
language: lang.language,
|
||||
region: lang.region,
|
||||
script: lang.script,
|
||||
isRTL: lang.isRtl,
|
||||
strings: flStrings
|
||||
}).onConflict('code').merge(['strings', 'updatedAt'])
|
||||
} else {
|
||||
WIKI.logger.debug(`Locale ${langFilename} is newer in the DB. Skipping disk version.`)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
WIKI.logger.warn(`Failed to load locales from disk: [ FAILED ]`)
|
||||
WIKI.logger.warn(err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static async getStrings (locale) {
|
||||
const { strings } = await WIKI.db.locales.query().findOne('code', locale).column('strings')
|
||||
return strings
|
||||
}
|
||||
|
||||
static async getNavLocales({ cache = false } = {}) {
|
||||
return []
|
||||
// if (!WIKI.config.lang.namespacing) {
|
||||
|
||||
@@ -58,6 +58,7 @@ export class Site extends Model {
|
||||
footerExtra: '',
|
||||
pageExtensions: ['md', 'html', 'txt'],
|
||||
pageCasing: true,
|
||||
discoverable: false,
|
||||
defaults: {
|
||||
tocDepth: {
|
||||
min: 1,
|
||||
@@ -79,9 +80,10 @@ export class Site extends Model {
|
||||
index: true,
|
||||
follow: true
|
||||
},
|
||||
locale: 'en',
|
||||
localeNamespacing: false,
|
||||
localeNamespaces: [],
|
||||
locales: {
|
||||
primary: 'en',
|
||||
active: ['en']
|
||||
},
|
||||
assets: {
|
||||
logo: false,
|
||||
logoExt: 'svg',
|
||||
|
||||
Generated
-23
@@ -637,18 +637,6 @@
|
||||
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/mock/node_modules/@graphql-tools/utils": {
|
||||
"version": "9.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz",
|
||||
"integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==",
|
||||
"dependencies": {
|
||||
"@graphql-typed-document-node/core": "^3.1.1",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/schema": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.0.tgz",
|
||||
@@ -1556,17 +1544,6 @@
|
||||
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/apollo-server-core/node_modules/@graphql-tools/utils": {
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz",
|
||||
"integrity": "sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/apollo-server-core/node_modules/value-or-promise": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz",
|
||||
|
||||
@@ -185,6 +185,9 @@
|
||||
"eslint-plugin-standard": "4.1.0",
|
||||
"nodemon": "2.0.22"
|
||||
},
|
||||
"overrides": {
|
||||
"@graphql-tools/utils": "10.0.0"
|
||||
},
|
||||
"collective": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/wikijs",
|
||||
|
||||
+1
-1
@@ -206,7 +206,7 @@ export async function init () {
|
||||
id: currentSite.id,
|
||||
title: currentSite.config.title,
|
||||
darkMode: currentSite.config.theme.dark,
|
||||
lang: currentSite.config.locale,
|
||||
lang: currentSite.config.locales.primary,
|
||||
rtl: false, // TODO: handle RTL
|
||||
company: currentSite.config.company,
|
||||
contentLicense: currentSite.config.contentLicense
|
||||
|
||||
Generated
+94
-31
@@ -38,7 +38,7 @@
|
||||
"@tiptap/starter-kit": "2.0.3",
|
||||
"@tiptap/vue-3": "2.0.3",
|
||||
"apollo-upload-client": "17.0.0",
|
||||
"browser-fs-access": "0.34.0",
|
||||
"browser-fs-access": "0.34.1",
|
||||
"clipboard": "2.0.11",
|
||||
"codemirror": "5.65.11",
|
||||
"codemirror-asciidoc": "1.0.4",
|
||||
@@ -84,7 +84,7 @@
|
||||
"quasar": "2.12.0",
|
||||
"slugify": "1.6.6",
|
||||
"socket.io-client": "4.6.1",
|
||||
"tabulator-tables": "5.4.4",
|
||||
"tabulator-tables": "5.5.0",
|
||||
"tippy.js": "6.3.7",
|
||||
"twemoji": "14.0.2",
|
||||
"uuid": "9.0.0",
|
||||
@@ -107,9 +107,9 @@
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-standard": "17.0.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-n": "16.0.0",
|
||||
"eslint-plugin-n": "15.7.0",
|
||||
"eslint-plugin-promise": "6.1.1",
|
||||
"eslint-plugin-vue": "9.13.0"
|
||||
"eslint-plugin-vue": "9.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0",
|
||||
@@ -2136,9 +2136,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browser-fs-access": {
|
||||
"version": "0.34.0",
|
||||
"resolved": "https://registry.npmjs.org/browser-fs-access/-/browser-fs-access-0.34.0.tgz",
|
||||
"integrity": "sha512-F49NXUfjE9ZkDSF9Xr0PXPt89Sb9TIZDpxWEHOp0kYnT7RfciGLYcpHqXFFJ/+CiocOlT+5DOJ8Y7+XrKrzafw=="
|
||||
"version": "0.34.1",
|
||||
"resolved": "https://registry.npmjs.org/browser-fs-access/-/browser-fs-access-0.34.1.tgz",
|
||||
"integrity": "sha512-HPaRf2yimp8kWSuWJXc8Mi78dPbDzfduA+Gyq14H4jlMvd6XNfIRm36Y2yRLaa4x0gwcGuepj4zf14oiTlxrxQ=="
|
||||
},
|
||||
"node_modules/browserlist": {
|
||||
"version": "1.0.1",
|
||||
@@ -3372,23 +3372,47 @@
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-es-x": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-6.2.1.tgz",
|
||||
"integrity": "sha512-uR34zUhZ9EBoiSD2DdV5kHLpydVEvwWqjteUr9sXRgJknwbKZJZhdJ7uFnaTtd+Nr/2G3ceJHnHXrFhJ67n3Tw==",
|
||||
"node_modules/eslint-plugin-es": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz",
|
||||
"integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.1.2",
|
||||
"@eslint-community/regexpp": "^4.5.0"
|
||||
"eslint-utils": "^2.0.0",
|
||||
"regexpp": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
"node": ">=8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ota-meshi"
|
||||
"url": "https://github.com/sponsors/mysticatea"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=8"
|
||||
"eslint": ">=4.19.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-es/node_modules/eslint-utils": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
|
||||
"integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mysticatea"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
|
||||
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-import": {
|
||||
@@ -3447,22 +3471,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-n": {
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.0.0.tgz",
|
||||
"integrity": "sha512-akkZTE3hsHBrq6CwmGuYCzQREbVUrA855kzcHqe6i0FLBkeY7Y/6tThCVkjUnjhvRBAlc+8lILcSe5QvvDpeZQ==",
|
||||
"version": "15.7.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz",
|
||||
"integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"builtins": "^5.0.1",
|
||||
"eslint-plugin-es-x": "^6.1.0",
|
||||
"eslint-plugin-es": "^4.1.0",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"ignore": "^5.1.1",
|
||||
"is-core-module": "^2.12.0",
|
||||
"is-core-module": "^2.11.0",
|
||||
"minimatch": "^3.1.2",
|
||||
"resolve": "^1.22.2",
|
||||
"semver": "^7.5.0"
|
||||
"resolve": "^1.22.1",
|
||||
"semver": "^7.3.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mysticatea"
|
||||
@@ -3483,9 +3507,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-vue": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.13.0.tgz",
|
||||
"integrity": "sha512-aBz9A8WB4wmpnVv0pYUt86cmH9EkcwWzgEwecBxMoRNhQjTL5i4sqadnwShv/hOdr8Hbl8XANGV7dtX9UQIAyA==",
|
||||
"version": "9.14.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.14.0.tgz",
|
||||
"integrity": "sha512-4O7EuiqPGVQA1wYCzLvCzsBTv9JIPHLHhrf0k55DLzbwtmJbSw2TKS0G/l7pOwi9RWMSkjIT7ftChU5gZpgnJw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.3.0",
|
||||
@@ -3519,6 +3543,33 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-utils": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
|
||||
"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mysticatea"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=5"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
|
||||
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-visitor-keys": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz",
|
||||
@@ -6419,6 +6470,18 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/regexpp": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
|
||||
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mysticatea"
|
||||
}
|
||||
},
|
||||
"node_modules/register-service-worker": {
|
||||
"version": "1.7.2",
|
||||
"dev": true,
|
||||
@@ -7052,9 +7115,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tabulator-tables": {
|
||||
"version": "5.4.4",
|
||||
"resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-5.4.4.tgz",
|
||||
"integrity": "sha512-WqPWLRwrD8UMISUjQqZyj6y9k3ajQBs7eJtRosbt9q8RRkZlYCJYGxLt3L44jEXaQCE5q5EJFbGeDUEDJa1a3w=="
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-5.5.0.tgz",
|
||||
"integrity": "sha512-UVe26QIaGqFfaP5wfN51zF4Vy23MNTWyvM95bF7EWGsDRQm6b2MV0wiBzakVGFj28YAgz5PvEyfqLZF415PWxw=="
|
||||
},
|
||||
"node_modules/tar-stream": {
|
||||
"version": "2.2.0",
|
||||
|
||||
+4
-4
@@ -43,7 +43,7 @@
|
||||
"@tiptap/starter-kit": "2.0.3",
|
||||
"@tiptap/vue-3": "2.0.3",
|
||||
"apollo-upload-client": "17.0.0",
|
||||
"browser-fs-access": "0.34.0",
|
||||
"browser-fs-access": "0.34.1",
|
||||
"clipboard": "2.0.11",
|
||||
"codemirror": "5.65.11",
|
||||
"codemirror-asciidoc": "1.0.4",
|
||||
@@ -89,7 +89,7 @@
|
||||
"quasar": "2.12.0",
|
||||
"slugify": "1.6.6",
|
||||
"socket.io-client": "4.6.1",
|
||||
"tabulator-tables": "5.4.4",
|
||||
"tabulator-tables": "5.5.0",
|
||||
"tippy.js": "6.3.7",
|
||||
"twemoji": "14.0.2",
|
||||
"uuid": "9.0.0",
|
||||
@@ -112,9 +112,9 @@
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-standard": "17.0.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-n": "16.0.0",
|
||||
"eslint-plugin-n": "15.7.0",
|
||||
"eslint-plugin-promise": "6.1.1",
|
||||
"eslint-plugin-vue": "9.13.0"
|
||||
"eslint-plugin-vue": "9.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="80px" height="80px"><path fill="#dff0fe" d="M17.537 38.5L18.966 18.5 21.034 18.5 22.463 38.5z"/><path fill="#4788c7" d="M20.569,19l1.357,19h-3.852l1.357-19H20.569 M21.5,18h-3L17,39h6L21.5,18L21.5,18z"/><path fill="none" stroke="#4788c7" stroke-linecap="round" stroke-miterlimit="10" d="M13.269,37.237 C6.38,34.545,1.5,27.843,1.5,20C1.5,9.783,9.783,1.5,20,1.5S38.5,9.783,38.5,20c0,7.845-4.883,14.55-11.776,17.24"/><path fill="none" stroke="#4788c7" stroke-linecap="round" stroke-miterlimit="10" d="M13.565,31.813 C9.357,29.527,6.5,25.068,6.5,19.942c0-7.456,6.044-13.5,13.5-13.5s13.5,6.044,13.5,13.5c0,5.083-2.81,9.511-6.961,11.813"/><path fill="none" stroke="#4788c7" stroke-linecap="round" stroke-miterlimit="10" d="M13.441,25.407 C12.228,23.937,11.5,22.054,11.5,20c0-4.694,3.806-8.5,8.5-8.5s8.5,3.806,8.5,8.5c0,2.043-0.721,3.917-1.921,5.383"/><g><path fill="#98ccfd" d="M20 16.5A3.5 3.5 0 1 0 20 23.5A3.5 3.5 0 1 0 20 16.5Z"/><path fill="#4788c7" d="M20,17c1.654,0,3,1.346,3,3s-1.346,3-3,3s-3-1.346-3-3S18.346,17,20,17 M20,16 c-2.209,0-4,1.791-4,4s1.791,4,4,4s4-1.791,4-4S22.209,16,20,16L20,16z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
+36
-2
@@ -10,6 +10,7 @@ import { useSiteStore } from 'src/stores/site'
|
||||
import { useUserStore } from 'src/stores/user'
|
||||
import { setCssVar, useQuasar } from 'quasar'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
import '@mdi/font/css/materialdesignicons.css'
|
||||
|
||||
@@ -27,7 +28,7 @@ const userStore = useUserStore()
|
||||
|
||||
// I18N
|
||||
|
||||
const { t } = useI18n()
|
||||
const i18n = useI18n({ useScope: 'global' })
|
||||
|
||||
// ROUTER
|
||||
|
||||
@@ -88,6 +89,35 @@ async function applyTheme () {
|
||||
}
|
||||
}
|
||||
|
||||
// LOCALE
|
||||
|
||||
async function fetchLocaleStrings (locale) {
|
||||
try {
|
||||
const resp = await APOLLO_CLIENT.query({
|
||||
query: gql`
|
||||
query fetchLocaleStrings (
|
||||
$locale: String!
|
||||
) {
|
||||
localeStrings (
|
||||
locale: $locale
|
||||
)
|
||||
}
|
||||
`,
|
||||
fetchPolicy: 'cache-first',
|
||||
variables: {
|
||||
locale
|
||||
}
|
||||
})
|
||||
return resp?.data?.localeStrings
|
||||
} catch (err) {
|
||||
console.warn(err)
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Failed to load locale strings.'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// INIT SITE STORE
|
||||
|
||||
if (typeof siteConfig !== 'undefined') {
|
||||
@@ -110,6 +140,10 @@ router.beforeEach(async (to, from) => {
|
||||
await siteStore.loadSite(window.location.hostname)
|
||||
console.info(`Using Site ID ${siteStore.id}`)
|
||||
}
|
||||
// Locales
|
||||
if (!i18n.availableLocales.includes('en')) {
|
||||
i18n.setLocaleMessage('en', await fetchLocaleStrings('en'))
|
||||
}
|
||||
// User Auth
|
||||
await userStore.refreshAuth()
|
||||
// User Profile
|
||||
@@ -128,7 +162,7 @@ EVENT_BUS.on('logout', () => {
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
icon: 'las la-sign-out-alt',
|
||||
message: t('auth.logoutSuccess')
|
||||
message: i18n.t('auth.logoutSuccess')
|
||||
})
|
||||
})
|
||||
EVENT_BUS.on('applyTheme', () => {
|
||||
|
||||
+4
-3
@@ -1,12 +1,13 @@
|
||||
import { boot } from 'quasar/wrappers'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import messages from 'src/i18n'
|
||||
|
||||
export default boot(({ app }) => {
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: 'en-US',
|
||||
messages
|
||||
locale: 'en',
|
||||
fallbackLocale: 'en',
|
||||
fallbackWarn: false,
|
||||
messages: {}
|
||||
})
|
||||
|
||||
// Set i18n instance on app
|
||||
|
||||
@@ -449,6 +449,10 @@ function processContent (newContent) {
|
||||
})
|
||||
}
|
||||
|
||||
function openEditorSettings () {
|
||||
siteStore.$patch({ overlay: 'EditorMarkdownConfig' })
|
||||
}
|
||||
|
||||
// MOUNTED
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -601,6 +605,7 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
EVENT_BUS.on('insertAsset', insertAssetClb)
|
||||
EVENT_BUS.on('openEditorSettings', openEditorSettings)
|
||||
|
||||
// this.$root.$on('editorInsert', opts => {
|
||||
// switch (opts.kind) {
|
||||
@@ -637,6 +642,7 @@ onMounted(async () => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
EVENT_BUS.off('insertAsset', insertAssetClb)
|
||||
EVENT_BUS.off('openEditorSettings', openEditorSettings)
|
||||
if (editor) {
|
||||
editor.dispose()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
<template lang="pug">
|
||||
q-layout(view='hHh lpR fFf', container)
|
||||
q-header.card-header.q-px-md.q-py-sm
|
||||
q-icon(name='img:/_assets/icons/ultraviolet-markdown.svg', left, size='md')
|
||||
span {{t('editor.settings.markdown')}}
|
||||
q-space
|
||||
q-btn.q-mr-sm(
|
||||
flat
|
||||
rounded
|
||||
color='white'
|
||||
:aria-label='t(`common.actions.refresh`)'
|
||||
icon='las la-question-circle'
|
||||
:href='siteStore.docsBase + `/editor/markdown`'
|
||||
target='_blank'
|
||||
type='a'
|
||||
)
|
||||
q-btn-group(push)
|
||||
q-btn(
|
||||
push
|
||||
color='grey-6'
|
||||
text-color='white'
|
||||
:aria-label='t(`common.actions.refresh`)'
|
||||
icon='las la-redo-alt'
|
||||
@click='load'
|
||||
:loading='state.loading > 0'
|
||||
)
|
||||
q-tooltip(anchor='center left', self='center right') {{t(`common.actions.refresh`)}}
|
||||
q-btn(
|
||||
push
|
||||
color='white'
|
||||
text-color='grey-7'
|
||||
:label='t(`common.actions.cancel`)'
|
||||
:aria-label='t(`common.actions.cancel`)'
|
||||
icon='las la-times'
|
||||
@click='close'
|
||||
)
|
||||
q-btn(
|
||||
push
|
||||
color='positive'
|
||||
text-color='white'
|
||||
:label='t(`common.actions.apply`)'
|
||||
:aria-label='t(`common.actions.apply`)'
|
||||
icon='las la-check'
|
||||
@click='save'
|
||||
:disabled='state.loading > 0'
|
||||
)
|
||||
q-page-container
|
||||
q-page.q-pa-md(style='max-width: 1200px; margin: 0 auto;')
|
||||
q-card.shadow-1.q-py-sm
|
||||
q-item(tag='label')
|
||||
blueprint-icon(icon='enter-key')
|
||||
q-item-section
|
||||
q-item-label {{t(`editor.settings.previewShown`)}}
|
||||
q-item-label(caption) {{t(`editor.settings.previewShownHint`)}}
|
||||
q-item-section(avatar)
|
||||
q-toggle(
|
||||
v-model='state.config.previewShown'
|
||||
color='primary'
|
||||
checked-icon='las la-check'
|
||||
unchecked-icon='las la-times'
|
||||
:aria-label='t(`editor.settings.previewShown`)'
|
||||
)
|
||||
q-separator.q-my-sm(inset)
|
||||
q-item
|
||||
blueprint-icon(icon='width')
|
||||
q-item-section
|
||||
q-item-label {{t(`editor.settings.fontSize`)}}
|
||||
q-item-label(caption) {{t(`editor.settings.fontSizeHint`)}}
|
||||
q-item-section(side)
|
||||
q-input(
|
||||
type='number'
|
||||
min='10'
|
||||
max='32'
|
||||
style='width: 100px;'
|
||||
outlined
|
||||
v-model='state.config.fontSize'
|
||||
dense
|
||||
:aria-label='t(`editor.settings.fontSize`)'
|
||||
)
|
||||
|
||||
q-inner-loading(:showing='state.loading > 0')
|
||||
q-spinner(color='accent', size='lg')
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { onMounted, reactive } from 'vue'
|
||||
import gql from 'graphql-tag'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
import { useEditorStore } from 'src/stores/editor'
|
||||
import { useSiteStore } from 'src/stores/site'
|
||||
import { useUserStore } from 'src/stores/user'
|
||||
|
||||
// QUASAR
|
||||
|
||||
const $q = useQuasar()
|
||||
|
||||
// STORES
|
||||
|
||||
const editorStore = useEditorStore()
|
||||
const siteStore = useSiteStore()
|
||||
const userStore = useUserStore()
|
||||
|
||||
// I18N
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
// DATA
|
||||
|
||||
const state = reactive({
|
||||
config: {
|
||||
previewShown: false,
|
||||
fontSize: 16
|
||||
},
|
||||
loading: 0
|
||||
})
|
||||
|
||||
// METHODS
|
||||
|
||||
function close () {
|
||||
siteStore.$patch({ overlay: '' })
|
||||
}
|
||||
|
||||
async function load () {
|
||||
state.loading++
|
||||
$q.loading.show()
|
||||
try {
|
||||
const resp = await APOLLO_CLIENT.query({
|
||||
query: gql`
|
||||
query loadEditorUserSettings (
|
||||
$editor: String!
|
||||
) {
|
||||
editorUserSettings (editor: "markdown")
|
||||
}`,
|
||||
fetchPolicy: 'network-only'
|
||||
})
|
||||
state.config = cloneDeep(resp?.data?.editorUserSettings)
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Failed to fetch Markdown editor settings.'
|
||||
})
|
||||
}
|
||||
$q.loading.hide()
|
||||
state.loading--
|
||||
}
|
||||
|
||||
async function save () {
|
||||
state.loading++
|
||||
try {
|
||||
const respRaw = await APOLLO_CLIENT.mutate({
|
||||
mutation: gql`
|
||||
mutation saveEditorUserSettings (
|
||||
$config: JSON!
|
||||
) {
|
||||
saveEditorUserSettings (
|
||||
editor: "markdown"
|
||||
config: $config
|
||||
) {
|
||||
operation {
|
||||
succeeded
|
||||
slug
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
config: state.config
|
||||
}
|
||||
})
|
||||
if (respRaw?.data?.saveEditorUserSettings?.operation?.succeeded) {
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
message: t('admin.editors.markdown.saveSuccess')
|
||||
})
|
||||
close()
|
||||
} else {
|
||||
throw new Error(respRaw?.data?.saveEditorUserSettings?.operation?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Failed to save Markdown editor settings.',
|
||||
caption: err.message
|
||||
})
|
||||
}
|
||||
state.loading--
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
load()
|
||||
})
|
||||
</script>
|
||||
@@ -19,6 +19,10 @@ import { useSiteStore } from '../stores/site'
|
||||
import LoadingGeneric from './LoadingGeneric.vue'
|
||||
|
||||
const overlays = {
|
||||
EditorMarkdownConfig: defineAsyncComponent({
|
||||
loader: () => import('./EditorMarkdownUserSettingsOverlay.vue'),
|
||||
loadingComponent: LoadingGeneric
|
||||
}),
|
||||
FileManager: defineAsyncComponent({
|
||||
loader: () => import('./FileManager.vue'),
|
||||
loadingComponent: LoadingGeneric
|
||||
|
||||
@@ -32,8 +32,8 @@ q-card.page-properties-dialog
|
||||
)
|
||||
q-scroll-area(
|
||||
ref='scrollArea'
|
||||
:thumb-style='siteStore.thumbStyle'
|
||||
:bar-style='siteStore.barStyle'
|
||||
:thumb-style='siteStore.scrollStyle.thumb'
|
||||
:bar-style='siteStore.scrollStyle.bar'
|
||||
style='height: calc(100% - 50px);'
|
||||
)
|
||||
q-card-section(id='refCardInfo')
|
||||
|
||||
@@ -37,7 +37,7 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
|
||||
q-icon(:name='item.icon', size='sm')
|
||||
q-item-section
|
||||
q-item-label {{item.title}}
|
||||
.page-save-dialog-path.font-robotomono {{folderPath}}
|
||||
.page-save-dialog-path.font-robotomono {{currentFolderPath}}
|
||||
q-list.q-py-sm
|
||||
q-item
|
||||
blueprint-icon(icon='new-document')
|
||||
@@ -202,7 +202,7 @@ const barStyle = {
|
||||
|
||||
// COMPUTED
|
||||
|
||||
const folderPath = computed(() => {
|
||||
const currentFolderPath = computed(() => {
|
||||
if (!state.currentFolderId) {
|
||||
return '/'
|
||||
} else {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import en from './locales/en.json'
|
||||
|
||||
export default {
|
||||
'en-US': en
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -228,6 +228,31 @@ q-page.admin-general
|
||||
:options='reasonForChangeModes'
|
||||
)
|
||||
|
||||
//- -----------------------
|
||||
//- Defaults
|
||||
//- -----------------------
|
||||
q-card.q-pb-sm.q-mt-md(v-if='state.config.defaults')
|
||||
q-card-section
|
||||
.text-subtitle1 {{t('admin.general.defaults')}}
|
||||
q-item
|
||||
blueprint-icon(icon='depth')
|
||||
q-item-section
|
||||
q-item-label {{t(`admin.general.defaultTocDepth`)}}
|
||||
q-item-label(caption) {{t(`admin.general.defaultTocDepthHint`)}}
|
||||
q-item-section.col-auto.q-pl-sm(style='min-width: 180px;')
|
||||
.text-caption {{t('editor.props.tocMinMaxDepth')}} #[strong (H{{state.config.defaults.tocDepth.min}} → H{{state.config.defaults.tocDepth.max}})]
|
||||
q-range(
|
||||
v-model='state.config.defaults.tocDepth'
|
||||
:min='1'
|
||||
:max='6'
|
||||
color='primary'
|
||||
:left-label-value='`H` + state.config.defaults.tocDepth.min'
|
||||
:right-label-value='`H` + state.config.defaults.tocDepth.max'
|
||||
snap
|
||||
label
|
||||
markers
|
||||
)
|
||||
|
||||
.col-12.col-lg-5
|
||||
//- -----------------------
|
||||
//- Logo
|
||||
@@ -316,29 +341,24 @@ q-page.admin-general
|
||||
.text-caption.q-ml-sm Dolor sit amet...
|
||||
|
||||
//- -----------------------
|
||||
//- Defaults
|
||||
//- Discovery
|
||||
//- -----------------------
|
||||
q-card.q-pb-sm.q-mt-md(v-if='state.config.defaults')
|
||||
q-card.q-pb-sm.q-mt-md
|
||||
q-card-section
|
||||
.text-subtitle1 {{t('admin.general.defaults')}}
|
||||
q-item
|
||||
blueprint-icon(icon='depth')
|
||||
.text-subtitle1 {{t('admin.general.discovery')}}
|
||||
q-item(tag='label')
|
||||
blueprint-icon(icon='cellular-network')
|
||||
q-item-section
|
||||
q-item-label {{t(`admin.general.defaultTocDepth`)}}
|
||||
q-item-label(caption) {{t(`admin.general.defaultTocDepthHint`)}}
|
||||
q-item-section.col-auto.q-pl-sm(style='min-width: 180px;')
|
||||
.text-caption {{t('editor.props.tocMinMaxDepth')}} #[strong (H{{state.config.defaults.tocDepth.min}} → H{{state.config.defaults.tocDepth.max}})]
|
||||
q-range(
|
||||
v-model='state.config.defaults.tocDepth'
|
||||
:min='1'
|
||||
:max='6'
|
||||
q-item-label {{t(`admin.general.discoverable`)}}
|
||||
q-item-label(caption) {{t(`admin.general.discoverableHint`)}}
|
||||
q-item-section(avatar)
|
||||
q-toggle(
|
||||
v-model='state.config.discoverable'
|
||||
color='primary'
|
||||
:left-label-value='`H` + state.config.defaults.tocDepth.min'
|
||||
:right-label-value='`H` + state.config.defaults.tocDepth.max'
|
||||
snap
|
||||
label
|
||||
markers
|
||||
)
|
||||
checked-icon='las la-check'
|
||||
unchecked-icon='las la-times'
|
||||
:aria-label='t(`admin.general.discoverable`)'
|
||||
)
|
||||
|
||||
//- -----------------------
|
||||
//- Uploads
|
||||
@@ -519,6 +539,7 @@ const state = reactive({
|
||||
reasonForChange: 'off',
|
||||
profile: false
|
||||
},
|
||||
discoverable: false,
|
||||
defaults: {
|
||||
timezone: '',
|
||||
dateFormat: '',
|
||||
@@ -618,6 +639,7 @@ async function load () {
|
||||
reasonForChange
|
||||
search
|
||||
}
|
||||
discoverable
|
||||
defaults {
|
||||
tocDepth {
|
||||
min
|
||||
@@ -687,6 +709,7 @@ async function save () {
|
||||
profile: state.config.features?.profile ?? false,
|
||||
search: state.config.features?.search ?? false
|
||||
},
|
||||
discoverable: state.config.discoverable ?? false,
|
||||
defaults: {
|
||||
tocDepth: {
|
||||
min: state.config.defaults?.tocDepth?.min ?? 1,
|
||||
|
||||
@@ -60,7 +60,7 @@ q-page.admin-locale
|
||||
q-item-section
|
||||
q-select(
|
||||
outlined
|
||||
v-model='state.selectedLocale'
|
||||
v-model='state.primary'
|
||||
:options='installedLocales'
|
||||
option-value='code'
|
||||
option-label='name'
|
||||
@@ -185,9 +185,8 @@ useMeta({
|
||||
const state = reactive({
|
||||
loading: 0,
|
||||
locales: [],
|
||||
selectedLocale: 'en',
|
||||
namespacing: false,
|
||||
namespaces: []
|
||||
primary: 'en',
|
||||
active: []
|
||||
})
|
||||
|
||||
// COMPUTED
|
||||
@@ -224,7 +223,7 @@ async function load () {
|
||||
query: gql`
|
||||
query getLocales ($siteId: UUID!) {
|
||||
locales {
|
||||
availability
|
||||
completeness
|
||||
code
|
||||
createdAt
|
||||
isInstalled
|
||||
@@ -232,15 +231,18 @@ async function load () {
|
||||
isRTL
|
||||
name
|
||||
nativeName
|
||||
region
|
||||
script
|
||||
updatedAt
|
||||
}
|
||||
siteById(
|
||||
id: $siteId
|
||||
) {
|
||||
id
|
||||
locale
|
||||
localeNamespacing
|
||||
localeNamespaces
|
||||
locales {
|
||||
primary
|
||||
active
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
@@ -250,11 +252,10 @@ async function load () {
|
||||
fetchPolicy: 'network-only'
|
||||
})
|
||||
state.locales = cloneDeep(resp?.data?.locales)
|
||||
state.selectedLocale = cloneDeep(resp?.data?.siteById?.locale)
|
||||
state.namespacing = cloneDeep(resp?.data?.siteById?.localeNamespacing)
|
||||
state.namespaces = cloneDeep(resp?.data?.siteById?.localeNamespaces)
|
||||
if (!state.namespaces.includes(state.selectedLocale)) {
|
||||
state.namespaces.push(state.selectedLocale)
|
||||
state.primary = cloneDeep(resp?.data?.siteById?.locales?.primary)
|
||||
state.active = cloneDeep(resp?.data?.siteById?.locales?.active ?? [])
|
||||
if (!state.active.includes(state.primary)) {
|
||||
state.active.push(state.primary)
|
||||
}
|
||||
$q.loading.hide()
|
||||
state.loading--
|
||||
|
||||
@@ -320,6 +320,8 @@ export const usePageStore = defineStore('page', {
|
||||
await editorStore.fetchConfigs()
|
||||
}
|
||||
|
||||
const noDefaultPath = Boolean(!path && path !== '')
|
||||
|
||||
// -> Init editor
|
||||
editorStore.$patch({
|
||||
originPageId: editorStore.isActive ? editorStore.originPageId : this.id, // Don't replace if already in edit mode
|
||||
@@ -352,7 +354,9 @@ export const usePageStore = defineStore('page', {
|
||||
mode: 'edit'
|
||||
})
|
||||
|
||||
this.router.push(`/_create/${editor}`)
|
||||
if (noDefaultPath) {
|
||||
this.router.push(`/_create/${editor}`)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* PAGE - EDIT
|
||||
|
||||
+26
-13
@@ -2,6 +2,8 @@ import { defineStore } from 'pinia'
|
||||
import gql from 'graphql-tag'
|
||||
import { clone } from 'lodash-es'
|
||||
|
||||
import { useUserStore } from './user'
|
||||
|
||||
export const useSiteStore = defineStore('site', {
|
||||
state: () => ({
|
||||
routerLoading: false,
|
||||
@@ -53,25 +55,36 @@ export const useSiteStore = defineStore('site', {
|
||||
showSharingMenu: true,
|
||||
showPrintBtn: true
|
||||
},
|
||||
thumbStyle: {
|
||||
right: '2px',
|
||||
borderRadius: '5px',
|
||||
backgroundColor: '#000',
|
||||
width: '5px',
|
||||
opacity: 0.15
|
||||
},
|
||||
barStyle: {
|
||||
backgroundColor: '#FAFAFA',
|
||||
width: '9px',
|
||||
opacity: 1
|
||||
},
|
||||
sideDialogShown: false,
|
||||
sideDialogComponent: '',
|
||||
docsBase: 'https://next.js.wiki/docs'
|
||||
}),
|
||||
getters: {
|
||||
overlayIsShown: (state) => Boolean(state.overlay),
|
||||
sideNavIsDisabled: (state) => Boolean(state.theme.sidebarPosition === 'off')
|
||||
sideNavIsDisabled: (state) => Boolean(state.theme.sidebarPosition === 'off'),
|
||||
scrollStyle: (state) => {
|
||||
const userStore = useUserStore()
|
||||
let isDark = false
|
||||
if (userStore.appearance === 'site') {
|
||||
isDark = state.theme.dark
|
||||
} else if (userStore.appearance === 'dark') {
|
||||
isDark = true
|
||||
}
|
||||
return {
|
||||
thumb: {
|
||||
right: '2px',
|
||||
borderRadius: '5px',
|
||||
backgroundColor: isDark ? '#FFF' : '#000',
|
||||
width: '5px',
|
||||
opacity: isDark ? 0.25 : 0.15
|
||||
},
|
||||
bar: {
|
||||
backgroundColor: isDark ? '#000' : '#FAFAFA',
|
||||
width: '9px',
|
||||
opacity: isDark ? 0.25 : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
openFileManager (opts) {
|
||||
|
||||
Reference in New Issue
Block a user