Merge branch 'feature/add_settings_search' into 'develop'

Implement settings search

Closes #35

See merge request pleroma/admin-fe!102
This commit is contained in:
Angelina Filippova 2020-03-21 18:18:53 +00:00
commit 343f9506f2
31 changed files with 656 additions and 124 deletions

View file

@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Ability to see local statuses in Statuses by instance section
- Ability to configure Oban.Cron settings and settings for notifications streamer
- Settings search
### Fixed

View file

@ -267,3 +267,25 @@ const wrapValues = (settings, currentState) => {
}
})
}
export const formSearchObject = description => {
const parseNestedSettings = (description, label, key) => description.reduce((acc, setting) => {
const searchArray = _.compact([setting.key, setting.label, setting.description]).map(el => el.toLowerCase())
const resultObject = { label: setting.label, key: setting.key || setting.group, groupKey: key, groupLabel: label, search: searchArray }
if (setting.children) {
const updatedAcc = [...acc, resultObject]
return [...updatedAcc, ...parseNestedSettings(setting.children, label, key)]
}
return [...acc, resultObject]
}, [])
return description.reduce((acc, setting) => {
const searchArray = _.compact([setting.key, setting.label, setting.description]).map(el => el.toLowerCase())
const resultObject = { label: setting.label, key: setting.key || setting.group, groupKey: setting.key || setting.group, groupLabel: setting.label, search: searchArray }
if (setting.children) {
const updatedAcc = !setting.key && setting.group === ':pleroma' ? acc : [...acc, resultObject]
return [...updatedAcc, ...parseNestedSettings(setting.children, setting.label, setting.key || setting.group)]
}
return !setting.key && setting.group === ':pleroma' ? acc : [...acc, resultObject]
}, [])
}

View file

@ -1,5 +1,5 @@
import { fetchDescription, fetchSettings, removeSettings, restartApp, updateSettings } from '@/api/settings'
import { checkPartialUpdate, parseNonTuples, parseTuples, valueHasTuples, wrapUpdatedSettings } from './normalizers'
import { checkPartialUpdate, formSearchObject, parseNonTuples, parseTuples, valueHasTuples, wrapUpdatedSettings } from './normalizers'
import _ from 'lodash'
const settings = {
@ -10,6 +10,7 @@ const settings = {
description: [],
loading: true,
needReboot: false,
searchData: {},
settings: {},
updatedSettings: {}
},
@ -32,6 +33,9 @@ const settings = {
SET_LOADING: (state, status) => {
state.loading = status
},
SET_SEARCH: (state, searchObject) => {
state.searchData = searchObject
},
SET_SETTINGS: (state, data) => {
const newSettings = data.reduce((acc, { group, key, value }) => {
const parsedValue = valueHasTuples(key, value)
@ -77,6 +81,8 @@ const settings = {
const response = await fetchSettings(getters.authHost, getters.token)
const description = await fetchDescription(getters.authHost, getters.token)
commit('SET_DESCRIPTION', description.data)
const searchObject = formSearchObject(description.data)
commit('SET_SEARCH', searchObject)
commit('SET_SETTINGS', response.data.configs)
commit('TOGGLE_REBOOT', response.data.need_reboot)
} catch (_e) {

View file

@ -39,7 +39,7 @@
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="closeDialogWindow">{{ $t('invites.cancel') }}</el-button>
<el-button class="invites-close-dialog" @click="closeDialogWindow">{{ $t('invites.cancel') }}</el-button>
<el-button type="primary" @click="createToken">{{ $t('invites.create') }}</el-button>
</span>
<el-card v-if="'token' in newToken">

View file

@ -1,10 +1,10 @@
<template>
<div v-if="!loading" class="form-container">
<el-form ref="activitypubData" :model="activitypubData" :label-width="labelWidth">
<el-form ref="activitypubData" :model="activitypubData" :label-width="labelWidth" data-search=":activitypub">
<setting :setting-group="activitypub" :data="activitypubData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-form ref="userData" :model="userData" :label-width="labelWidth">
<el-form ref="userData" :model="userData" :label-width="labelWidth" data-search=":user">
<setting :setting-group="user" :data="userData"/>
</el-form>
<div class="submit-button-container">

View file

@ -15,7 +15,7 @@
</el-form>
<el-divider class="divider thick-line"/>
<el-form ref="emojiData" :model="emojiData" :label-width="labelWidth">
<el-form-item class="grouped-settings-header">
<el-form-item data-search=":emoji" class="grouped-settings-header">
<span class="label-font">{{ $t('settings.emoji') }}</span>
</el-form-item>
<setting :setting-group="emoji" :data="emojiData"/>
@ -26,7 +26,7 @@
</el-form>
<el-divider class="divider thick-line"/>
<el-form ref="markupData" :model="markupData" :label-width="labelWidth">
<el-form-item class="grouped-settings-header">
<el-form-item data-search=":markup" class="grouped-settings-header">
<span class="label-font">{{ $t('settings.markup') }}</span>
</el-form-item>
<setting :setting-group="markup" :data="markupData"/>

View file

@ -4,7 +4,7 @@
<setting :setting-group="http" :data="httpData"/>
</el-form>
<el-form ref="corsPlugData" :model="corsPlugData" :label-width="labelWidth">
<el-form-item class="grouped-settings-header">
<el-form-item data-search=":cors_plug" class="grouped-settings-header">
<span class="label-font">{{ $t('settings.corsPlug') }}</span>
</el-form-item>
<setting :setting-group="corsPlug" :data="corsPlugData"/>

View file

@ -1,7 +1,11 @@
<template>
<div class="input-container">
<div v-if="setting.type === 'keyword'" class="keyword-container">
<el-form-item :label-width="customLabelWidth" :class="labelClass" :style="`margin-left:${margin}px;margin-bottom:0`" >
<el-form-item
:label-width="customLabelWidth"
:class="labelClass"
:style="`margin-left:${margin}px;margin-bottom:0`"
:data-search="setting.key || setting.group">
<span slot="label">
{{ setting.label }}
<el-tooltip v-if="canBeDeleted && isDesktop" :content="$t('settings.removeFromDB')" placement="bottom-end">
@ -33,11 +37,13 @@
v-if="setting.type === 'string' || (setting.type.includes('string') && setting.type.includes('atom'))"
:value="inputValue"
:placeholder="setting.suggestions ? setting.suggestions[0] : null"
:data-search="setting.key || setting.group"
class="input"
@input="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)"/>
<el-switch
v-if="setting.type === 'boolean'"
:value="inputValue"
:data-search="setting.key || setting.group"
class="switch-input"
@change="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)"/>
<el-input-number
@ -46,10 +52,12 @@
:placeholder="setting.suggestions ? setting.suggestions[0].toString() : null"
:min="0"
:size="isDesktop ? 'large' : 'medium'"
:data-search="setting.key || setting.group"
@change="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)"/>
<el-select
v-if="setting.type === 'module' || (setting.type.includes('atom') && setting.type.includes('dropdown'))"
:value="inputValue === false ? 'false' : inputValue"
:data-search="setting.key || setting.group"
clearable
class="input"
@change="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)">
@ -61,6 +69,7 @@
<el-select
v-if="renderMultipleSelect(setting.type)"
:value="setting.key === ':rewrite_policy' ? rewritePolicyValue : inputValue"
:data-search="setting.key || setting.group"
multiple
filterable
allow-create
@ -71,6 +80,7 @@
<el-input
v-if="setting.key === ':ip'"
:value="inputValue"
:data-search="setting.key || setting.group"
placeholder="xxx.xxx.xxx.xx"
class="input"
@input="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)"/>
@ -78,6 +88,7 @@
v-if="setting.type === 'atom'"
:value="inputValue"
:placeholder="setting.suggestions[0] ? setting.suggestions[0].substr(1) : ''"
:data-search="setting.key || setting.group"
class="input"
@input="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)">
<template slot="prepend">:</template>

View file

@ -20,7 +20,7 @@
<setting :setting-group="manifest" :data="manifestData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-form ref="pleromaUser" :model="pleromaUserData" :label-width="labelWidth">
<el-form ref="pleromaUser" :model="pleromaUserData" :label-width="labelWidth" data-search="Pleroma.User">
<setting :setting-group="pleromaUser" :data="pleromaUserData"/>
</el-form>
<el-divider class="divider thick-line"/>

View file

@ -11,9 +11,12 @@
<el-form ref="emailNotifications" :model="emailNotificationsData" :label-width="labelWidth">
<setting :setting-group="emailNotifications" :data="emailNotificationsData"/>
</el-form>
<el-form ref="userEmail" :model="userEmail" :label-width="labelWidth">
<el-form ref="userEmail" :model="userEmailData" :label-width="labelWidth">
<setting :setting-group="userEmail" :data="userEmailData"/>
</el-form>
<el-form ref="newUsersDigestEmail" :model="newUsersDigestEmailData" :label-width="labelWidth">
<setting :setting-group="newUsersDigestEmail" :data="newUsersDigestEmailData"/>
</el-form>
<div class="submit-button-container">
<el-button class="submit-button" type="primary" @click="onSubmit">Submit</el-button>
</div>
@ -65,6 +68,12 @@ export default {
mailerData() {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Emails.Mailer']) || {}
},
newUsersDigestEmail() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Emails.NewUsersDigestEmail')
},
newUsersDigestEmailData() {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Emails.NewUsersDigestEmail']) || {}
},
swoosh() {
return this.settings.description.find(setting => setting.group === ':swoosh')
},

View file

@ -1,6 +1,6 @@
<template>
<div v-if="!loading">
<el-form-item v-if="settingGroup.description" class="description-container">
<el-form-item v-if="settingGroup.description" :data-search="settingGroup.key || settingGroup.group" class="description-container">
<span class="description" v-html="getFormattedDescription(settingGroup.description)"/>
</el-form-item>
<div v-if="settingGroup.key === 'Pleroma.Emails.Mailer'">
@ -39,7 +39,7 @@
</div>
<div v-else>
<div class="input-container">
<el-form-item class="grouped-settings-header">
<el-form-item :data-search="setting.key || setting.group" class="grouped-settings-header">
<span slot="label">
<el-tooltip v-if="isDesktop && canBeDeleted(setting.key)" :content="$t('settings.removeFromDB')" placement="bottom-end">
<el-button icon="el-icon-delete" circle size="mini" style="margin-left:5px" @click="removeSetting(setting.key)"/>

View file

@ -1,10 +1,10 @@
<template>
<div>
<div v-if="setting.key === ':class' || setting.key === ':rel'">
<div v-if="setting.key === ':class' || setting.key === ':rel'" :data-search="setting.key || setting.group">
<el-switch :value="autoLinkerBooleanValue(setting.key)" @change="processTwoTypeValue($event, setting.key)"/>
<el-input v-if="autoLinkerBooleanValue(setting.key)" :value="autoLinkerStringValue(setting.key)" @input="processTwoTypeValue($event, setting.key)"/>
</div>
<div v-if="setting.key === ':truncate'">
<div v-if="setting.key === ':truncate'" :data-search="setting.key || setting.group">
<el-switch :value="autoLinkerBooleanValue(setting.key)" @change="processTwoTypeValue($event, setting.key)"/>
<el-input-number v-if="autoLinkerBooleanValue(setting.key)" :value="autoLinkerIntegerValue(setting.key)" @input="processTwoTypeValue($event, setting.key)"/>
</div>

View file

@ -1,6 +1,6 @@
<template>
<el-form :label-width="labelWidth" :label-position="isMobile ? 'top' : 'right'" class="crontab">
<el-form-item v-for="worker in workers" :key="worker" :label="worker" class="crontab-container">
<el-form-item v-for="worker in workers" :key="worker" :label="worker" :data-search="setting.key" class="crontab-container">
<el-input
:value="data[worker]"
:placeholder="getSuggestion(worker) || null"

View file

@ -1,6 +1,6 @@
<template>
<div class="editable-keyword-container">
<div v-if="setting.key === ':replace'">
<div v-if="setting.key === ':replace'" :data-search="setting.key || setting.group">
<div v-for="element in data" :key="getId(element)" class="setting-input">
<el-input :value="getKey(element)" placeholder="pattern" class="name-input" @input="parseEditableKeyword($event, 'key', element)"/> :
<el-input :value="getValue(element)" placeholder="replacement" class="value-input" @input="parseEditableKeyword($event, 'value', element)"/>
@ -8,7 +8,7 @@
</div>
<el-button :size="isDesktop ? 'medium' : 'mini'" icon="el-icon-plus" circle @click="addRowToEditableKeyword"/>
</div>
<div v-else-if="editableKeywordWithInteger">
<div v-else-if="editableKeywordWithInteger" :data-search="setting.key || setting.group">
<div v-for="element in data" :key="getId(element)" class="setting-input">
<el-input :value="getKey(element)" placeholder="key" class="name-input" @input="parseEditableKeyword($event, 'key', element)"/> :
<el-input-number :value="getValue(element)" :min="0" size="large" class="value-input" @change="parseEditableKeyword($event, 'value', element)"/>
@ -16,7 +16,7 @@
</div>
<el-button :size="isDesktop ? 'medium' : 'mini'" icon="el-icon-plus" circle @click="addRowToEditableKeyword"/>
</div>
<div v-else>
<div v-else :data-search="setting.key || setting.group">
<div v-for="element in data" :key="getId(element)" class="setting-input">
<el-input :value="getKey(element)" placeholder="key" class="name-input" @input="parseEditableKeyword($event, 'key', element)"/> :
<el-select :value="getValue(element)" multiple filterable allow-create class="value-input" @change="parseEditableKeyword($event, 'value', element)"/>

View file

@ -1,5 +1,5 @@
<template>
<div class="mascot-container">
<div :data-search="setting.key || setting.group" class="mascot-container">
<div v-for="(icon, index) in data" :key="index" class="mascot">
<div class="icons-container">
<div class="icon-container">

View file

@ -1,5 +1,5 @@
<template>
<div class="mascot-container">
<div :data-search="setting.key || setting.group" class="mascot-container">
<div v-for="mascot in data" :key="getId(mascot)" class="mascot">
<el-form-item label="Name" label-width="85px" class="mascot-form-item">
<div class="mascot-name-container">

View file

@ -3,6 +3,7 @@
<el-select
v-if="setting.key === ':backends'"
:value="data.value"
:data-search="setting.key || setting.group"
multiple
filterable
allow-create
@ -15,6 +16,7 @@
<el-select
v-if="setting.key === ':args'"
:value="data[setting.key]"
:data-search="setting.key || setting.group"
multiple
filterable
allow-create

View file

@ -1,5 +1,5 @@
<template>
<div class="proxy-url-input">
<div :data-search="setting.key || setting.group" class="proxy-url-input">
<el-input
:value="proxyUrlData.host"
placeholder="host (e.g. localhost or 127.0.0.1)"

View file

@ -1,5 +1,5 @@
<template>
<div>
<div :data-search="setting.key || setting.group">
<el-radio-group v-model="prune" class="prune-options">
<el-radio label=":disabled">Disabled</el-radio>
<el-radio label=":maxlen">Limit-based</el-radio>

View file

@ -1,5 +1,5 @@
<template>
<div class="rate-limit-container">
<div :data-search="setting.key || setting.group" class="rate-limit-container">
<div v-if="!rateLimitAuthUsers">
<el-input
:value="rateLimitAllUsers[0]"

View file

@ -0,0 +1,82 @@
export const tabs = {
'activity-pub': {
label: 'settings.activityPub',
settings: [':activitypub', ':user']
},
'authentication': {
label: 'settings.auth',
settings: [':auth', ':ldap', ':oauth2', 'Pleroma.Web.Auth.Authenticator']
},
'auto-linker': {
label: 'settings.autoLinker',
settings: [':opts']
},
'esshd': {
label: 'settings.esshd',
settings: [':esshd']
},
'captcha': {
label: 'settings.captcha',
settings: ['Pleroma.Captcha', 'Pleroma.Captcha.Kocaptcha']
},
'frontend': {
label: 'settings.frontend',
settings: [':assets', ':chat', ':emoji', ':frontend_configurations', ':markup', ':static_fe']
},
'gopher': {
label: 'settings.gopher',
settings: [':gopher']
},
'http': {
label: 'settings.http',
settings: [':cors_plug', ':http', ':http_security', ':http_signatures', ':web_cache_ttl']
},
'instance': {
label: 'settings.instance',
settings: [':admin_token', ':fetch_initial_posts', ':instance', ':manifest', 'Pleroma.User', 'Pleroma.ScheduledActivity', ':uri_schemes', ':feed', ':streamer']
},
'job-queue': {
label: 'settings.jobQueue',
settings: ['Pleroma.ActivityExpiration', 'Oban', ':workers']
},
'logger': {
label: 'settings.logger',
settings: [':console', ':ex_syslogger', ':quack', ':logger']
},
'mailer': {
label: 'settings.mailer',
settings: [':email_notifications', 'Pleroma.Emails.Mailer', 'Pleroma.Emails.UserEmail', ':swoosh', 'Pleroma.Emails.NewUsersDigestEmail']
},
'media-proxy': {
label: 'settings.mediaProxy',
settings: [':media_proxy']
},
'metadata': {
label: 'settings.metadata',
settings: ['Pleroma.Web.Metadata', ':rich_media']
},
'mrf': {
label: 'settings.mrf',
settings: [':mrf_simple', ':mrf_rejectnonpublic', ':mrf_hellthread', ':mrf_keyword', ':mrf_subchain', ':mrf_mention', ':mrf_normalize_markup', ':mrf_vocabulary', ':mrf_object_age', ':modules']
},
'rate-limiters': {
label: 'settings.rateLimiters',
settings: [':rate_limit']
},
'relays': {
label: 'settings.relays',
settings: []
},
'web-push': {
label: 'settings.webPush',
settings: [':vapid_details']
},
'upload': {
label: 'settings.upload',
settings: ['Pleroma.Upload.Filter.AnonymizeFilename', 'Pleroma.Upload.Filter.Mogrify', 'Pleroma.Uploaders.S3', 'Pleroma.Uploaders.Local', 'Pleroma.Upload']
},
'other': {
label: 'settings.other',
settings: [':mime', 'Pleroma.Plugs.RemoteIp']
}
}

View file

@ -23,68 +23,26 @@
</span>
</el-button>
</el-link>
<el-autocomplete
v-model="searchQuery"
:fetch-suggestions="querySearch"
:trigger-on-focus="false"
clearable
placeholder="Search"
prefix-icon="el-icon-search"
class="settings-search-input"
@select="handleSearchSelect"/>
</div>
</div>
<el-tabs v-model="activeTab" tab-position="left">
<el-tab-pane :label="$t('settings.activityPub')" :disabled="configDisabled" name="activityPub" lazy>
<activity-pub/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.auth')" :disabled="configDisabled" name="auth" lazy>
<authentication/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.autoLinker')" :disabled="configDisabled" name="autoLinker" lazy>
<auto-linker/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.esshd')" :disabled="configDisabled" name="esshd" lazy>
<esshd/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.captcha')" :disabled="configDisabled" name="captcha" lazy>
<captcha/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.frontend')" :disabled="configDisabled" name="frontend" lazy>
<frontend/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.gopher')" :disabled="configDisabled" name="gopher" lazy>
<gopher/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.http')" :disabled="configDisabled" name="http" lazy>
<http/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.instance')" :disabled="configDisabled" name="instance">
<instance/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.jobQueue')" :disabled="configDisabled" name="jobQueue" lazy>
<job-queue/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.logger')" :disabled="configDisabled" name="logger" lazy>
<logger/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.mailer')" :disabled="configDisabled" name="mailer" lazy>
<mailer/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.mediaProxy')" :disabled="configDisabled" name="mediaProxy" lazy>
<media-proxy/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.metadata')" :disabled="configDisabled" name="metadata" lazy>
<metadata/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.mrf')" :disabled="configDisabled" name="mrf" lazy>
<mrf/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.rateLimiters')" :disabled="configDisabled" name="rateLimiters" lazy>
<rate-limiters/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.relays')" lazy name="relays">
<relays/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.webPush')" :disabled="configDisabled" name="webPush" lazy>
<web-push/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.upload')" :disabled="configDisabled" name="upload" lazy>
<upload/>
</el-tab-pane>
<el-tab-pane :label="$t('settings.other')" :disabled="configDisabled" name="other" lazy>
<other/>
<el-tab-pane
v-for="(value, componentName) in tabs"
:label="$t(value.label)"
:disabled="configDisabled"
:key="componentName"
:name="componentName"
lazy>
<component :is="componentName"/>
</el-tab-pane>
</el-tabs>
</div>
@ -119,6 +77,7 @@
</el-button>
</el-link>
</div>
<div class="settings-search-input-container"/>
<activity-pub v-if="activeTab === 'activityPub'"/>
<authentication v-if="activeTab === 'auth'"/>
<auto-linker v-if="activeTab === 'autoLinker'"/>
@ -145,6 +104,7 @@
<script>
import i18n from '@/lang'
import { tabs } from './components/tabs'
import {
ActivityPub,
Authentication,
@ -214,7 +174,8 @@ export default {
{ value: 'webPush', label: i18n.t('settings.webPush') },
{ value: 'upload', label: i18n.t('settings.upload') },
{ value: 'other', label: i18n.t('settings.other') }
]
],
searchQuery: ''
}
},
computed: {
@ -240,6 +201,12 @@ export default {
},
needReboot() {
return this.$store.state.settings.needReboot
},
searchData() {
return this.$store.state.settings.searchData
},
tabs() {
return tabs
}
},
mounted: function() {
@ -256,6 +223,25 @@ export default {
type: 'success',
message: i18n.t('settings.restartSuccess')
})
},
async handleSearchSelect(selectedValue) {
const tab = Object.keys(this.tabs).find(tab => {
return this.tabs[tab].settings.includes(selectedValue.group === ':pleroma' ? selectedValue.key : selectedValue.group)
})
await this.$store.dispatch('SetActiveTab', tab)
const selectedSetting = document.querySelector(`[data-search="${selectedValue.key}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
},
querySearch(queryString, cb) {
const results = this.searchData.filter(searchObj => searchObj.search.find(el => el.includes(queryString.toLowerCase())))
.map(searchObj => {
return searchObj.groupKey === ':opts'
? { value: `${searchObj.label} in Auto Linker`, group: searchObj.groupKey, key: searchObj.key }
: { value: `${searchObj.label} in ${searchObj.groupLabel}`, group: searchObj.groupKey, key: searchObj.key }
})
cb(results)
}
}
}

View file

@ -277,6 +277,10 @@
padding: 10px;
margin-right: 5px;
}
.settings-search-input {
width: 350px;
margin-left: 5px;
}
.single-input {
margin-right: 10px
}
@ -406,6 +410,13 @@
.limit-input {
width: 45%;
}
.nav-container {
display: flex;
height: 36px;
justify-content: space-between;
align-items: center;
margin: 15px;
}
.proxy-url-input {
flex-direction: column;
align-items: flex-start;
@ -446,12 +457,12 @@
.settings-header-container {
margin: 10px 15px 15px 15px;
}
.nav-container {
display: flex;
height: 36px;
justify-content: space-between;
align-items: center;
margin: 15px;
.settings-search-input {
width: 100%;
margin-left: 0;
}
.settings-search-input-container {
margin: 0 15px 15px 15px;
}
.settings-menu {
width: 163px;
@ -530,7 +541,6 @@
margin-right: 8px
}
}
@media only screen and (max-width:801px) and (min-width: 481px) {
.delete-setting-button {
margin: 4px 0 0 10px;
@ -575,5 +585,9 @@
.settings-delete-button {
float: right;
}
.settings-search-input {
width: 250px;
margin: 0 0 15px 15px;
}
}
}

View file

@ -172,7 +172,7 @@ export default {
<style rel='stylesheet/scss' lang='scss'>
.moderate-user-button {
text-align: left;
width: 200px;
width: 350px;
padding: 10px;
}
.moderate-user-button-container {

View file

@ -6,7 +6,12 @@
</h1>
<div class="filter-container">
<users-filter/>
<el-input :placeholder="$t('users.search')" v-model="search" class="search" @input="handleDebounceSearchInput"/>
<el-input
:placeholder="$t('users.search')"
v-model="search"
prefix-icon="el-icon-search"
class="search"
@input="handleDebounceSearchInput"/>
</div>
<div class="actions-container">
<el-button class="actions-button" @click="createAccountDialogOpen = true">

View file

@ -44,19 +44,18 @@ describe('Invite tokens', () => {
})
await flushPromises()
const dialog = wrapper.find('div.el-dialog__wrapper .create-new-token-dialog')
expect(dialog.isVisible()).toBe(false)
wrapper.setData({ createTokenDialogVisible: false })
const openDialogButton = wrapper.find('button.create-invite-token')
const closeDialogButton = wrapper.find('div.el-dialog__footer button')
const closeDialogButton = wrapper.find('div.el-dialog__footer button.invites-close-dialog')
expect(wrapper.vm.createTokenDialogVisible).toBe(false)
openDialogButton.trigger('click')
await flushPromises()
expect(dialog.isVisible()).toBe(true)
expect(wrapper.vm.createTokenDialogVisible).toBe(true)
closeDialogButton.trigger('click')
await flushPromises()
expect(dialog.isVisible()).toBe(false)
expect(wrapper.vm.createTokenDialogVisible).toBe(false)
done()
})

View file

@ -33,7 +33,8 @@ describe('Status in reports', () => {
status,
page: 1,
userId: '7',
godmode: false
godmode: false,
showCheckbox: false
}
})
await flushPromises()
@ -58,7 +59,8 @@ describe('Status in reports', () => {
status,
page: 1,
userId: '7',
godmode: false
godmode: false,
showCheckbox: false
}
})
await flushPromises()
@ -83,7 +85,8 @@ describe('Status in reports', () => {
status,
page: 1,
userId: '7',
godmode: false
godmode: false,
showCheckbox: false
}
})
await flushPromises()
@ -108,7 +111,8 @@ describe('Status in reports', () => {
status,
page: 1,
userId: '7',
godmode: false
godmode: false,
showCheckbox: false
}
})
await flushPromises()
@ -133,7 +137,8 @@ describe('Status in reports', () => {
status,
page: 1,
userId: '7',
godmode: false
godmode: false,
showCheckbox: false
}
})
await flushPromises()

View file

@ -0,0 +1,213 @@
import { formSearchObject } from '@/store/modules/normalizers'
import _ from 'lodash'
describe('Form search object', () => {
it('forms search object for regular setting', () => {
const description = [{
description: "Upload general settings",
group: ":pleroma",
key: "Pleroma.Upload",
label: "Pleroma.Upload",
children: [
{ description: "Module which will be used for uploads",
key: ":uploader",
label: "Uploader" },
{ description: "List of filter modules for uploads",
key: ":filters",
label: "Filters" }
]
}]
const expected = [
{ label: "Pleroma.Upload",
key: "Pleroma.Upload",
groupKey: "Pleroma.Upload",
groupLabel: "Pleroma.Upload",
search: [
"pleroma.upload",
"pleroma.upload",
"upload general settings"
]
},
{ label: "Uploader",
key: ":uploader",
groupKey: "Pleroma.Upload",
groupLabel: "Pleroma.Upload",
search: [
":uploader",
"uploader",
"module which will be used for uploads"
]
},
{ label: "Filters",
key: ":filters",
groupKey: "Pleroma.Upload",
groupLabel: "Pleroma.Upload",
search: [
":filters",
"filters",
"list of filter modules for uploads"
]
}
]
expect(_.isEqual(formSearchObject(description), expected)).toBeTruthy()
})
it('forms search object for setting without key', () => {
const description = [{
description: "`Swoosh.Adapters.Local` adapter specific settings",
group: ":swoosh",
label: "Swoosh",
children: [
{ description: "Run the preview server together as part of your app",
group: [":subgroup", "Swoosh.Adapters.Local"],
key: ":serve_mailbox",
label: "Serve mailbox"
}
]
}]
const expected = [
{ label: "Swoosh",
key: ":swoosh",
groupKey: ":swoosh",
groupLabel: "Swoosh",
search: ["swoosh", "`swoosh.adapters.local` adapter specific settings"]
},
{ label: "Serve mailbox",
key: ":serve_mailbox",
groupKey: ":swoosh",
groupLabel: "Swoosh",
search: [
":serve_mailbox",
"serve mailbox",
"run the preview server together as part of your app"
]
}
]
expect(_.isEqual(formSearchObject(description), expected)).toBeTruthy()
})
it('forms search object for setting without key and description', () => {
const description = [{
group: ":cors_plug",
label: "Cors plug",
children: [
{ key: ":max_age",
label: "Max age" },
{ key: ":methods",
label: "Methods" }
]
}]
const expected = [
{ label: "Cors plug",
key: ":cors_plug",
groupKey: ":cors_plug",
groupLabel: "Cors plug",
search: ["cors plug"]
},
{ label: "Max age",
key: ":max_age",
groupKey: ":cors_plug",
groupLabel: "Cors plug",
search: [":max_age", "max age"]
},
{ label: "Methods",
key: ":methods",
groupKey: ":cors_plug",
groupLabel: "Cors plug",
search: [":methods", "methods"]
}
]
expect(_.isEqual(formSearchObject(description), expected)).toBeTruthy()
})
it('forms search object for setting without key in pleroma group', () => {
const description = [{
description: "Allows to set a token",
group: ":pleroma",
label: "Pleroma",
children: [
{ description: "Token",
key: ":admin_token",
label: "Admin token" }
]
}]
const expected = [{
label: "Admin token",
key: ":admin_token",
groupKey: ":pleroma",
groupLabel: "Pleroma",
search: [":admin_token", "admin token", "token"]
}]
expect(_.isEqual(formSearchObject(description), expected)).toBeTruthy()
})
it('forms search object for nested setting', () => {
const description = [{
description: "Media proxy",
group: ":pleroma",
key: ":media_proxy",
label: "Media proxy",
children: [
{ description: "Options for Pleroma.ReverseProxy",
key: ":proxy_opts",
label: "Proxy opts",
children: [
{ description: "HTTP options",
key: ":http",
label: "Http",
children: [
{ description: "Adapter specific options",
key: ":adapter",
label: "Adapter",
children: [
{ description: "SSL options for HTTP adapter",
key: ":ssl_options",
label: "SSL Options"
}
]
}
]
}
]}
]
}]
const expected = [
{
label: "Media proxy",
key: ":media_proxy",
groupKey: ":media_proxy",
groupLabel: "Media proxy",
search: [":media_proxy", "media proxy", "media proxy"]
},
{
label: "Proxy opts",
key: ":proxy_opts",
groupKey: ":media_proxy",
groupLabel: "Media proxy",
search: [":proxy_opts", "proxy opts", "options for pleroma.reverseproxy"]
},
{
label: "Http",
key: ":http",
groupKey: ":media_proxy",
groupLabel: "Media proxy",
search: [":http", "http", "http options"]
},
{
label: "Adapter",
key: ":adapter",
groupKey: ":media_proxy",
groupLabel: "Media proxy",
search: [":adapter", "adapter", "adapter specific options"]
},
{
label: "SSL Options",
key: ":ssl_options",
groupKey: ":media_proxy",
groupLabel: "Media proxy",
search: [":ssl_options", "ssl options", "ssl options for http adapter"]
}
]
expect(_.isEqual(formSearchObject(description), expected)).toBeTruthy()
})
})

View file

@ -0,0 +1,63 @@
import Vuex from 'vuex'
import { mount, createLocalVue, config } from '@vue/test-utils'
import Element from 'element-ui'
import Settings from '@/views/settings/index'
import flushPromises from 'flush-promises'
import app from '@/store/modules/app'
import settings from '@/store/modules/settings'
import getters from '@/store/getters'
config.mocks["$t"] = () => {}
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Element)
describe('Settings search', () => {
let store
let actions
beforeEach(() => {
actions = { ...settings.actions, FetchSettings: jest.fn() }
store = new Vuex.Store({
modules: {
app,
settings: { ...settings, actions }
},
getters
})
})
it('shows search input', async (done) => {
const wrapper = mount(Settings, {
store,
localVue
})
await flushPromises()
const searchInput = wrapper.find('.settings-search-input')
expect(searchInput.exists()).toBe(true)
done()
})
it('changes tab when search value was selected', async (done) => {
const wrapper = mount(Settings, {
store,
localVue
})
wrapper.vm.handleSearchSelect({ group: 'Pleroma.Upload', key: 'Pleroma.Upload' })
expect(store.state.settings.activeTab).toBe('upload')
wrapper.vm.handleSearchSelect({ group: ':swoosh', key: ':serve_mailbox' })
expect(store.state.settings.activeTab).toBe('mailer')
wrapper.vm.handleSearchSelect({ group: ':pleroma', key: ':admin_token' })
expect(store.state.settings.activeTab).toBe('instance')
wrapper.vm.handleSearchSelect({ group: ':media_proxy', key: ':ssl_options' })
expect(store.state.settings.activeTab).toBe('media-proxy')
wrapper.vm.handleSearchSelect({ group: ':opts', key: ':opts' })
expect(store.state.settings.activeTab).toBe('auto-linker')
done()
})
})

View file

@ -239,21 +239,21 @@ describe('Users actions', () => {
})
await flushPromises()
const dialog = wrapper.find('.password-reset-token-dialog')
wrapper.setData({ resetPasswordDialogOpen: false })
const closeDialogButton = wrapper.find('.password-reset-token-dialog button')
expect(dialog.isVisible()).toBe(false)
expect(wrapper.vm.resetPasswordDialogOpen).toBe(false)
expect(store.state.users.passwordResetToken.token).toBe('')
wrapper.find(htmlElement(1, 11)).trigger('click')
await flushPromises()
expect(dialog.isVisible()).toBe(true)
expect(wrapper.vm.resetPasswordDialogOpen).toBe(true)
expect(store.state.users.passwordResetToken.token).toBe('g05lxnBJQnL')
expect(store.state.users.passwordResetToken.link).toBe('http://url/api/pleroma/password_reset/g05lxnBJQnL')
closeDialogButton.trigger('click')
await flushPromises()
expect(dialog.isVisible()).toBe(false)
expect(wrapper.vm.resetPasswordDialogOpen).toBe(false)
done()
})
})
@ -278,19 +278,18 @@ describe('Creates new account', () => {
})
await flushPromises()
const dialog = wrapper.find('div.el-dialog__wrapper')
expect(dialog.isVisible()).toBe(false)
wrapper.setData({ createAccountDialogOpen: false })
const openDialogButton = wrapper.find('button.actions-button')
const closeDialogButton = wrapper.find('div.el-dialog__footer button')
expect(wrapper.vm.createAccountDialogOpen).toBe(false)
openDialogButton.trigger('click')
await flushPromises()
expect(dialog.isVisible()).toBe(true)
expect(wrapper.vm.createAccountDialogOpen).toBe(true)
closeDialogButton.trigger('click')
await flushPromises()
expect(dialog.isVisible()).toBe(false)
expect(wrapper.vm.createAccountDialogOpen).toBe(false)
done()
})

139
yarn.lock
View file

@ -1013,12 +1013,13 @@
vue-template-es2015-compiler "^1.9.0"
"@vue/test-utils@^1.0.0-beta.29":
version "1.0.0-beta.29"
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.29.tgz#c942cf25e891cf081b6a03332b4ae1ef430726f0"
integrity sha512-yX4sxEIHh4M9yAbLA/ikpEnGKMNBCnoX98xE1RwxfhQVcn0MaXNSj1Qmac+ZydTj6VBSEVukchBogXBTwc+9iA==
version "1.0.0-beta.32"
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.32.tgz#38c3947886236201a3f24b583c73598eb95ccc69"
integrity sha512-ywhe7PATMAk/ZGdsrcuQIliQusOyfe0OOHjKKCCERqgHh1g/kqPtmSMT5Jx4sErx53SYbNucr8QOK6/u5ianAw==
dependencies:
dom-event-types "^1.0.0"
lodash "^4.17.4"
lodash "^4.17.15"
pretty "^2.0.0"
"@webassemblyjs/ast@1.8.5":
version "1.8.5"
@ -2496,6 +2497,11 @@ commander@^2.13.0, commander@^2.14.1, commander@^2.9.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
commander@^2.19.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@~2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
@ -2568,6 +2574,23 @@ concat-stream@^1.5.0, concat-stream@^1.6.0:
readable-stream "^2.2.2"
typedarray "^0.0.6"
condense-newlines@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/condense-newlines/-/condense-newlines-0.2.1.tgz#3de985553139475d32502c83b02f60684d24c55f"
integrity sha1-PemFVTE5R10yUCyDsC9gaE0kxV8=
dependencies:
extend-shallow "^2.0.1"
is-whitespace "^0.3.0"
kind-of "^3.0.2"
config-chain@^1.1.12:
version "1.1.12"
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==
dependencies:
ini "^1.3.4"
proto-list "~1.2.1"
connect-history-api-fallback@^1.3.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
@ -3431,6 +3454,16 @@ echarts@4.1.0:
dependencies:
zrender "4.0.4"
editorconfig@^0.15.3:
version "0.15.3"
resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5"
integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==
dependencies:
commander "^2.19.0"
lru-cache "^4.1.5"
semver "^5.6.0"
sigmund "^1.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@ -4399,7 +4432,7 @@ glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1:
version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
@ -4411,6 +4444,18 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, gl
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.3:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
global-modules@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea"
@ -4934,16 +4979,21 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
inherits@2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@^1.3.4, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
@ -5289,6 +5339,11 @@ is-utf8@^0.2.0:
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
is-whitespace@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/is-whitespace/-/is-whitespace-0.3.0.tgz#1639ecb1be036aec69a54cbb401cfbed7114ab7f"
integrity sha1-Fjnssb4DauxppUy7QBz77XEUq38=
is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
@ -5776,6 +5831,17 @@ js-base64@^2.1.8, js-base64@^2.1.9:
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121"
integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==
js-beautify@^1.6.12:
version "1.10.3"
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.10.3.tgz#c73fa10cf69d3dfa52d8ed624f23c64c0a6a94c1"
integrity sha512-wfk/IAWobz1TfApSdivH5PJ0miIHgDoYb1ugSqHcODPmaYu46rYe5FVuIEkhjg8IQiv6rDNPyhsqbsohI/C2vQ==
dependencies:
config-chain "^1.1.12"
editorconfig "^0.15.3"
glob "^7.1.3"
mkdirp "~0.5.1"
nopt "~4.0.1"
js-cookie@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.0.tgz#1b2c279a6eece380a12168b92485265b35b1effb"
@ -6218,6 +6284,11 @@ lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.3, lodash@^4.17.4,
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
lodash@^4.17.15:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
log-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
@ -6274,7 +6345,7 @@ lower-case@^1.1.1:
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2:
lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2, lru-cache@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
@ -6567,6 +6638,11 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minimist@~0.0.1:
version "0.0.10"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
@ -6640,13 +6716,20 @@ mixin-object@^2.0.1:
for-in "^0.1.3"
is-extendable "^0.1.1"
mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
dependencies:
minimist "0.0.8"
mkdirp@~0.5.1:
version "0.5.3"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c"
integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==
dependencies:
minimist "^1.2.5"
moment@^2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
@ -6919,6 +7002,14 @@ nopt@^4.0.1:
abbrev "1"
osenv "^0.1.4"
nopt@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==
dependencies:
abbrev "1"
osenv "^0.1.4"
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
@ -8022,6 +8113,15 @@ pretty-format@^24.3.0:
ansi-regex "^4.0.0"
ansi-styles "^3.2.0"
pretty@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pretty/-/pretty-2.0.0.tgz#adbc7960b7bbfe289a557dc5f737619a220d06a5"
integrity sha1-rbx5YLe7/iiaVX3F9zdhmiINBqU=
dependencies:
condense-newlines "^0.2.1"
extend-shallow "^2.0.1"
js-beautify "^1.6.12"
printj@~1.1.0, printj@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
@ -8065,6 +8165,11 @@ prompts@^2.0.1:
kleur "^3.0.2"
sisteransi "^1.0.0"
proto-list@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
proxy-addr@~2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93"
@ -8792,7 +8897,7 @@ semver-compare@^1.0.0:
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
@ -8802,6 +8907,11 @@ semver@5.5.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
semver@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
semver@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
@ -8939,6 +9049,11 @@ showdown@1.8.6:
dependencies:
yargs "^10.0.3"
sigmund@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"