浏览代码

fix: various fixes + profile editing block

NGPixel 2 年之前
父节点
当前提交
97bcc56c65

+ 4 - 4
server/models/tree.mjs

@@ -244,10 +244,10 @@ export class Tree extends Model {
     }
 
     // Check for collision
-    const existingFolder = await WIKI.db.knex('tree').where({
+    const existingFolder = await WIKI.db.knex('tree').select('id').where({
       siteId: siteId,
       localeCode: locale,
-      folderPath: parentPath,
+      folderPath: encodeTreePath(parentPath),
       fileName: pathName
     }).first()
     if (existingFolder) {
@@ -261,7 +261,7 @@ export class Tree extends Model {
         const parentPathParts = parentPath.split('.')
         for (let i = 1; i <= parentPathParts.length; i++) {
           const ancestor = {
-            folderPath: dropRight(parentPathParts, i).join('.'),
+            folderPath: encodeTreePath(dropRight(parentPathParts, i).join('.')),
             fileName: nth(parentPathParts, i * -1)
           }
           expectedAncestors.push(ancestor)
@@ -296,7 +296,7 @@ export class Tree extends Model {
     // Create folder
     const fullPath = parentPath ? `${decodeTreePath(parentPath)}/${pathName}` : pathName
     const folder = await WIKI.db.knex('tree').insert({
-      folderPath: parentPath,
+      folderPath: encodeTreePath(parentPath),
       fileName: pathName,
       type: 'folder',
       title: title,

+ 11 - 8
server/modules/extensions/puppeteer/ext.mjs

@@ -25,16 +25,19 @@ export default {
   },
   async install () {
     try {
-      const { stdout, stderr } = await exec('node install.js', {
-        cwd: path.join(WIKI.SERVERPATH, 'node_modules/puppeteer-core'),
-        timeout: 300000,
-        windowsHide: true
-      })
-      this.isInstalled = true
-      WIKI.logger.info(stdout)
-      WIKI.logger.warn(stderr)
+      // TODO: https://pptr.dev/browsers-api/
+      // const { stdout, stderr } = await exec('node install.js', {
+      //   cwd: path.join(WIKI.SERVERPATH, 'node_modules/puppeteer-core'),
+      //   timeout: 300000,
+      //   windowsHide: true
+      // })
+      // this.isInstalled = true
+      // WIKI.logger.info(stdout)
+      // WIKI.logger.warn(stderr)
+      throw new Error('Not implemented yet.')
     } catch (err) {
       WIKI.logger.error(err)
+      throw err
     }
   }
 }

+ 0 - 1
ux/src/components/PageNewMenu.vue

@@ -111,7 +111,6 @@ const { t } = useI18n()
 // METHODS
 
 function create (editor) {
-  // window.location.assign('/_edit/new')
   pageStore.pageCreate({ editor })
 }
 

+ 10 - 0
ux/src/components/PagePropertiesDialog.vue

@@ -14,6 +14,16 @@ q-card.page-properties-dialog
   q-toolbar.bg-primary.text-white.flex
     .text-subtitle2 {{t('editor.props.pageProperties')}}
     q-space
+    q-btn.q-mr-sm(
+      dense
+      flat
+      rounded
+      color='white'
+      icon='las la-question-circle'
+      :href='siteStore.docsBase + `/editor/properties`'
+      target='_blank'
+      type='a'
+    )
     q-btn(
       icon='las la-times'
       dense

+ 1 - 1
ux/src/components/TreeBrowserDialog.vue

@@ -236,7 +236,7 @@ async function save () {
   })
 }
 
-async function treeLazyLoad (nodeId, { done, fail }) {
+async function treeLazyLoad (nodeId, isCurrent, { done, fail }) {
   await loadTree({
     parentId: nodeId,
     types: ['folder', 'page']

+ 3 - 0
ux/src/i18n/locales/en.json

@@ -1666,6 +1666,7 @@
   "profile.avatar": "Avatar",
   "profile.avatarClearFailed": "Failed to clear profile picture.",
   "profile.avatarClearSuccess": "Profile picture cleared successfully.",
+  "profile.avatarUploadDisabled": "Your avatar is set by your organization and cannot be changed.",
   "profile.avatarUploadFailed": "Failed to upload user profile picture.",
   "profile.avatarUploadHint": "For best results, use a 180x180 image of type JPG or PNG.",
   "profile.avatarUploadSuccess": "Profile picture uploaded successfully.",
@@ -1682,6 +1683,8 @@
   "profile.dateFormatHint": "Set your preferred format to display dates.",
   "profile.displayName": "Display Name",
   "profile.displayNameHint": "Your full name; shown when authoring content (e.g. pages, comments, etc.).",
+  "profile.editDisabledDescription": "Your wiki administrator has disabled profile editing.",
+  "profile.editDisabledTitle": "Profile info is managed by your organization.",
   "profile.email": "Email Address",
   "profile.emailHint": "The email address used for login.",
   "profile.groups": "Groups",

+ 9 - 3
ux/src/pages/ProfileAvatar.vue

@@ -1,7 +1,7 @@
 <template lang="pug">
 q-page.q-py-md(:style-fn='pageStyle')
   .text-header {{t('profile.avatar')}}
-  .row.q-gutter-lg.q-mt-xl.align-center
+  .row.q-gutter-lg.q-mt-xl
     .col.text-center
       q-avatar.profile-avatar-circ(
         size='180px'
@@ -17,7 +17,7 @@ q-page.q-py-md(:style-fn='pageStyle')
           v-else,
           name='las la-user'
           )
-    .col
+    .col.self-center(v-if='canEdit')
       .text-body1 {{ t('profile.avatarUploadTitle') }}
       .text-caption {{ t('profile.avatarUploadHint') }}
       .q-mt-md
@@ -37,6 +37,8 @@ q-page.q-py-md(:style-fn='pageStyle')
           @click='clearImage'
           :disable='!userStore.hasAvatar'
         )
+    .col.self-center(v-else)
+      .text-caption.text-negative {{ t('profile.avatarUploadDisabled') }}
 
   q-inner-loading(:showing='state.loading > 0')
 </template>
@@ -46,8 +48,9 @@ import gql from 'graphql-tag'
 
 import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
-import { reactive } from 'vue'
+import { computed, reactive } from 'vue'
 
+import { useSiteStore } from 'src/stores/site'
 import { useUserStore } from 'src/stores/user'
 
 // QUASAR
@@ -56,6 +59,7 @@ const $q = useQuasar()
 
 // STORES
 
+const siteStore = useSiteStore()
 const userStore = useUserStore()
 
 // I18N
@@ -75,6 +79,8 @@ const state = reactive({
   assetTimestamp: (new Date()).toISOString()
 })
 
+const canEdit = computed(() => siteStore.features?.profile)
+
 // METHODS
 
 function pageStyle (offset, height) {

+ 16 - 1
ux/src/pages/ProfileInfo.vue

@@ -1,6 +1,15 @@
 <template lang="pug">
 q-page.q-py-md(:style-fn='pageStyle')
   .text-header {{t('profile.myInfo')}}
+  q-item(v-if='!canEdit')
+    q-item-section
+      q-card.bg-negative.text-white.rounded-borders(flat)
+        q-card-section.items-center(horizontal)
+          q-card-section.col-auto.q-pr-none
+            q-icon(name='las la-ban', size='sm')
+          q-card-section
+            span {{ t('profile.editDisabledTitle') }}
+            .text-caption.text-red-1 {{ t('profile.editDisabledDescription') }}
   q-item
     blueprint-icon(icon='contact')
     q-item-section
@@ -13,6 +22,7 @@ q-page.q-py-md(:style-fn='pageStyle')
         dense
         hide-bottom-space
         :aria-label='t(`profile.displayName`)'
+        :readonly='!canEdit'
         )
   q-separator.q-my-sm(inset)
   q-item
@@ -41,6 +51,7 @@ q-page.q-py-md(:style-fn='pageStyle')
         dense
         hide-bottom-space
         :aria-label='t(`profile.location`)'
+        :readonly='!canEdit'
         )
   q-separator.q-my-sm(inset)
   q-item
@@ -55,6 +66,7 @@ q-page.q-py-md(:style-fn='pageStyle')
         dense
         hide-bottom-space
         :aria-label='t(`profile.jobTitle`)'
+        :readonly='!canEdit'
         )
   q-separator.q-my-sm(inset)
   q-item
@@ -69,6 +81,7 @@ q-page.q-py-md(:style-fn='pageStyle')
         dense
         hide-bottom-space
         :aria-label='t(`profile.pronouns`)'
+        :readonly='!canEdit'
         )
   .text-header.q-mt-lg {{t('profile.preferences')}}
   q-item
@@ -164,7 +177,7 @@ import gql from 'graphql-tag'
 
 import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
-import { onMounted, reactive } from 'vue'
+import { computed, onMounted, reactive } from 'vue'
 
 import { useSiteStore } from 'src/stores/site'
 import { useUserStore } from 'src/stores/user'
@@ -230,6 +243,8 @@ const cvdChoices = [
 ]
 const timezones = Intl.supportedValuesOf('timeZone')
 
+const canEdit = computed(() => siteStore.features?.profile)
+
 // METHODS
 
 function pageStyle (offset, height) {

+ 3 - 2
ux/src/stores/page.js

@@ -1,6 +1,6 @@
 import { defineStore } from 'pinia'
 import gql from 'graphql-tag'
-import { cloneDeep, initial, last, pick, transform } from 'lodash-es'
+import { cloneDeep, dropRight, initial, last, pick, transform } from 'lodash-es'
 import { DateTime } from 'luxon'
 
 import { useSiteStore } from './site'
@@ -291,7 +291,8 @@ export const usePageStore = defineStore('page', {
       // -> Default Page Path
       let newPath = path
       if (!path && path !== '') {
-        newPath = this.path.length < 2 ? 'new-page' : `${this.path}/new-page`
+        const parentPath = dropRight(this.path.split('/'), 1).join('/')
+        newPath = parentPath ? `${parentPath}/new-page` : 'new-page'
       }
 
       // -> Set Default Page Data

+ 2 - 0
ux/src/stores/site.js

@@ -27,6 +27,7 @@ export const useSiteStore = defineStore('site', {
     overlay: null,
     overlayOpts: {},
     features: {
+      profile: false,
       ratingsMode: 'off',
       reasonForChange: 'required',
       search: false
@@ -97,6 +98,7 @@ export const useSiteStore = defineStore('site', {
                 contentLicense
                 footerExtra
                 features {
+                  profile
                   ratingsMode
                   reasonForChange
                   search