%PDF- %PDF-
Direktori : /var/www/cwg/wp-content/plugins/searchwp/assets/javascript/src/Components/ |
Current File : //var/www/cwg/wp-content/plugins/searchwp/assets/javascript/src/Components/Engines.vue |
<template> <div id="searchwp-settings-engines"> <div :class="['searchwp-settings', saving ? 'searchwp-settings-disabled' : '' ]"> <div class="searchwp-engines"> <div class="searchwp-settings-view-header"> <h1>{{ 'SearchWP Engines' | i18n }}</h1> <ul class="searchwp-actions searchwp-settings-engines-actions"> <li> <button class="button" @click="newEngine">{{ 'Add New' | i18n }}</button> </li> </ul> </div> <ul> <li v-for="(settings, name, index) in engines" :key="index"> <Engine :name="name"></Engine> </li> </ul> </div> <div class="searchwp-settings-info"> <ul class="searchwp-actions searchwp-settings-actions"> <li> <button :disabled="!updated" class="button button-primary" @click="save"> {{ 'Save Engines' | i18n }} </button> </li> <li> <button :disabled="updated" class="button" @click="docContentResetAsk ? docContentResetModal() : rebuildIndex()"> {{ 'Rebuild Index' | i18n }} </button> </li> </ul> <Notice v-if="!hasInitialSave" :type="'warning'" :message="'_needs_initial_save' | i18n "> </Notice> <Notice v-else-if="indexOutdated" :type="'warning'" :message="'_index_outdated' | i18n" :tooltip="'_index_outdated_tooltip' | i18n" ></Notice> <Environment :suggestAlternateIndexer="pollingIndexCount > 5 && 0 == $store.state.index.indexed && $store.state.index.total > 0" ></Environment> <div class="searchwp-index-info"> <IndexProgress :progress="indexProgressValue"></IndexProgress> <IndexStats :lastActivity="$store.state.index.lastActivity" :indexed="$store.state.index.indexed" :omitted="$store.state.index.omitted" :total="$store.state.index.total"></IndexStats> <p class="description">{{ '_index_optimization_note' | i18n }}</p> </div> </div> <Modal :name="'omitted'" :label="'Omitted Entries' | i18n()" :actionIsPrimary="false" :actionLabel="'Close' | i18n"> <div v-if="$store.state.index.omitted && $store.state.index.omitted.length" class="searchwp-omitted-notes"> <p>{{ 'SearchWP was unable to index the following content due to an indexing failure. Reviewing server logs may expose the reason for failure.' | i18n }}</p> <table> <colgroup> <col class="searchwp-omitted-source"> <col class="searchwp-omitted-id"> <col class="searchwp-omitted-actions"> </colgroup> <thead> <tr> <th>{{ 'Source' | i18n }}</th> <th>{{ 'ID' | i18n }}</th> <th>{{ 'Actions' | i18n }}</th> </tr> </thead> <tbody> <tr v-for="entry in $store.state.index.omitted" :key="entry.source + entry.id"> <td>{{ entry.source }}</td> <td>{{ entry.id }}</td> <td> <button class="button button-primary" @click="reintroduce(entry)">{{ 'Reintroduce' | i18n }}</button> <a v-if="entry.edit_link" :href="entry.edit_link" class="button" target="_blank">{{ 'Edit' | i18n }}</a> <a v-if="entry.permalink" :href="entry.permalink" class="button" target="_blank">{{ 'View' | i18n }}</a> </td> </tr> </tbody> </table> </div> <p v-else class="description">{{ 'There are no omitted entries at this time.' | i18n }}</p> </Modal> <Modal :name="'welcome'" :label="'Welcome!' | i18n()" :actionIsPrimary="false" :actionLabel="'Close' | i18n"> <div class="searchwp-migration-notes"> <div v-if="welcome && !activeMigration"> <p>{{ '_welcome_intro', | i18n }}</p> <Notice :type="'warning'"> <component :is="i18nWelcomeWarning"></component> </Notice> <component :is="i18nWelcomeBlurb"></component> </div> <div v-else-if="activeMigration"> <component :is="i18nWelcomeMigratedIntro"></component> <Notice v-if="!migratingComplete" :type="'warning'"> <component :is="i18nWelcomeMigratedWarning"></component> <p><button :disabled="migrating" @click="migrateStatistics" class="button button-primary">{{ 'Migrate Statistics' | i18n }} </button></p> <div class="searchwp-migrating-statistics" v-if="migrating"> <p>{{ 'Performing migration, please wait...' | i18n }}</p> <div> <Spinner size="small" :line-fg-color="spinnerFgColor" :line-bg-color="spinnerBgColor" ></Spinner> </div> </div> </Notice> <Notice v-else :type="'success'"> <component :is="i18nWelcomeMigratedSuccess"></component> </Notice> </div> </div> </Modal> <Modal :name="'docContentResetNotice'" :label="'Reset Document Content?' | i18n()" :actionIsPrimary="false" :actionLabel="'Cancel' | i18n" @hide="docContentResetNoticeShowing = false"> <div class="searchwp-doc-ccontent-reset"> <Notice :type="'warning'"> <p>{{ 'In order to optimize index rebuilds, SearchWP does not purge extracted Document Content, PDF Metadata, or image EXIF data by default.' | i18n }}</p> </Notice> <p>{{ 'If you are customizing Document Content, PDF Metadata, or image EXIF in any way, you may want to change that behavior and have SearchWP re-parse that content when rebuilding the index.' | i18n }}</p> <p>{{ 'Would you prefer that SearchWP remove all stored Document Content, PDF Metadata, and image EXIF data so it can be re-parsed during Index rebuilds?' | i18n }}</p> <div style="padding-top: 0.5em; padding-bottom: 1em;"> <Checkbox :id="'searchwp-doc-content-reset-modal'" :checked="false" @change="function(value) { resetDocContentOnRebuild = value; }"> <template v-slot:default> {{ 'Remove stored Document Content, PDF Metadata, and image EXIF when rebuilding index' | i18n }} </template> <template v-slot:description> <p class="description">{{ '(This setting can be changed at any time on the Advanced tab of the SearchWP settings screen)' | i18n }}</p> </template> </Checkbox> </div> <p><button @click="handleDocContentResetModal" class="button button-primary">{{ 'Continue Index Rebuild' | i18n }} </button></p> </div> </Modal> </div> <transition name="fade"> <div v-if="saving" class="searchwp-settings-disabled-message"> <div> <span>{{ saving }}</span> <div> <Spinner size="small" :line-fg-color="spinnerFgColor" :line-bg-color="spinnerBgColor" ></Spinner> </div> </div> </div> </transition> </div> </template> <script> import { mapState } from 'vuex'; import Modal from './Modal.vue'; import Engine from './Engine.vue'; import Notice from './Notice.vue'; import Tooltip from './Tooltip.vue'; import isEqual from 'lodash.isequal'; import { __ } from './../helpers.js'; import cloneDeep from 'lodash.clonedeep'; import Spinner from 'vue-simple-spinner'; import IndexStats from './Index/Stats.vue'; import Environment from './Environment.vue'; import Checkbox from './Inputs/Checkbox.vue'; import IndexProgress from './Index/Progress.vue'; export default { name: 'Engines', components: { Engine, IndexProgress, IndexStats, Notice, Environment, Modal, Tooltip, Spinner, Checkbox }, computed: { spinnerFgColor: function() { return _SEARCHWP.misc.colors.link.hover; }, spinnerBgColor: function() { return _SEARCHWP.misc.colors.border; }, i18nWelcomeWarning: function() { return { template: '<p> ' + __('_welcome_warning') + '</p>' }; }, i18nWelcomeBlurb: function() { return { template: '<div> ' + __('_welcome_blurb') + '</div>' }; }, i18nWelcomeMigratedIntro: function() { return { template: '<p> ' + __('_welcome_migration_intro') + '</p>' }; }, i18nWelcomeMigratedSuccess: function() { return { template: '<p> ' + __('_welcome_migration_success') + '</p>' }; }, i18nWelcomeMigratedWarning: function() { return { template: '<p> ' + __('_welcome_migration_warning') + '</p>' }; }, activeMigration: function() { // Migration is only active if we've migrated and we're showing the Welcome screen. return this.migrated && location.search.indexOf('welcome'); }, engines: function() { return this.$store.state.engines; }, updateInterval: function() { return this.indexProgressValue < 100 ? 3000 : 20000; }, indexOutdated: function() { return this.$store.state.index.outdated; }, indexProgressValue: function() { return this.$store.getters.indexProgress; }, updated: function() { // Check to see whether the engine models have changed at all. return !isEqual(this.current, this.original); }, ...mapState({ current: state => state.engines }) }, methods: { reintroduce: function(entry) { let vm = this; // Remove the entry from the list. this.$store.commit('removeOmittedEntry', entry); jQuery.post(ajaxurl, { _ajax_nonce: _SEARCHWP.nonce, action: _SEARCHWP.prefix + 'reintroduce_entry', source: entry.source, id: entry.id }, function(response) { clearInterval(vm.pollingIndex); vm.$store.commit('updateIndexStats', response.data); vm.updateIndexStats(); }); }, migrateStatistics: function() { let vm = this; vm.migrating = true; jQuery.post(ajaxurl, { _ajax_nonce: _SEARCHWP.nonce, action: _SEARCHWP.prefix + 'migrate_stats' }, function(response) { vm.migrating = false; vm.migratingComplete = true; }); }, newEngine: function() { let self = this; this.$store.commit('addNewEngine'); setTimeout(function() { self.$scrollTo('.searchwp-engines > ul > li:last-of-type', 500, { offset: -40 }); }, 200); }, docContentResetModal: function() { // We only want to show this notice if Document Content or PDF Metadata has been added to an Engine. let applicable = false; let sourceName = 'post' + _SEARCHWP.separator + 'attachment'; for (let engine in this.engines) { if (!this.engines[engine].sources.hasOwnProperty(sourceName)) { continue; } if ( this.engines[engine].sources[sourceName].attributes.document_content.settings || this.engines[engine].sources[sourceName].attributes.pdf_metadata.settings || this.engines[engine].sources[sourceName].attributes.image_exif.settings ) { applicable = true; break; } if (applicable) { break; } } if (applicable) { this.docContentResetNoticeShowing = true; this.$modal.show('docContentResetNotice'); } else { this.rebuildIndex(); } }, handleDocContentResetModal: function() { this.$modal.hide('docContentResetNotice'); this.docContentResetAsk = false; // Permanently dismiss this dialog. jQuery.post(ajaxurl, { _ajax_nonce: _SEARCHWP.nonce, action: _SEARCHWP.prefix + 'update_setting', setting: 'document_content_reset_dismissed', value: JSON.stringify(true) }); // Persist the setting. jQuery.post(ajaxurl, { _ajax_nonce: _SEARCHWP.nonce, action: _SEARCHWP.prefix + 'update_setting', setting: 'document_content_reset', value: JSON.stringify(this.resetDocContentOnRebuild) }); this.rebuildIndex(); }, rebuildIndex: function() { let vm = this; if (confirm(__('_rebuild_index_confirmation'))) { // Force an update to the Index Stats. vm.saving = __('Resetting index...'); clearInterval(vm.pollingIndex); vm.$store.commit('updateIndexStats', { lastActivity: '--', indexed: 0, total: 0, outdated: false }); jQuery.post(ajaxurl, { _ajax_nonce: _SEARCHWP.nonce, action: _SEARCHWP.prefix + 'rebuild_index' }, function(response) { vm.saving = false; vm.updateIndexStats(); }); } }, save: function() { let vm = this; vm.saving = __('Saving Engines...'); setTimeout(function() { jQuery.post(ajaxurl, { _ajax_nonce: _SEARCHWP.nonce, action: _SEARCHWP.prefix + 'engines_configs', configs: JSON.stringify(vm.$store.state.engines) }, function(response) { vm.saving = false; if (response.success) { vm.hasInitialSave = true; // We're going to accept the returned configs from the server and reset the entire state using them. const engines = response.data.engines; vm.original = cloneDeep(engines); vm.$store.commit('replaceupdateEngineSettings', cloneDeep(engines)); // Force the index progress to update right now. Includes whether the index is outdated. clearInterval(vm.pollingIndex); vm.$store.commit('updateIndexStats', response.data.index); vm.updateIndexStats(); } else { alert(__('Saving engine settings FAILED!')); } }); }, 500); }, updateIndexStats: function() { let vm = this; vm.pollingIndex = setInterval(function(){ if (vm.hasInitialSave && !vm.waiting) { vm.waiting = true; jQuery.post(ajaxurl, { _ajax_nonce: _SEARCHWP.nonce, action: _SEARCHWP.prefix + 'index_stats' }, function(response) { vm.waiting = false; vm.pollingIndexCount++; if (response.success) { vm.$store.commit('updateIndexStats', response.data); // If the index has completed building, we can reduce our update interval. if (vm.indexProgressValue > 99) { clearInterval(vm.pollingIndex); vm.updateIndexStats(); } } }); } }, vm.updateInterval); }, checkForUnsavedEngines: function(event) { if (this.updated) { event.preventDefault(); // Chrome requires returnValue to be set. event.returnValue = ''; } } }, created() { this.updateIndexStats(); if (this.hasInitialSave) { this.original = cloneDeep(_SEARCHWP.engines); } }, mounted() { if (this.welcome || this.migrated) { this.$modal.show('welcome'); } }, beforeMount() { window.addEventListener('beforeunload', this.checkForUnsavedEngines); }, beforeDestroy() { window.removeEventListener('beforeunload', this.checkForUnsavedEngines); clearInterval(this.pollingIndex); }, data() { return { waiting: false, original: {}, saving: false, migrating: false, migratingComplete: false, pollingIndex: null, pollingIndexCount: 0, hasInitialSave: _SEARCHWP.misc.hasInitialSave, welcome: _SEARCHWP.welcome && _SEARCHWP.welcome === '1' ? true : false, migrated: _SEARCHWP.migrated && _SEARCHWP.migrated === '1' ? true : false, docContentResetAsk: _SEARCHWP.misc.docContentResetAsk, docContentResetNoticeShowing: false, resetDocContentOnRebuild: false, } } } </script> <style lang="scss"> @import './../global.scss'; .js .searchwp-settings .postbox .hndle { cursor: pointer; } .searchwp-migrating-statistics { margin: 0.5em 0; display: flex; align-items: center; > p { margin-right: 0.5em; } } .searchwp-engines { width: 75%; > ul { margin: 0; padding: 0; list-style: none; } .searchwp-engine { margin-bottom: 2em; } } .searchwp-settings-info { width: 23%; position: sticky; top: calc(1em + 36px); // 1em is .searchwp-settings-view padding-top. 36px is Admin Bar height. .searchwp-notice { margin-top: 1em; } } .searchwp-settings-actions { justify-content: space-between; } .searchwp-index-info { margin-top: 1em; > p { margin-top: 1em; } } .searchwp-settings .searchwp-actions { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; > * { margin-right: 1em; margin-bottom: 1em; &:last-child { margin-right: 0; } } } .searchwp-settings-view .searchwp-settings-view-header .searchwp-actions.searchwp-settings-engines-actions > * { margin-left: 0; } .searchwp-migration-status { text-align: center !important; } .searchwp-migration-notes > div > p:first-of-type { margin-top: 0; } .searchwp-migration-notes table { margin-top: 2em; } .searchwp-migration-notes .searchwp-notice { background-color: #f7f7f7; } #searchwp-settings-engines { position: relative; } .searchwp-settings-disabled-message { position: absolute; top: 0; left: 0; width: 96%; height: 60vh; font-size: 1.5em; text-align: center; display: flex; align-content: center; align-items: center; justify-content: center; cursor: wait; > div { display: flex; align-items: center; justify-items: center; padding: 1.25em 2em; background: white; border-radius: 3px; border: 1px solid #e3e3e3; > span { display: block; line-height: 1; } > div { padding-left: 1em; } } } @media screen and (max-width:1024px) { .searchwp-settings-view .searchwp-settings { display: block; .searchwp-engines, .searchwp-settings-info { width: auto; } .searchwp-settings-info { max-width: 400px; } } } @media screen and (max-width:782px) { .searchwp-engines .searchwp-engine-settings-sources ul > li { width: 50%; } } @media screen and (max-width:700px) { .searchwp-settings .searchwp-engines { .searchwp-engine-source-config-overview { display: block; } .searchwp-engine-source-config-overview::after { display: none; } .searchwp-engine-source-config-attributes, .searchwp-engine-source-config-options-rules { width: auto; } } } @media screen and (max-width:550px) { .wp-core-ui #poststuff .searchwp-engines .searchwp-meta-box-heading .searchwp-meta-box-heading__label { display: block; } .searchwp-engines .searchwp-meta-box-heading__label .searchwp-button-subtle { display: none; } .searchwp-engines .searchwp-meta-box-heading__label > button { margin-top: 1em; margin-left: 0; } .searchwp-engines .searchwp-engine-settings-sources ul > li { width: 100%; } .searchwp-engine-source-rule-group-rule .searchwp-engine-source-rule { display: block; } .searchwp-engines .searchwp-engine-source-rule > * { margin-left: 0; margin-top: 0.5em; } } .searchwp-omitted-source { width: 30%; } .searchwp-omitted-id { width: 30%; } .searchwp-omitted-actions { width: 40%; } </style>