diff --git a/README.md b/README.md
index 6a670571..0afe4f56 100644
--- a/README.md
+++ b/README.md
@@ -2,13 +2,8 @@
-[![Release](https://img.shields.io/github/release/Requarks/wiki.svg?style=flat&maxAge=3600)](https://github.com/Requarks/wiki/releases)
[![License](https://img.shields.io/badge/license-AGPLv3-blue.svg?style=flat)](https://github.com/requarks/wiki/blob/master/LICENSE)
[![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-green.svg?style=flat&logo=javascript&logoColor=white)](http://standardjs.com/)
-[![Downloads](https://img.shields.io/github/downloads/Requarks/wiki/total.svg?style=flat&logo=github)](https://github.com/Requarks/wiki/releases)
-[![Docker Pulls](https://img.shields.io/docker/pulls/requarks/wiki.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/requarks/wiki/)
-[![Build + Publish](https://github.com/Requarks/wiki/actions/workflows/build.yml/badge.svg)](https://github.com/Requarks/wiki/actions/workflows/build.yml)
-[![Huntr](https://img.shields.io/badge/security%20bounty-disclose-brightgreen.svg?style=flat&logo=cachet&logoColor=white)](https://huntr.dev/bounties/disclose)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/ngpixel?logo=github&color=ea4aaa)](https://github.com/users/NGPixel/sponsorship)
[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/wikijs?label=backers&color=218bff&logo=opencollective&logoColor=white)](https://opencollective.com/wikijs)
[![Chat on Slack](https://img.shields.io/badge/slack-requarks-CC2B5E.svg?style=flat&logo=slack)](https://wiki.requarks.io/slack)
@@ -16,412 +11,135 @@
[![Reddit](https://img.shields.io/badge/reddit-%2Fr%2Fwikijs-orange?logo=reddit&logoColor=white)](https://www.reddit.com/r/wikijs/)
[![Subscribe to Newsletter](https://img.shields.io/badge/newsletter-subscribe-yellow.svg?style=flat&logo=mailchimp)](https://blog.js.wiki/subscribe)
-##### A modern, lightweight and powerful wiki app built on NodeJS
+##### Next Generation Open Source Wiki
-- **[Official Website](https://js.wiki/)**
-- **[Documentation](https://docs.requarks.io/)**
-- [Requirements](https://docs.requarks.io/install/requirements)
-- [Installation](https://docs.requarks.io/install)
-- [Demo](https://docs.requarks.io/demo)
-- [Changelog](https://docs.requarks.io/releases)
-- [Feature Requests](https://feedback.js.wiki/wiki)
-- [Chat with us on Slack](https://wiki.requarks.io/slack)
-- [Translations](https://docs.requarks.io/dev/translations) *(We need your help!)*
-- [E2E Testing Results](https://dashboard.cypress.io/projects/r7qxah/runs)
-- [Special Thanks](#special-thanks)
-- [Contribute](#contributors)
+- **[Official Website](https://next.js.wiki/)**
+- **[Documentation](https://next.js.wiki/docs/)**
-[Follow our Twitter feed](https://twitter.com/requarks) to learn about upcoming updates and new releases!
+:warning: :warning: **THIS IS A VERY BUGGY, INCOMPLETE AND NON-SECURE DEVELOPMENT BRANCH! USE AT YOUR OWN RISK! THERE'S NO UPGRADE PATH FROM THIS BUILD.** :warning: :warning:
-
Donate
+The current stable release (2.x) is available at https://js.wiki
-
+---
-Wiki.js is an open source project that has been made possible due to the generous contributions by community [backers](https://wiki.js.org/about). If you are interested in supporting this project, please consider [becoming a sponsor](https://github.com/users/NGPixel/sponsorship), [becoming a patron](https://www.patreon.com/requarks), donating to our [OpenCollective](https://opencollective.com/wikijs), via [Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FLV5X255Z9CJU&source=url) or via Ethereum (`0xe1d55c19ae86f6bcbfb17e7f06ace96bdbb22cb5`).
-
- [![Become a Sponsor](https://img.shields.io/badge/donate-github-ea4aaa.svg?style=popout&logo=github)](https://github.com/users/NGPixel/sponsorship)
- [![Become a Patron](https://img.shields.io/badge/donate-patreon-orange.svg?style=popout&logo=patreon)](https://www.patreon.com/requarks)
- [![Donate on OpenCollective](https://img.shields.io/badge/donate-open%20collective-blue.svg?style=popout&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIyNTZweCIgaGVpZ2h0PSIyNTZweCIgdmlld0JveD0iMCAwIDI1NiAyNTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQiPjxnPjxwYXRoIGQ9Ik0yMDkuNzY1MTQ0LDEyOC4xNDk5NzkgQzIwOS43NjUxNDQsMTQ0LjE2MzMgMjA0Ljg2NDM4MSwxNTkuNDg5ODkgMTk2LjQ5ODc0NywxNzIuNzI1MDcyIEwyMjkuOTQ1Njc1LDIwNi4xNzE5OTkgQzI0Ni42ODIxMDUsMTgzLjg1Njc1OSAyNTUuNzI5MzA3LDE1Ni43MTUxNTIgMjU1LjcyOTMwNywxMjguODIxMTAyIEMyNTUuNzI5MzA3LDk5LjU1Njk5MTcgMjQ1Ljk3NDYwMyw3My4wNzEwMjA3IDIyOS4yNTg5NDQsNTEuNDg1ODEyOCBMMTk2LjQ4MzE0LDg0LjIxNDc5NCBDMjA1LjEyMjU2MSw5Ny4yMjI0NjgzIDIwOS43MzY5MDcsMTEyLjQ4NzgxIDIwOS43NDk1MzcsMTI4LjEwMzE1NiBMMjA5Ljc2NTE0NCwxMjguMTQ5OTc5IFoiIGZpbGw9IiNCOEQzRjQiPjwvcGF0aD48cGF0aCBkPSJNMTI3LjUxMzQ4NCwyMTAuMzU0ODE2IEM4Mi4xNDYwODcyLDIxMC4yNjg5NTggNDUuMzg3NTA5NCwxNzMuNTE3MzU4IDQ1LjI5MzAzOTMsMTI4LjE0OTk3OSBDNDUuMzYxNzUwMiw4Mi43NjQzMTM4IDgyLjEyNzg0ODcsNDUuOTg0MjU3IDEyNy41MTM0ODQsNDUuODk4MzE4NiBDMTQ0LjI0NDc1Miw0NS44OTgzMTg2IDE1OS41NzEzNDIsNTAuNzk5MDgxNyAxNzIuMTE5NzkyLDU5LjE2NDcxNTQgTDIwNC44NjQzODEsMjYuMzg4OTExNiBDMTgyLjU0MzY1LDkuNjY2NjUxMjkgMTU1LjQwMzQyOSwwLjYzMDg2MzI5OCAxMjcuNTEzNDg0LDAuNjM2NDk0NDAzIEM1Ny4xMjM1NDM3LDAuNjM2NDk0NDAzIDAsNTcuNzYwMDM4MSAwLDEyOC4xNDk5NzkgQzAsMTk4LjUwODcwNCA1Ny4xMjM1NDM3LDI1NS42NjM0NjMgMTI3LjUxMzQ4NCwyNTUuNjYzNDYzIEMxNTUuNTM3MzUyLDI1NS43NDA4NzYgMTgyLjc3NTk4OSwyNDYuNDA4NTEgMjA0Ljg2NDM4MSwyMjkuMTYxODg0IEwxNzEuNDE3NDU0LDE5NS43MzA1NjQgQzE1OS41NTU3MzQsMjA1LjQ4NTI2OCAxNDQuMjYwMzU5LDIxMC4zNTQ4MTYgMTI3LjUxMzQ4NCwyMTAuMzU0ODE2IEwxMjcuNTEzNDg0LDIxMC4zNTQ4MTYgWiIgZmlsbD0iIzdGQURGMiI+PC9wYXRoPjwvZz48L3N2Zz4=)](https://opencollective.com/wikijs)
- [![Donate via Paypal](https://img.shields.io/badge/donate-paypal-blue.svg?style=popout&logo=paypal)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FLV5X255Z9CJU&source=url)
- [![Donate via Ethereum](https://img.shields.io/badge/donate-ethereum-999.svg?style=popout&logo=ethereum&logoColor=CCC)](https://etherscan.io/address/0xe1d55c19ae86f6bcbfb17e7f06ace96bdbb22cb5)
- [![Donate via Bitcoin](https://img.shields.io/badge/donate-bitcoin-ff9900.svg?style=popout&logo=bitcoin&logoColor=CCC)](https://checkout.opennode.com/p/2553c612-f863-4407-82b3-1a7685268747)
- [![Buy a T-Shirt](https://img.shields.io/badge/buy-t--shirts-teal.svg?style=popout&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjBweCIgeT0iMHB4Igp3aWR0aD0iMjQiIGhlaWdodD0iMjQiCnZpZXdCb3g9IjAgMCAxOTIgMTkyIgpzdHlsZT0iIGZpbGw6IzAwMDAwMDsiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIj48L3BhdGg+PGcgZmlsbD0iIzFhYmM5YyI+PGcgaWQ9InN1cmZhY2UxIj48cGF0aCBkPSJNOTYsMGMtMTUuMjE4NzUsMCAtMjQuNjg3NSwzLjY1NjI1IC0yNS41LDRsLTIyLjUsNy4yNWMtMTAuNDA2MjUsMy4xODc1IC0xOS4wOTM3NSw5LjQzNzUgLTI1LjUsMTguMjVsLTIyLjUsNDIuNWwyNy4yNSwxNi43NWwxMi43NSwtMjR2MTE5LjI1YzAsNC40MDYyNSAyNS4wNjI1LDggNTYsOGMzMC45Mzc1LDAgNTYsLTMuNTkzNzUgNTYsLTh2LTExOS4yNWwxMi43NSwyNGwyNy4yNSwtMTYuNzVsLTIyLjUsLTQyLjVjLTYuNDA2MjUsLTguODEyNSAtMTUuMTU2MjUsLTE1LjA2MjUgLTI0Ljc1LC0xOC4yNWwtMjIuMjUsLTcuMjVjLTAuMTg3NSwwIC0xLjAzMTI1LDEuMzEyNSAtMiwyLjc1bDEuMjUsLTIuNWMwLDAgLTkuODQzNzUsLTQuMjUgLTI1Ljc1LC00LjI1ek05Niw4YzExLjQwNjI1LDAgMTguNDM3NSwyLjI1IDIxLDMuMjVjLTQuNDY4NzUsNS43NSAtMTEuNDA2MjUsMTIuNzUgLTIxLDEyLjc1Yy05LjQwNjI1LDAgLTE2LjQwNjI1LC03LjA2MjUgLTIwLjc1LC0xMi43NWMyLjg3NSwtMS4wNjI1IDkuODc1LC0zLjI1IDIwLjc1LC0zLjI1eiI+PC9wYXRoPjwvZz48L2c+PC9nPjwvc3ZnPg==)](https://wikijs.threadless.com)
+## Requirements
-
+- Node.js **18.x** or later
+- Yarn
+- PostgreSQL **11** or later
-Gold Tier Sponsors
+## Setup
-
-
-
-
-
-
-
-
- |
-
-
-
-
+1. Clone the project
+1. Make a copy of `config.sample.yml` and rename it to `config.yml`
+1. Edit `config.yml` and fill in the database details. **You need an empty PostgreSQL database.**
+1. Run the following commands to install dependencies and generate the client assets:
+ ```sh
+ yarn
+ yarn legacy:build
+ cd ux
+ yarn
+ yarn build
+ cd ..
+ ```
+1. Run this command to start the server:
+ ```sh
+ node server
+ ```
+1. In your browser, navigate to `http://localhost:3000` *(or the IP/hostname of the server and the PORT you defined earlier.)*
+1. Login using the default administrator user:
+ - Email: `admin@example.com`
+ - Password: `12345678`
-GitHub Sponsors
+> **DO NOT** report bugs. This build is **VERY** buggy and **VERY** incomplete. Absolutely **NO** support is provided either.
-Support this project by becoming a sponsor. Your name will show up in the Contribute page of all Wiki.js installations as well as here with a link to your website! [[Become a sponsor](https://github.com/users/NGPixel/sponsorship)]
+## Using VS Code Dev Environment
-
+### Requirements
-
-
-
-
-
-
-- Akira Suenami ([@a-suenami](https://github.com/a-suenami))
-- Arnaud Marchand ([@snuids](https://github.com/snuids))
-- Brian Douglass ([@bhdouglass](https://github.com/bhdouglass))
-- Bryon Vandiver ([@asterick](https://github.com/asterick))
-- Cameron Steele ([@ATechAdventurer](https://github.com/ATechAdventurer))
-- Cloud Data Hosting LLC ([@CloudDataHostingLLC](https://github.com/CloudDataHostingLLC))
-- CrazyMarvin ([@CrazyMarvin](https://github.com/CrazyMarvin))
-- David Christian Holin ([@SirGibihm](https://github.com/SirGibihm))
-- Dragan Espenschied ([@despens](https://github.com/despens))
-- Elijah Zobenko ([@he110](https://github.com/he110))
-- Ernie ([@iamernie](https://github.com/iamernie))
-- Fabio Ferrari ([@devxops](https://github.com/devxops))
-- Finsa S.p.A. ([@finsaspa](https://github.com/finsaspa))
-- Florian Moss ([@florianmoss](https://github.com/florianmoss))
-- GoodCorporateCitizen ([@GoodCorporateCitizen](https://github.com/GoodCorporateCitizen))
-- HeavenBay ([@HeavenBay](https://github.com/heavenbay))
-- Ian Hyzy ([@ianhyzy](https://github.com/ianhyzy))
-- Jaimyn Mayer ([@jabelone](https://github.com/jabelone))
-- Jay Lee ([@polyglotm](https://github.com/polyglotm))
-- Kelly Wardrop ([@dropcoded](https://github.com/dropcoded))
-- Loki ([@binaryloki](https://github.com/binaryloki))
-- MaFarine ([@MaFarine](https://github.com/MaFarine))
-- Marcilio Leite Neto ([@marclneto](https://github.com/marclneto))
-
- |
-
-
-- Mattias Johnson ([@mattiasJohnson](https://github.com/mattiasJohnson))
-- Max Ricketts-Uy ([@MaxRickettsUy](https://github.com/MaxRickettsUy))
-- Mitchell Rowton ([@mrowton](https://github.com/mrowton))
-- M. Scott Ford ([@mscottford](https://github.com/mscottford))
-- Nick Halase ([@nhalase](https://github.com/nhalase))
-- Nina Reynolds ([@cutecycle](https://github.com/cutecycle))
-- Noel Cower ([@nilium](https://github.com/nilium))
-- Philipp Schmitt ([@pschmitt](https://github.com/pschmitt))
-- Robert Lanzke ([@winkelement](https://github.com/winkelement))
-- Sam Martin ([@ABitMoreDepth](https://github.com/ABitMoreDepth))
-- Sean Coffey ([@seanecoffey](https://github.com/seanecoffey))
-- Stephan Kristyn ([@stevek-pro](https://github.com/stevek-pro))
-- Theodore Chu ([@TheodoreChu](https://github.com/TheodoreChu))
-- Tyler Denman ([@tylerguy](https://github.com/tylerguy))
-- Victor Bilgin ([@vbilgin](https://github.com/vbilgin))
-- VMO Solutions ([@vmosolutions](https://github.com/vmosolutions))
-- aniketpanjwani ([@aniketpanjwani](https://github.com/aniketpanjwani))
-- aytaa ([@aytaa](https://github.com/aytaa))
-- magicpotato ([@fortheday](https://github.com/fortheday))
-- motoacs ([@motoacs](https://github.com/motoacs))
-- rburckner ([@rburckner](https://github.com/rburckner))
-- scorpion ([@scorpion](https://github.com/scorpion))
-- valantien ([@valantien](https://github.com/valantien))
-
- |
-
+- VS Code
+- Docker Desktop
+- **Windows-only:** WSL 2 + WSL Integration enabled in Docker Desktop
-OpenCollective Sponsors
+### Usage
-Support this project by becoming a sponsor. Your logo will show up in the Contribute page of all Wiki.js installations as well as here with a link to your website! [[Become a sponsor](https://opencollective.com/wikijs#sponsor)]
+1. Clone the project
+1. Open the project in VS Code
+1. Make sure you have **Dev Containers** extension installed. (On Windows, you need the **WSL** VS Code extension as well.)
+1. Reopen the project in container (from the popup in the lower-right corner of the screen when opening the project, or via the Command Palette (Ctrl+Shift+P) afterwards).
+1. Once in container mode, run the task "Create terminals" from the Command Palette:
+ - Launch the Command Palette (Ctrl+Shift+P)
+ - Type "Run Task" and press Enter
+ - Select the task "Create terminals" and press Enter
+1. Two terminals will launch in split-screen mode at the bottom of the screen. **Server** on the left and **UX** on the right.
+1. In the left-side terminal (Server), run the command:
+ ```sh
+ yarn legacy:build
+ ```
+1. In the right-side terminal (UX), run the command:
+ ```sh
+ yarn build
+ ```
+1. Back in the left-side terminal (Server), run the command:
+ ```sh
+ yarn dev
+ ```
+1. Open your browser to `http://localhost:3000`
+1. Login using the default administrator user:
+ - Email: `admin@example.com`
+ - Password: `12345678`
-
+> **DO NOT** report bugs. This build is **VERY** buggy and **VERY** incomplete. Absolutely **NO** support is provided either.
-Patreon Backers
+### Server Development
-Thank you to all our patrons! 🙏 [[Become a patron](https://www.patreon.com/requarks)]
+From the left-side terminal (Server), run the command:
-
-
-
-
-- Al Romano
-- Alex Balabanov
-- Alex Zen
-- Arti Zirk
-- Brandon Curtis
-- Dave 'Sri' Seah
-- djagoo
-- Douglas Lassance
-- Ernie Reid
-- Etienne
-- Flemis Jurgenheimer
-- Florent
-- Günter Pavlas
-- hong
-- Hope
-- Ian
-
- |
-
-
-- Iskander Callos
-- Josh Stewart
-- Justin Dunsworth
-- Keir
-- Loïc CRAMPON
-- Ludgeir Ibanez
-- Mark Mansur
-- Matt Gedigian
-- Patryk
-- Philipp Schürch
-- Tracey Duffy
-- Richeir
-- Shad Narcher
-- SmartNET.works
-- Stepan Sokolovskyi
-- Zach Maynard
-- 张白驹
-
- |
-
+```sh
+yarn dev
+```
+
+This will launch the server and automatically restart upon modification of any server files.
+
+Only precompiled client assets are served in this mode. See the sections below on how to modify the frontend and run in SPA (Single Page Application) mode.
-OpenCollective Backers
+### Frontend Development (Quasar/Vue 3)
-Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/wikijs#backer)]
+> Make sure you are running `yarn dev` in the left-side terminal (Server) first! Requests still need to be forwarded to the server, even in SPA mode!
-
+If you wish to modify any frontend content (under `/ux`), you need to start the Quasar Dev Server in the right-side terminal (UX):
-Contributors
+```sh
+yarn dev
+```
-This project exists thanks to all the people who contribute. [[Contribute]](https://github.com/Requarks/wiki/blob/master/.github/CONTRIBUTING.md).
-
+You can then access the site at `http://localhost:3001`. Notice the port being `3001` rather than `3000`. The app runs in a SPA (single-page application) mode and automatically hot-reload any modified component. Any requests made to the `/graphql` endpoint are automatically forwarded to the server running on port `3000`, which is why both must be running at the same time.
-Special Thanks
+Note that not all sections/features are available from this mode, notably the page editing features which still relies on the old client code (Vuetify/Vue 2). For example, trying to edit a page will simply not work. You must use the normal mode (port 3000) to edit pages as it relies on legacy client code. As more features gets ported / developed for Vue 3, they will become available in the SPA mode.
-![Algolia](https://js.wiki/legacy/logo_algolia.png)
-[Algolia](https://www.algolia.com/) for providing access to their incredible search engine.
+Any change you make to the frontend will not be reflected on port 3000 until you run the command `yarn build` in the right-side terminal.
-![Browserstack](https://js.wiki/legacy/logo_browserstack.png)
-[Browserstack](https://www.browserstack.com/) for providing access to their great cross-browser testing tools.
+### Legacy Frontend Development (Vuetify/Vue 2)
-![Cloudflare](https://js.wiki/legacy/logo_cloudflare.png)
-[Cloudflare](https://www.cloudflare.com/) for providing their great CDN, SSL and advanced networking services.
+Client code from Wiki.js 2.x is located under `/client`. Some sections still rely on this legacy code (notably the page editing features). Code is gradually being removed from this location and replaced with newer code in `/ux`.
-![DigitalOcean](https://js.wiki/legacy/logo_digitalocean.png)
-[DigitalOcean](https://m.do.co/c/5f7445bfa4d0) for providing hosting of the Wiki.js documentation site.
+In the unlikely event that you need to modify legacy code and regenerate the old client files, you can do so by running in this command in the left-side terminal (Server):
+```sh
+yarn legacy:build
+```
-![Icons8](https://static.requarks.io/logo/icons8-text-h40.png)
-[Icons8](https://icons8.com/) for providing beautiful icon sets.
+Then run `yarn dev` to start the server again.
-![Lokalise](https://static.requarks.io/logo/lokalise-text-h40.png)
-[Lokalise](https://lokalise.com/) for providing access to their great localization tool.
+### pgAdmin
-![Netlify](https://js.wiki/legacy/logo_netlify.png)
-[Netlify](https://www.netlify.com) for providing hosting for landings and blog websites.
+A web version of pgAdmin (a PostgreSQL administration tool) is available at `http://localhost:8000`. Use the login `dev` / `123123` to login.
-![ngrok](https://static.requarks.io/logo/ngrok-h40.png)
-[ngrok](https://ngrok.com) for providing access to their great HTTP tunneling services.
+The server **dev** should already be available under **Servers**. If that's not the case, add a new one with the following settings:
-![Porkbun](https://static.requarks.io/logo/porkbun.png)
-[Porkbun](https://www.porkbun.com) for providing domain registration services.
+- Hostname: `db`
+- Port: `5432`
+- Username: `postgres`
+- Password: `postgres`
+- Database: `postgres`
diff --git a/client/components/editor.vue b/client/components/editor.vue
index 170eb1f3..eaa959a0 100644
--- a/client/components/editor.vue
+++ b/client/components/editor.vue
@@ -141,8 +141,8 @@ export default {
default: null
},
pageId: {
- type: Number,
- default: 0
+ type: String,
+ default: ''
},
checkoutDate: {
type: String,
@@ -369,33 +369,32 @@ export default {
// -> UPDATE EXISTING PAGE
// --------------------------------------------
- const conflictResp = await this.$apollo.query({
- query: gql`
- query ($id: Int!, $checkoutDate: Date!) {
- pages {
- checkConflicts(id: $id, checkoutDate: $checkoutDate)
- }
- }
- `,
- fetchPolicy: 'network-only',
- variables: {
- id: this.pageId,
- checkoutDate: this.checkoutDateActive
- }
- })
- if (_.get(conflictResp, 'data.pages.checkConflicts', false)) {
- this.$root.$emit('saveConflict')
- throw new Error(this.$t('editor:conflict.warning'))
- }
+ // const conflictResp = await this.$apollo.query({
+ // query: gql`
+ // query ($id: Int!, $checkoutDate: Date!) {
+ // pages {
+ // checkConflicts(id: $id, checkoutDate: $checkoutDate)
+ // }
+ // }
+ // `,
+ // fetchPolicy: 'network-only',
+ // variables: {
+ // id: this.pageId,
+ // checkoutDate: this.checkoutDateActive
+ // }
+ // })
+ // if (_.get(conflictResp, 'data.pages.checkConflicts', false)) {
+ // this.$root.$emit('saveConflict')
+ // throw new Error(this.$t('editor:conflict.warning'))
+ // }
let resp = await this.$apollo.mutate({
mutation: gql`
mutation (
- $id: Int!
+ $id: UUID!
$content: String
$description: String
$editor: String
- $isPrivate: Boolean
$isPublished: Boolean
$locale: String
$path: String
@@ -406,32 +405,27 @@ export default {
$tags: [String]
$title: String
) {
- pages {
- update(
- id: $id
- content: $content
- description: $description
- editor: $editor
- isPrivate: $isPrivate
- isPublished: $isPublished
- locale: $locale
- path: $path
- publishEndDate: $publishEndDate
- publishStartDate: $publishStartDate
- scriptCss: $scriptCss
- scriptJs: $scriptJs
- tags: $tags
- title: $title
- ) {
- operation {
- succeeded
- errorCode
- slug
- message
- }
- page {
- updatedAt
- }
+ updatePage(
+ id: $id
+ content: $content
+ description: $description
+ editor: $editor
+ isPublished: $isPublished
+ locale: $locale
+ path: $path
+ publishEndDate: $publishEndDate
+ publishStartDate: $publishStartDate
+ scriptCss: $scriptCss
+ scriptJs: $scriptJs
+ tags: $tags
+ title: $title
+ ) {
+ operation {
+ succeeded
+ message
+ }
+ page {
+ updatedAt
}
}
}
@@ -442,7 +436,6 @@ export default {
description: this.$store.get('page/description'),
editor: this.$store.get('editor/editorKey'),
locale: this.$store.get('page/locale'),
- isPrivate: false,
isPublished: this.$store.get('page/isPublished'),
path: this.$store.get('page/path'),
publishEndDate: this.$store.get('page/publishEndDate') || '',
@@ -453,7 +446,7 @@ export default {
title: this.$store.get('page/title')
}
})
- resp = _.get(resp, 'data.pages.update', {})
+ resp = _.get(resp, 'data.updatePage', {})
if (_.get(resp, 'operation.succeeded')) {
this.checkoutDateActive = _.get(resp, 'page.updatedAt', this.checkoutDateActive)
this.isConflict = false
@@ -547,30 +540,30 @@ export default {
styl.appendChild(document.createTextNode(css))
}
}, 1000)
- },
- apollo: {
- isConflict: {
- query: gql`
- query ($id: Int!, $checkoutDate: Date!) {
- pages {
- checkConflicts(id: $id, checkoutDate: $checkoutDate)
- }
- }
- `,
- fetchPolicy: 'network-only',
- pollInterval: 5000,
- variables () {
- return {
- id: this.pageId,
- checkoutDate: this.checkoutDateActive
- }
- },
- update: (data) => _.cloneDeep(data.pages.checkConflicts),
- skip () {
- return this.mode === 'create' || this.isSaving || !this.isDirty
- }
- }
}
+ // apollo: {
+ // isConflict: {
+ // query: gql`
+ // query ($id: Int!, $checkoutDate: Date!) {
+ // pages {
+ // checkConflicts(id: $id, checkoutDate: $checkoutDate)
+ // }
+ // }
+ // `,
+ // fetchPolicy: 'network-only',
+ // pollInterval: 5000,
+ // variables () {
+ // return {
+ // id: this.pageId,
+ // checkoutDate: this.checkoutDateActive
+ // }
+ // },
+ // update: (data) => _.cloneDeep(data.pages.checkConflicts),
+ // skip () {
+ // return this.mode === 'create' || this.isSaving || !this.isDirty
+ // }
+ // }
+ // }
}
diff --git a/server/controllers/common.js b/server/controllers/common.js
index 34bacd88..21f4b21f 100644
--- a/server/controllers/common.js
+++ b/server/controllers/common.js
@@ -238,51 +238,6 @@ router.get(['/_edit', '/_edit/*'], async (req, res, next) => {
js: ''
}
}
-
- // -> From Template
- if (req.query.from && tmplCreateRegex.test(req.query.from)) {
- let tmplPageId = 0
- let tmplVersionId = 0
- if (req.query.from.indexOf(',')) {
- const q = req.query.from.split(',')
- tmplPageId = _.toSafeInteger(q[0])
- tmplVersionId = _.toSafeInteger(q[1])
- } else {
- tmplPageId = _.toSafeInteger(req.query.from)
- }
-
- if (tmplVersionId > 0) {
- // -> From Page Version
- const pageVersion = await WIKI.db.pageHistory.getVersion({ pageId: tmplPageId, versionId: tmplVersionId })
- if (!pageVersion) {
- _.set(res.locals, 'pageMeta.title', 'Page Not Found')
- return res.status(404).render('notfound', { action: 'template' })
- }
- if (!WIKI.auth.checkAccess(req.user, ['read:history'], { path: pageVersion.path, locale: pageVersion.locale })) {
- _.set(res.locals, 'pageMeta.title', 'Unauthorized')
- return res.render('unauthorized', { action: 'sourceVersion' })
- }
- page.content = Buffer.from(pageVersion.content).toString('base64')
- page.editorKey = pageVersion.editor
- page.title = pageVersion.title
- page.description = pageVersion.description
- } else {
- // -> From Page Live
- const pageOriginal = await WIKI.db.pages.query().findById(tmplPageId)
- if (!pageOriginal) {
- _.set(res.locals, 'pageMeta.title', 'Page Not Found')
- return res.status(404).render('notfound', { action: 'template' })
- }
- if (!WIKI.auth.checkAccess(req.user, ['read:source'], { path: pageOriginal.path, locale: pageOriginal.locale })) {
- _.set(res.locals, 'pageMeta.title', 'Unauthorized')
- return res.render('unauthorized', { action: 'source' })
- }
- page.content = Buffer.from(pageOriginal.content).toString('base64')
- page.editorKey = pageOriginal.editorKey
- page.title = pageOriginal.title
- page.description = pageOriginal.description
- }
- }
}
res.render('editor', { page, injectCode, effectivePermissions })
diff --git a/server/core/asar.js b/server/core/asar.js
index 2578c692..c1521934 100644
--- a/server/core/asar.js
+++ b/server/core/asar.js
@@ -9,7 +9,7 @@ const fs = require('fs')
*/
const packages = {
- 'twemoji': path.join(WIKI.ROOTPATH, `assets/svg/twemoji.asar`)
+ 'twemoji': path.join(WIKI.ROOTPATH, `assets-legacy/svg/twemoji.asar`)
}
module.exports = {
diff --git a/server/db/migrations/3.0.0.js b/server/db/migrations/3.0.0.js
index 35c2af1a..377d85f6 100644
--- a/server/db/migrations/3.0.0.js
+++ b/server/db/migrations/3.0.0.js
@@ -603,7 +603,8 @@ exports.up = async knex => {
auth: {
[authModuleId]: {
password: await bcrypt.hash(process.env.ADMIN_PASS || '12345678', 12),
- mustChangePwd: !process.env.ADMIN_PASS,
+ mustChangePwd: false, // TODO: Revert to true (below) once change password flow is implemented
+ // mustChangePwd: !process.env.ADMIN_PASS,
restrictLogin: false,
tfaRequired: false,
tfaSecret: ''
diff --git a/server/graph/resolvers/page.js b/server/graph/resolvers/page.js
index 7ca03b6f..09710808 100644
--- a/server/graph/resolvers/page.js
+++ b/server/graph/resolvers/page.js
@@ -1,5 +1,6 @@
const _ = require('lodash')
const graphHelper = require('../../helpers/graph')
+const pageHelper = require('../../helpers/page')
module.exports = {
Query: {
@@ -139,7 +140,7 @@ module.exports = {
return results
},
/**
- * FETCH SINGLE PAGE
+ * FETCH SINGLE PAGE BY ID
*/
async pageById (obj, args, context, info) {
let page = await WIKI.db.pages.getPageFromDb(args.id)
@@ -160,6 +161,22 @@ module.exports = {
throw new WIKI.Error.PageNotFound()
}
},
+ /**
+ * FETCH SINGLE PAGE BY PATH
+ */
+ async pageByPath (obj, args, context, info) {
+ const pageArgs = pageHelper.parsePath(args.path)
+ let page = await WIKI.db.pages.getPageFromDb(pageArgs)
+ if (page) {
+ return {
+ ...page,
+ locale: page.localeCode,
+ editor: page.editorKey
+ }
+ } else {
+ throw new Error('ERR_PAGE_NOT_FOUND')
+ }
+ },
/**
* FETCH TAGS
*/
@@ -366,7 +383,7 @@ module.exports = {
user: context.req.user
})
return {
- responseResult: graphHelper.generateSuccess('Page created successfully.'),
+ operation: graphHelper.generateSuccess('Page created successfully.'),
page
}
} catch (err) {
@@ -383,7 +400,7 @@ module.exports = {
user: context.req.user
})
return {
- responseResult: graphHelper.generateSuccess('Page has been updated.'),
+ operation: graphHelper.generateSuccess('Page has been updated.'),
page
}
} catch (err) {
diff --git a/server/graph/schemas/page.graphql b/server/graph/schemas/page.graphql
index 5c48ee83..3d300cfe 100644
--- a/server/graph/schemas/page.graphql
+++ b/server/graph/schemas/page.graphql
@@ -34,6 +34,10 @@ extend type Query {
id: Int!
): Page
+ pageByPath(
+ path: String!
+ ): Page
+
tags: [PageTag]!
searchTags(
@@ -80,7 +84,7 @@ extend type Mutation {
): PageResponse
updatePage(
- id: Int!
+ id: UUID!
content: String
description: String
editor: String
@@ -158,7 +162,7 @@ type PageMigrationResponse {
}
type Page {
- id: Int
+ id: UUID
path: String
hash: String
title: String
diff --git a/server/models/pageHistory.js b/server/models/pageHistory.js
index 33a5bff4..bcf72ec5 100644
--- a/server/models/pageHistory.js
+++ b/server/models/pageHistory.js
@@ -19,7 +19,7 @@ module.exports = class PageHistory extends Model {
hash: {type: 'string'},
title: {type: 'string'},
description: {type: 'string'},
- isPublished: {type: 'boolean'},
+ publishState: {type: 'string'},
publishStartDate: {type: 'string'},
publishEndDate: {type: 'string'},
content: {type: 'string'},
@@ -60,14 +60,6 @@ module.exports = class PageHistory extends Model {
to: 'users.id'
}
},
- editor: {
- relation: Model.BelongsToOneRelation,
- modelClass: require('./editors'),
- join: {
- from: 'pageHistory.editorKey',
- to: 'editors.key'
- }
- },
locale: {
relation: Model.BelongsToOneRelation,
modelClass: require('./locales'),
@@ -89,18 +81,18 @@ module.exports = class PageHistory extends Model {
static async addVersion(opts) {
await WIKI.db.pageHistory.query().insert({
pageId: opts.id,
+ siteId: opts.siteId,
authorId: opts.authorId,
content: opts.content,
contentType: opts.contentType,
description: opts.description,
- editorKey: opts.editorKey,
+ editor: opts.editor,
hash: opts.hash,
- isPrivate: (opts.isPrivate === true || opts.isPrivate === 1),
- isPublished: (opts.isPublished === true || opts.isPublished === 1),
+ publishState: opts.publishState,
localeCode: opts.localeCode,
path: opts.path,
- publishEndDate: opts.publishEndDate || '',
- publishStartDate: opts.publishStartDate || '',
+ publishEndDate: opts.publishEndDate?.toISO(),
+ publishStartDate: opts.publishStartDate?.toISO(),
title: opts.title,
action: opts.action || 'updated',
versionDate: opts.versionDate
@@ -116,7 +108,6 @@ module.exports = class PageHistory extends Model {
'pageHistory.path',
'pageHistory.title',
'pageHistory.description',
- 'pageHistory.isPrivate',
'pageHistory.isPublished',
'pageHistory.publishStartDate',
'pageHistory.publishEndDate',
diff --git a/server/models/pages.js b/server/models/pages.js
index 583a1777..3c374497 100644
--- a/server/models/pages.js
+++ b/server/models/pages.js
@@ -421,8 +421,8 @@ module.exports = class Page extends Model {
content: opts.content,
description: opts.description,
publishState: opts.publishState,
- publishEndDate: opts.publishEndDate || '',
- publishStartDate: opts.publishStartDate || '',
+ publishEndDate: opts.publishEndDate?.toISO(),
+ publishStartDate: opts.publishStartDate?.toISO(),
title: opts.title,
extra: JSON.stringify({
...ogPage.extra,
@@ -439,18 +439,18 @@ module.exports = class Page extends Model {
await WIKI.db.pages.renderPage(page)
WIKI.events.outbound.emit('deletePageFromCache', page.hash)
- // -> Update Search Index
- const pageContents = await WIKI.db.pages.query().findById(page.id).select('render')
- page.safeContent = WIKI.db.pages.cleanHTML(pageContents.render)
- await WIKI.data.searchEngine.updated(page)
+ // // -> Update Search Index
+ // const pageContents = await WIKI.db.pages.query().findById(page.id).select('render')
+ // page.safeContent = WIKI.db.pages.cleanHTML(pageContents.render)
+ // await WIKI.data.searchEngine.updated(page)
// -> Update on Storage
- if (!opts.skipStorage) {
- await WIKI.db.storage.pageEvent({
- event: 'updated',
- page
- })
- }
+ // if (!opts.skipStorage) {
+ // await WIKI.db.storage.pageEvent({
+ // event: 'updated',
+ // page
+ // })
+ // }
// -> Perform move?
if ((opts.locale && opts.locale !== page.localeCode) || (opts.path && opts.path !== page.path)) {
diff --git a/server/views/base.pug b/server/views/base.pug
index 3a4b32ca..6189c5e9 100644
--- a/server/views/base.pug
+++ b/server/views/base.pug
@@ -40,28 +40,20 @@ html(lang=siteConfig.lang)
//- CSS
-
- link(
- type='text/css'
- rel='stylesheet'
- href='/_assets-legacy/css/app.629ebe3c082227dbee31.css'
- )
-
-
//- JS
script(
type='text/javascript'
- src='/_assets-legacy/js/runtime.js?1664769154'
+ src='/_assets-legacy/js/runtime.js'
)
script(
type='text/javascript'
- src='/_assets-legacy/js/app.js?1664769154'
+ src='/_assets-legacy/js/app.js'
)
diff --git a/server/views/editor.pug b/server/views/editor.pug
index b395cbec..322cdf76 100644
--- a/server/views/editor.pug
+++ b/server/views/editor.pug
@@ -7,7 +7,7 @@ block head
block body
#root
editor(
- :page-id=page.id
+ page-id=page.id
locale=page.localeCode
path=page.path
title=page.title
diff --git a/ux/.yarnrc.yml b/ux/.yarnrc.yml
index 2067f48f..39757f21 100644
--- a/ux/.yarnrc.yml
+++ b/ux/.yarnrc.yml
@@ -5,18 +5,12 @@ enableTelemetry: false
nodeLinker: node-modules
packageExtensions:
- '@quasar/vite-plugin@*':
- dependencies:
- 'quasar': '*'
'rollup-plugin-visualizer@*':
dependencies:
'rollup': '*'
'v-network-graph@*':
dependencies:
'd3-force': '*'
- '@intlify/vite-plugin-vue-i18n@*':
- dependencies:
- 'vite': '*'
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
diff --git a/ux/quasar.config.js b/ux/quasar.config.js
index ac81ea2b..9e50b7cb 100644
--- a/ux/quasar.config.js
+++ b/ux/quasar.config.js
@@ -77,6 +77,7 @@ module.exports = configure(function (/* ctx */) {
extendViteConf (viteConf) {
viteConf.build.assetsDir = '_assets'
+ // viteConf.resolve.alias.vue = '/workspace/ux/node_modules/vue/dist/vue.esm-bundler.js'
// viteConf.build.rollupOptions = {
// ...viteConf.build.rollupOptions ?? {},
// external: [
diff --git a/ux/src/components/BlueprintIcon.vue b/ux/src/components/BlueprintIcon.vue
index 882d5116..1653d981 100644
--- a/ux/src/components/BlueprintIcon.vue
+++ b/ux/src/components/BlueprintIcon.vue
@@ -5,7 +5,7 @@ q-item-section(avatar)
:text-color='avatarTextColor'
font-size='14px'
rounded
- :style='hueRotate !== 0 ? `filter: hue-rotate(` + hueRotate + `deg)` : ``'
+ :style='props.hueRotate !== 0 ? `filter: hue-rotate(` + props.hueRotate + `deg)` : ``'
)
q-badge(
v-if='indicatorDot'
@@ -13,57 +13,57 @@ q-item-section(avatar)
:color='indicatorDot'
floating
)
- q-tooltip(v-if='indicatorText') {{indicatorText}}
+ q-tooltip(v-if='props.indicatorText') {{props.indicatorText}}
q-icon(
v-if='!textMode'
:name='`img:/_assets/icons/ultraviolet-` + icon + `.svg`'
size='sm'
)
- span.text-uppercase(v-else) {{text}}
+ span.text-uppercase(v-else) {{props.text}}
-
diff --git a/ux/src/components/IconPickerDialog.vue b/ux/src/components/IconPickerDialog.vue
index 31acee43..6686e3bb 100644
--- a/ux/src/components/IconPickerDialog.vue
+++ b/ux/src/components/IconPickerDialog.vue
@@ -1,7 +1,7 @@
q-card.icon-picker(flat, style='width: 400px;')
q-tabs.text-primary(
- v-model='currentTab'
+ v-model='state.currentTab'
no-caps
inline-label
)
@@ -17,12 +17,12 @@ q-card.icon-picker(flat, style='width: 400px;')
)
q-separator
q-tab-panels(
- v-model='currentTab'
+ v-model='state.currentTab'
)
q-tab-panel(name='icon')
q-select(
:options='iconPacks'
- v-model='selPack'
+ v-model='state.selPack'
emit-value
map-options
outlined
@@ -52,7 +52,7 @@ q-card.icon-picker(flat, style='width: 400px;')
size='sm'
) {{scope.opt.subset.toUpperCase()}}
q-input.q-mt-md(
- v-model='selIcon'
+ v-model='state.selIcon'
outlined
label='Icon Name'
dense
@@ -96,7 +96,7 @@ q-card.icon-picker(flat, style='width: 400px;')
q-img(
transition='jump-down'
:ratio='1'
- :src='imgPath'
+ :src='state.imgPath'
)
q-separator
q-card-actions
@@ -118,67 +118,80 @@ q-card.icon-picker(flat, style='width: 400px;')
)
-