feat: content link states
This commit is contained in:
		| @@ -71,6 +71,7 @@ | |||||||
|                 single-line, |                 single-line, | ||||||
|                 solo |                 solo | ||||||
|                 flat |                 flat | ||||||
|  |                 rounded | ||||||
|                 hide-details, |                 hide-details, | ||||||
|                 prepend-inner-icon='mdi-magnify', |                 prepend-inner-icon='mdi-magnify', | ||||||
|                 :loading='searchIsLoading', |                 :loading='searchIsLoading', | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ | |||||||
|           .overline.mr-3.animated.fadeInLeft Current Selection |           .overline.mr-3.animated.fadeInLeft Current Selection | ||||||
|           v-chip.mr-3.primary--text( |           v-chip.mr-3.primary--text( | ||||||
|             v-for='tag of tagsSelected' |             v-for='tag of tagsSelected' | ||||||
|  |             :key='`tagSelected-` + tag.tag' | ||||||
|             color='white' |             color='white' | ||||||
|             close |             close | ||||||
|             @click:close='toggleTag(tag.tag)' |             @click:close='toggleTag(tag.tag)' | ||||||
| @@ -38,7 +39,7 @@ | |||||||
|         template(v-else) |         template(v-else) | ||||||
|           v-icon.mr-3.animated.fadeInRight mdi-arrow-left |           v-icon.mr-3.animated.fadeInRight mdi-arrow-left | ||||||
|           .overline.animated.fadeInRight Select one or more tags |           .overline.animated.fadeInRight Select one or more tags | ||||||
|       v-toolbar(color='grey lighten-4', flat, height='58') |       v-toolbar(:color='$vuetify.theme.dark ? `grey darken-4-l5` : `grey lighten-4`', flat, height='58') | ||||||
|         v-text-field.tags-search( |         v-text-field.tags-search( | ||||||
|           label='Search within results...' |           label='Search within results...' | ||||||
|           solo |           solo | ||||||
| @@ -50,12 +51,29 @@ | |||||||
|           prepend-icon='mdi-file-document-box-search-outline' |           prepend-icon='mdi-file-document-box-search-outline' | ||||||
|           append-icon='mdi-arrow-right' |           append-icon='mdi-arrow-right' | ||||||
|         ) |         ) | ||||||
|  |         template(v-if='locales.length > 1') | ||||||
|  |           v-divider.mx-3(vertical) | ||||||
|  |           .overline Locale | ||||||
|  |           v-select.ml-2( | ||||||
|  |             :items='locales' | ||||||
|  |             v-model='locale' | ||||||
|  |             :background-color='$vuetify.theme.dark ? `grey darken-3` : `white`' | ||||||
|  |             hide-details | ||||||
|  |             label='Locale' | ||||||
|  |             item-text='name' | ||||||
|  |             item-value='code' | ||||||
|  |             rounded | ||||||
|  |             single-line | ||||||
|  |             dense | ||||||
|  |             height='40' | ||||||
|  |             style='max-width: 170px;' | ||||||
|  |           ) | ||||||
|         v-divider.mx-3(vertical) |         v-divider.mx-3(vertical) | ||||||
|         .overline Order By |         .overline Order By | ||||||
|         v-select.ml-2( |         v-select.ml-2( | ||||||
|           :items='orderByItems' |           :items='orderByItems' | ||||||
|           v-model='orderBy' |           v-model='orderBy' | ||||||
|           background-color='white' |           :background-color='$vuetify.theme.dark ? `grey darken-3` : `white`' | ||||||
|           hide-details |           hide-details | ||||||
|           label='Order By' |           label='Order By' | ||||||
|           rounded |           rounded | ||||||
| @@ -64,12 +82,13 @@ | |||||||
|           height='40' |           height='40' | ||||||
|           style='max-width: 250px;' |           style='max-width: 250px;' | ||||||
|         ) |         ) | ||||||
|         v-divider.mx-3(vertical) |         v-btn-toggle.ml-2(v-model='orderByDirection', rounded, mandatory) | ||||||
|         v-btn-toggle(v-model='displayStyle', rounded, mandatory) |           v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-up | ||||||
|           v-btn(text, height='40'): v-icon(small) mdi-view-list |           v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-down | ||||||
|           v-btn(text, height='40'): v-icon(small) mdi-cards-variant |  | ||||||
|           v-btn(text, height='40'): v-icon(small) mdi-format-align-justify |  | ||||||
|       v-divider |       v-divider | ||||||
|  |       .text-center.pt-10 | ||||||
|  |         img(src='/svg/icon-price-tag.svg') | ||||||
|  |         .subtitle-2.grey--text Select one or more tags on the left. | ||||||
|     nav-footer |     nav-footer | ||||||
|     notify |     notify | ||||||
|     search-results |     search-results | ||||||
| @@ -77,16 +96,25 @@ | |||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import { get } from 'vuex-pathify' | import { get } from 'vuex-pathify' | ||||||
|  | import VueRouter from 'vue-router' | ||||||
| import _ from 'lodash' | import _ from 'lodash' | ||||||
|  |  | ||||||
| import tagsQuery from 'gql/common/common-pages-query-tags.gql' | import tagsQuery from 'gql/common/common-pages-query-tags.gql' | ||||||
|  |  | ||||||
|  | /* global siteLangs */ | ||||||
|  |  | ||||||
|  | const router = new VueRouter({ | ||||||
|  |   mode: 'history', | ||||||
|  |   base: '/t' | ||||||
|  | }) | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       tags: [], |       tags: [], | ||||||
|       selection: [], |       selection: [], | ||||||
|       displayStyle: 0, |       locale: 'any', | ||||||
|  |       locales: [], | ||||||
|       orderBy: 'TITLE', |       orderBy: 'TITLE', | ||||||
|       orderByItems: [ |       orderByItems: [ | ||||||
|         { text: 'Creation Date', value: 'CREATED' }, |         { text: 'Creation Date', value: 'CREATED' }, | ||||||
| @@ -95,6 +123,7 @@ export default { | |||||||
|         { text: 'Path', value: 'PATH' }, |         { text: 'Path', value: 'PATH' }, | ||||||
|         { text: 'Title', value: 'TITLE' } |         { text: 'Title', value: 'TITLE' } | ||||||
|       ], |       ], | ||||||
|  |       orderByDirection: 0, | ||||||
|       scrollStyle: { |       scrollStyle: { | ||||||
|         vuescroll: {}, |         vuescroll: {}, | ||||||
|         scrollPanel: { |         scrollPanel: { | ||||||
| @@ -127,8 +156,27 @@ export default { | |||||||
|       return _.filter(this.tags, t => _.includes(this.selection, t.tag)) |       return _.filter(this.tags, t => _.includes(this.selection, t.tag)) | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|  |   watch: { | ||||||
|  |     locale (newValue, oldValue) { | ||||||
|  |       this.rebuildURL() | ||||||
|  |     }, | ||||||
|  |     orderBy (newValue, oldValue) { | ||||||
|  |       this.rebuildURL() | ||||||
|  |     }, | ||||||
|  |     orderByDirection (newValue, oldValue) { | ||||||
|  |       this.rebuildURL() | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   router, | ||||||
|   created () { |   created () { | ||||||
|     this.$store.commit('page/SET_MODE', 'tags') |     this.$store.commit('page/SET_MODE', 'tags') | ||||||
|  |  | ||||||
|  |     this.locales = _.concat( | ||||||
|  |       [{name: 'Any', code: 'any'}], | ||||||
|  |       (siteLangs.length > 0 ? siteLangs : []) | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     this.selection = _.compact(this.$route.path.split('/')) | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     toggleTag (tag) { |     toggleTag (tag) { | ||||||
| @@ -137,9 +185,25 @@ export default { | |||||||
|       } else { |       } else { | ||||||
|         this.selection.push(tag) |         this.selection.push(tag) | ||||||
|       } |       } | ||||||
|  |       this.rebuildURL() | ||||||
|     }, |     }, | ||||||
|     isSelected (tag) { |     isSelected (tag) { | ||||||
|       return _.includes(this.selection, tag) |       return _.includes(this.selection, tag) | ||||||
|  |     }, | ||||||
|  |     rebuildURL () { | ||||||
|  |       let urlObj = { | ||||||
|  |         path: '/' + this.selection.join('/') | ||||||
|  |       } | ||||||
|  |       if (this.locale !== `any`) { | ||||||
|  |         _.set(urlObj, 'query.lang', this.locale) | ||||||
|  |       } | ||||||
|  |       if (this.orderBy !== `TITLE`) { | ||||||
|  |         _.set(urlObj, 'query.sort', this.orderBy.toLowerCase()) | ||||||
|  |       } | ||||||
|  |       if (this.orderByDirection !== 0) { | ||||||
|  |         _.set(urlObj, 'query.dir', this.orderByDirection === 0 ? `asc` : `desc`) | ||||||
|  |       } | ||||||
|  |       this.$router.push(urlObj) | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   apollo: { |   apollo: { | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								client/static/svg/icon-price-tag.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								client/static/svg/icon-price-tag.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="256" height="256"><path fill="#f7f7fb" d="M43.5,28L16.9,45.4c-5,2.3-2.3,9.1,0,14.1l17.9,17.9l62.2,23l17-47L43.5,28 M35.3,59.5 c-3.9-1.4-5.9-5.7-4.5-9.6c1.4-3.9,5.7-5.9,9.6-4.5s5.9,5.7,4.5,9.6C43.5,58.9,39.2,61,35.3,59.5"/><path fill="#dfdfe3" d="M30.353,52.448c0-0.846,0.144-1.706,0.446-2.548c1.098-3.057,3.977-4.946,7.048-4.946 c0.847,0,1.709,0.144,2.552,0.446l0,0c3.065,1.1,4.957,3.993,4.946,7.074C45.349,53.317,45.206,54.17,44.9,55l0,0 c-1.091,3.04-3.945,4.986-6.997,4.986c-0.864,0-1.743-0.156-2.603-0.486C32.242,58.402,30.352,55.521,30.353,52.448 M43.5,28 L16.9,45.4c-2.078,0.956-2.826,2.689-2.825,4.746c0,2.894,1.481,6.431,2.825,9.354l17.9,17.9l21.635,8H102.4l11.6-32L43.5,28"/><path fill="#454b54" d="M97,103.5c-0.4,0-0.7-0.1-1-0.2l-62.2-23c-0.4-0.2-0.8-0.4-1.1-0.7L14.8,61.7 c-0.6-0.6-0.9-1.3-0.9-2.1V45.4c0-1,0.5-2,1.4-2.5l26.6-17.5c0.8-0.5,1.8-0.6,2.7-0.3l70.5,25.5c0.7,0.3,1.4,0.8,1.7,1.5 s0.4,1.5,0.1,2.3l-17,47c-0.3,0.8-0.8,1.4-1.6,1.7C97.9,103.4,97.4,103.5,97,103.5z M36.5,74.9l58.7,21.7l15-41.4l-66.3-24L19.9,47 v11.3L36.5,74.9z"/><path fill="#6ec7b0" d="M34.8,27.5L16.9,45.4c-3.9,3.9-3.9,10.2,0,14.1l17.9,17.9h75v-50h-75V27.5z M37.8,60 c-4.1,0-7.5-3.4-7.5-7.5s3.4-7.5,7.5-7.5s7.5,3.4,7.5,7.5S42,60,37.8,60z"/><path fill="#454b54" d="M109.8,80.5h-75c-0.8,0-1.6-0.3-2.1-0.9L14.8,61.7C12.3,59.2,11,56,11,52.5s1.4-6.7,3.8-9.2 l17.9-17.9c0.6-0.6,1.3-0.9,2.1-0.9h75c1.7,0,3,1.3,3,3v50C112.8,79.2,111.5,80.5,109.8,80.5z M36.1,74.5h70.8v-44H36.1L19,47.5 c-1.3,1.3-2,3.1-2,5s0.7,3.6,2,4.9L36.1,74.5z"/><path fill="#fff" d="M89.8 50.5h-25c-1.7 0-3-1.3-3-3s1.3-3 3-3h25c1.7 0 3 1.3 3 3S91.5 50.5 89.8 50.5zM89.8 60.5h-25c-1.7 0-3-1.3-3-3s1.3-3 3-3h25c1.7 0 3 1.3 3 3S91.5 60.5 89.8 60.5z"/><path fill="#454b54" d="M7.8,85.5c-0.8,0-1.5-0.3-2.1-0.9c-1.2-1.2-1.2-3.1,0-4.2l9.1-9.1c1.2-1.2,3.1-1.2,4.2,0 c1.2,1.2,1.2,3.1,0,4.2l-9.1,9.1C9.4,85.2,8.6,85.5,7.8,85.5z"/></svg> | ||||||
| After Width: | Height: | Size: 1.9 KiB | 
| @@ -10,11 +10,40 @@ | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   @at-root .theme--dark & { |   @at-root .theme--dark & { | ||||||
|     // background-color: darken(mc('grey', '900'), 4%); |  | ||||||
|     color: mc('grey', '300'); |     color: mc('grey', '300'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|     a { |   // --------------------------------- | ||||||
|       color: mc('blue', '100'); |   // LINKS | ||||||
|  |   // --------------------------------- | ||||||
|  |  | ||||||
|  |   a { | ||||||
|  |     color: mc('blue', '700'); | ||||||
|  |  | ||||||
|  |     &.is-internal-link.is-invalid-page { | ||||||
|  |       color: mc('red', '700'); | ||||||
|  |  | ||||||
|  |       @at-root .theme--dark & { | ||||||
|  |         color: mc('red', '200'); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     &.is-external-link { | ||||||
|  |       padding-right: 3px; | ||||||
|  |  | ||||||
|  |       &::after { | ||||||
|  |         font-family: 'Material Design Icons'; | ||||||
|  |         font-size: 24px/1; | ||||||
|  |         padding-left: 3px; | ||||||
|  |         display: inline-block; | ||||||
|  |         content: '\F3CC'; | ||||||
|  |         color: mc('grey', '500'); | ||||||
|  |         text-decoration: none; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @at-root .theme--dark & { | ||||||
|  |       color: mc('blue', '200'); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ module.exports = { | |||||||
|         name: _.get(c, 'name', 'Anonymous') || '', |         name: _.get(c, 'name', 'Anonymous') || '', | ||||||
|         profile: _.get(c, 'profile', ''), |         profile: _.get(c, 'profile', ''), | ||||||
|         tier: _.toLower(_.get(c, 'tier', 'backers')), |         tier: _.toLower(_.get(c, 'tier', 'backers')), | ||||||
|         totalDonated: _.get(c, 'totalAmountDonated', 0), |         totalDonated: Math.ceil(_.get(c, 'totalAmountDonated', 0)), | ||||||
|         twitter: _.get(c, 'twitter', '') || '', |         twitter: _.get(c, 'twitter', '') || '', | ||||||
|         website: _.get(c, 'website', '') || '' |         website: _.get(c, 'website', '') || '' | ||||||
|       })) |       })) | ||||||
|   | |||||||
| @@ -8,6 +8,8 @@ module.exports = async (pageId) => { | |||||||
|  |  | ||||||
|   try { |   try { | ||||||
|     WIKI.models = require('../core/db').init() |     WIKI.models = require('../core/db').init() | ||||||
|  |     await WIKI.configSvc.loadFromDb() | ||||||
|  |     await WIKI.configSvc.applyFlags() | ||||||
|  |  | ||||||
|     const page = await WIKI.models.pages.getPageFromDb(pageId) |     const page = await WIKI.models.pages.getPageFromDb(pageId) | ||||||
|     if (!page) { |     if (!page) { | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| const _ = require('lodash') | const _ = require('lodash') | ||||||
| const cheerio = require('cheerio') | const cheerio = require('cheerio') | ||||||
|  |  | ||||||
|  | /* global WIKI */ | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|   async render() { |   async render() { | ||||||
|     const $ = cheerio.load(this.input) |     const $ = cheerio.load(this.input) | ||||||
| @@ -14,6 +16,98 @@ module.exports = { | |||||||
|       renderer.init($, child.config) |       renderer.init($, child.config) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // -------------------------------- | ||||||
|  |     // Detect internal / external links | ||||||
|  |     // -------------------------------- | ||||||
|  |  | ||||||
|  |     let internalRefs = [] | ||||||
|  |     const reservedPrefixes = /^\/[a-z]\//gi | ||||||
|  |  | ||||||
|  |     const isHostSet = WIKI.config.host.length > 7 && WIKI.config.host !== 'http://' | ||||||
|  |     if (!isHostSet) { | ||||||
|  |       WIKI.logger.warn('Host is not set. You must set the Site Host under General in the Administration Area!') | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $('a').each((i, elm) => { | ||||||
|  |       let href = $(elm).attr('href') | ||||||
|  |  | ||||||
|  |       // -> Ignore empty links | ||||||
|  |       if (!href || href.length < 1) { | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // -> Strip host from local links | ||||||
|  |       if (isHostSet && href.indexOf(WIKI.config.site.host) === 0) { | ||||||
|  |         href = href.replace(WIKI.config.site.host, '') | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // -> Assign local / external tag | ||||||
|  |       if (href.indexOf('://') < 0) { | ||||||
|  |         // -> Remove trailing slash | ||||||
|  |         if (_.endsWith('/')) { | ||||||
|  |           href = href.slice(0, -1) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // -> Check for system prefix | ||||||
|  |         if (!reservedPrefixes.test(href)) { | ||||||
|  |           $(elm).addClass(`is-internal-link`) | ||||||
|  |  | ||||||
|  |           // -> Reformat paths | ||||||
|  |           if (href.indexOf('/') !== 0) { | ||||||
|  |             href = `/${this.page.localeCode}/${this.page.path}/${href}` | ||||||
|  |           } else if (href.charAt(3) !== '/') { | ||||||
|  |             href = `/${this.page.localeCode}${href}` | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           // -> Save internal references | ||||||
|  |           internalRefs.push({ | ||||||
|  |             localeCode: href.substring(1, 3), | ||||||
|  |             path: _.head(href.substring(4).split('#')) | ||||||
|  |           }) | ||||||
|  |         } else { | ||||||
|  |           $(elm).addClass(`is-system-link`) | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         $(elm).addClass(`is-external-link`) | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // -> Update element | ||||||
|  |       $(elm).attr('href', href) | ||||||
|  |     }) | ||||||
|  |  | ||||||
|  |     // -------------------------------- | ||||||
|  |     // Detect internal link states | ||||||
|  |     // -------------------------------- | ||||||
|  |  | ||||||
|  |     if (internalRefs.length > 0) { | ||||||
|  |       // -> Find matching pages | ||||||
|  |       const results = await WIKI.models.pages.query().column('path', 'localeCode').where(builder => { | ||||||
|  |         internalRefs.forEach((ref, idx) => { | ||||||
|  |           if (idx < 1) { | ||||||
|  |             builder.where(ref) | ||||||
|  |           } else { | ||||||
|  |             builder.orWhere(ref) | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |  | ||||||
|  |       // -> Apply tag to internal links for found pages | ||||||
|  |       $('a.is-internal-link').each((i, elm) => { | ||||||
|  |         const href = $(elm).attr('href') | ||||||
|  |         const hrefObj = { | ||||||
|  |           localeCode: href.substring(1, 3), | ||||||
|  |           path: _.head(href.substring(4).split('#')) | ||||||
|  |         } | ||||||
|  |         if (_.some(results, r => { | ||||||
|  |           return r.localeCode === hrefObj.localeCode && r.path === hrefObj.path | ||||||
|  |         })) { | ||||||
|  |           $(elm).addClass(`is-valid-page`) | ||||||
|  |         } else { | ||||||
|  |           $(elm).addClass(`is-invalid-page`) | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return $.html('body').replace('<body>', '').replace('</body>', '') |     return $.html('body').replace('<body>', '').replace('</body>', '') | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user