Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
#tvd-contents-sets h4{margin:10px 0;font-size:1.1em}#tvd-content-sets-autocomplete{width:400px;max-width:100%;height:36px;outline:none;padding:0 10px;border-radius:4px;border:solid 1px rgba(176,185,193,.8);position:relative;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23b0b9c1' d='M508.5 481.6l-129-129c-2.3-2.3-5.3-3.5-8.5-3.5h-10.3C395 312 416 262.5 416 208 416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c54.5 0 104-21 141.1-55.2V371c0 3.2 1.3 6.2 3.5 8.5l129 129c4.7 4.7 12.3 4.7 17 0l9.9-9.9c4.7-4.7 4.7-12.3 0-17zM208 384c-97.3 0-176-78.7-176-176S110.7 32 208 32s176 78.7 176 176-78.7 176-176 176z'/%3E%3C/svg%3E") center right 10px/15px no-repeat}#tvd-content-sets-autocomplete::placeholder{color:#b0b9c1}.tvd-content-sets-dropdown{box-shadow:0 1px 3px 0 rgba(0,0,0,.22);background-color:#fff;border:none;border-bottom:1px solid #a3a3a3}.tvd-content-sets-dropdown li{line-height:1.5rem;min-height:22px;margin-bottom:0;padding:6px;font-size:14px}.tvd-content-sets-dropdown li:hover{background-color:#f6f6f6}#tvd-matched-content-sets:not(:empty){background-color:rgba(241,241,241,.5);margin:10px 0 0;font-size:14px;padding:10px 10px 5px;width:400px;max-width:100%;box-sizing:border-box}#tvd-matched-content-sets:not(:empty)>div{background-color:#fff;line-height:22px;border:solid 1px #c0cad1;border-radius:2px;color:#50565f;position:relative;display:inline-flex;align-items:center;margin:0 5px 5px 0;padding:0 5px}.removeMatchedTag{color:#c0cad1;font-weight:normal;margin-left:10px;cursor:pointer}.removeMatchedTag:after{content:"×"}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,18 @@
module.exports = Backbone.Collection.extend( {
model: require( '../models/rule' ),
isCompleted() {
let isComplete = true;
if ( this.length === 0 ) {
isComplete = false
} else {
this.each( model => {
if ( ! model.isCompleted() ) {
isComplete = false;
}
} );
}
return isComplete
}
} );

View File

@@ -0,0 +1,118 @@
module.exports = Backbone.Collection.extend( {
model: require( '../models/set' ),
/**
* @property {number} offset offset
*/
offset: 0,
/**
* @property {number} of items per page
*/
limit: 8,
/**
* @property {number} of current page
*/
currentPage: 1,
/**
* Filter - map of key => value pairs to be sent to the server for filtering
*/
_filter: {},
/**
* Checks if the collection has filters
*
* @return {boolean} has filters
*/
hasFilters() {
return Object.keys( this._filter ).length > 0;
},
/**
* Reset all filters
*
* @return {Backbone.Collection} this
*/
resetFilters() {
this.currentPage = 1;
this.offset = 0;
this._filter = {};
return this;
},
applyFilters( key, value ) {
this.currentPage = 1;
this.offset = 0;
this._filter[ key ] = value;
return this;
},
/**
* Increment the current page and calculates the new offset
*
* @return {Backbone.Collection} this
*/
next() {
this.currentPage ++;
this.offset = ( this.currentPage - 1 ) * this.limit;
return this;
},
/**
* Decrement the current page and calculates the new offset
*
* @return {Backbone.Collection} this
*/
prev() {
this.currentPage --;
this.offset = ( this.currentPage - 1 ) * this.limit;
return this;
},
/**
* Based on current items returns them to be used outside:
* - used on pagination template
*
* @return {{next: boolean, prev: boolean, totalPages: number, page: *, currentPage: *}} page information
*/
pageInfo() {
const total = this.length;
return {
currentPage: this.currentPage,
totalPages: Math.ceil( total / this.limit ),
page: this.currentPage,
prev: this.offset > 0,
next: ( this.currentPage * this.limit ) < total,
}
},
paginated() {
const page = this.currentPage - 1,
removeIds = [];
let collection = this.clone();
if ( this.hasFilters() ) {
if ( this._filter.search && this._filter.search.length > 0 ) {
collection.each( model => {
const title = model.get( 'post_title' ).toLowerCase();
if ( title.indexOf( this._filter.search ) === - 1 ) {
removeIds.push( model );
}
} );
for ( let i in removeIds ) {
collection.remove( removeIds[ i ] );
}
}
}
collection = _( collection.rest( this.limit * page ) );
collection = _( collection.first( this.limit ) );
return collection;
}
} );

View File

@@ -0,0 +1,118 @@
( function ( $ ) {
/**
* On document ready init app
*/
$( tvdInitApp );
/**
* Init App
*/
function tvdInitApp() {
TD.matches = new Backbone.Collection( TD.matches || [] );
TD.sets = new Backbone.Collection( TD.sets || [] );
new AppView( {
el: $( '#tvd-contents-sets' ),
} );
}
/**
* Main App View
*/
const AppView = Backbone.View.extend( {
events: {
/**
* @param {Event} event
*/
'click .removeMatchedTag': event => {
const ID = parseInt( event.currentTarget.parentNode.getAttribute( 'data-id' ) ),
matchedModel = TD.matches.findWhere( {id: ID} );
if ( matchedModel ) {
TD.matches.remove( matchedModel );
}
},
},
/**
* @param {Object} options
*/
initialize( options ) {
$.extend( this, true, options );
this.listenTo( TD.matches, 'add remove', this.renderMatchedTags );
this.$autocomplete = this.$( '#tvd-content-sets-autocomplete' );
this.$matchedWrapper = this.$( '#tvd-matched-content-sets' );
this.renderMatchedTags();
},
renderMatchedTags() {
this.$matchedWrapper.empty();
/**
* Array needed to send data to backend
*
* @type {string[]}
*/
const matchedIDs = [];
TD.matches.each( matchedModel => {
this.$matchedWrapper.append( `<div data-id="${matchedModel.get( 'id' )}"><span>${matchedModel.get( 'text' )}</span><span class="removeMatchedTag"></span></div>` )
matchedIDs.push( matchedModel.get( 'id' ) );
} );
this.bindAutocomplete();
if ( matchedIDs.length ) {
this.$matchedWrapper.append( `<input type="hidden" name="tvd_matched_content_sets_id" value="${matchedIDs.toString()}" />` );
}
},
_escape( value ) {
return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
},
bindAutocomplete() {
if ( this.$autocomplete.data( 'autocomplete' ) ) {
this.$autocomplete.autocomplete( 'destroy' );
this.$autocomplete.removeData( 'autocomplete' );
}
this.$autocomplete.autocomplete( {
appendTo: this.$autocomplete.parent(),
minLength: 0,
classes: {
'ui-autocomplete': 'tvd-content-sets-dropdown',
},
source: ( request, response ) => {
const regex = new RegExp( this._escape( request.term ), 'i' ),
setsJSON = TD.sets.toJSON().filter( elem => typeof TD.matches.findWhere( {id: elem.id} ) === 'undefined' ),
recs = request.term.trim().length === 0 ? setsJSON : $.grep( setsJSON, function ( set ) {
return regex.test( set.text )
} );
response( $.map( recs, function ( set ) {
return {
label: set.text,
key: set.id
};
} ) );
},
select: ( event, ui ) => {
this.$autocomplete.val( '' );
const matchedModel = TD.sets.findWhere( {id: ui.item.key} );
if ( matchedModel ) {
TD.matches.add( matchedModel.clone() );
}
return false;
}
} )
}
} );
} )( jQuery );

View File

@@ -0,0 +1,40 @@
( function ( $ ) {
/**
* We need this because this needs to be implemented in other plugins as well
*
* Ex: Thrive Apprentice
*/
$.extend( TD_SETS, {
Models: {
Set: require( './models/set' ),
Rule: require( './models/rule' ),
},
Collections: {
Sets: require( './collection/sets' ),
Rules: require( './collection/rules' ),
},
Views: {
List: require( './views/list' ),
Item: require( './views/item' ),
Rule: require( './views/rule' ),
Form: require( './views/form' ),
Pagination: require( './views/pagination' ),
Confirm: require( './views/confirm-action' ),
Modals: {
Edit: require( './views/modals/edit' ),
},
Controls: {
Base: require('./views/controls/base')
}
},
} );
/**
* Allow other functionality to modify stuff on TD_SETS constant
*
* Used in TA Plugin
*/
$( window ).trigger( 'td_sets_ready', TD_SETS );
TD_SETS.sets = new TD_SETS.Collections.Sets( TD_SETS.sets );
} )( jQuery );

View File

@@ -0,0 +1,65 @@
module.exports = Backbone.Model.extend( {
/**
* Append WP Nonce to all requests
*
* @param {string} method
* @param {*} collection
* @param {*} options
*/
sync( method, collection, options ) {
const beforeSend = options.beforeSend;
options.beforeSend = function ( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', TD_SETS.nonce );
if ( typeof beforeSend === 'function' ) {
beforeSend.apply( this, arguments );
}
};
return Backbone.Model.prototype.sync.apply( this, arguments );
},
/**
* Builds an correctly-formatted URL from baseUri (which can already contain a query string) and (optionally) a map of query string parameters
*
* @param {string} baseUri
* @param {Object} [params]
* @param {string} [pathAppend] string to be appended to the path of the url object
*
* @return {string} url
*/
buildUrl( baseUri, params = {}, pathAppend = '' ) {
const url = new URL( baseUri );
_.each( params, ( value, key ) => {
url.searchParams.append( key, value );
} );
if ( pathAppend ) {
if ( baseUri.includes( '?rest_route' ) ) { //Fixed issue with different type of permalinks
url.searchParams.set( 'rest_route', url.searchParams.get( 'rest_route' ) + pathAppend );
} else {
url.pathname += pathAppend;
}
}
return url.toString();
},
/**
* Wraps up an error object for later use
*
* @param {string} field
* @param {string} message
* @return {{field: *, message: *}} error object
*/
validation_error( field, message ) {
return {
field,
message
};
},
/**
* Gets the first error message from list if any defined
*
* @return {string} the error message
*/
getValidationError() {
return this.validationError && this.validationError[ 0 ] ? this.validationError[ 0 ].message : '';
}
} );

View File

@@ -0,0 +1,92 @@
module.exports = require( './base' ).extend( {
idAttribute: 'ID',
defaults() {
return {
content_type: '', //post or course
content: '',
field: '',
operator: '',
value: [],
content_label: {
singular: '',
plural: '',
}
}
},
orderSteps: [ 'content', 'field', 'operator', 'value' ],
getSteps() {
return this.orderSteps;
},
getValues() {
let _return = [];
if ( Array.isArray( this.get( 'value' ) ) ) {
this.get( 'value' ).forEach( val => {
if ( typeof val === 'object' && typeof val.id !== 'undefined' ) {
_return.push( val.id );
}
} );
}
return _return;
},
/**
* Checks if all the attributes of the model are filled
*
* @return {boolean}
*/
isCompleted() {
let isCompleted = true
this.orderSteps.some( step => {
if ( this.get( 'field' ) && parseInt( this.get( 'field' ) ) === - 1 ) {
/**
* if field === -1 return true.
*/
return true;
}
if ( this.get( step ).length === 0 ) {
isCompleted = false;
return true;
}
} );
return isCompleted;
},
/**
* Checks if all the attributes of the model are empty
*
* @return {boolean}
*/
isEmpty() {
let empty = true
this.orderSteps.some( step => {
if ( this.get( step ).length > 0 ) {
empty = false;
return true;
}
} );
return empty;
},
/**
* Updates the rule info from DB.
* mainly this is for content_label rule key
*
* @returns {Promise<unknown>}
*/
normalize() {
return new Promise( resolve => {
wp.apiRequest(
{
type: 'POST',
url: `${TD_SETS.routes.base}/normalize-rule`,
data: this.toJSON(),
}
).always( data => resolve( data ) );
} );
}
} );

View File

@@ -0,0 +1,64 @@
const RulesCollection = require( '../collection/rules' );
module.exports = require( './base' ).extend( {
idAttribute: 'ID',
allowEmptyRules: false,
defaults() {
return {
post_title: '',
post_content: new RulesCollection(),
}
},
initialize( options = {} ) {
if ( options.post_content instanceof Backbone.Collection ) {
options.post_content = jQuery.extend( true, [], options.post_content.toJSON() );
}
if ( options.post_content ) {
this.set( 'post_content', new RulesCollection( options.post_content ) );
}
},
parse( data ) {
if ( data.post_content instanceof Backbone.Collection ) {
data.post_content = jQuery.extend( true, [], data.post_content.toJSON() );
}
if ( data.post_content ) {
/**
* This is not optimal but gets the job done
*/
this.get( 'post_content' ).reset( data.post_content );
data = [];
}
return data;
},
url( path = '', params = {} ) {
return this.buildUrl( [
TD_SETS.routes.base,
this.get( 'ID' ),
path
].filter( i => i ).join( '/' ), params );
},
getRules() {
return this.get( 'post_content' );
},
/**
* Validates current model
*
* @param {Object} data
* @return {undefined|Array} result
*/
validate( data = {} ) {
const errors = [];
if ( ! data.post_title ) {
errors.push( this.validation_error( 'post_title', 'Title is empty!' ) );
}
if ( this.allowEmptyRules === false && ! data.post_content.isCompleted() ) {
errors.push( this.validation_error( 'post_content', 'You must add some content' ) );
}
if ( errors.length ) {
return errors;
}
},
} );

View File

@@ -0,0 +1,100 @@
( function ( $ ) {
module.exports = Backbone.View.extend( {
events: {
'click .click': '_call',
'change .change': '_call',
'input .input': '_call',
},
/**
* View Constructor
*
* @param {Object} options
*/
initialize( options ) {
$.extend( true, this, options );
if ( options && options.template ) {
this.template = options.template;
}
this.afterInitialize( options );
},
/**
* Call method for specific events
*
* @param {Event} event
* @return {*} result from handler function
*/
_call( event ) {
const _method = event.currentTarget.dataset.fn;
if ( typeof this[ _method ] === 'function' ) {
return this[ _method ].call( this, event, event.currentTarget );
}
},
/**
* Appends the template's html into $el
*
* @return {Backbone.View} the caller view
*/
render() {
if ( typeof this.template === 'string' ) {
this.template = TVE_Dash.tpl( this.template );
}
if ( typeof this.template === 'function' ) {
this.$el.html( this.template( {model: this.model} ) );
}
/**
* Used to do stuff after the template is applied.
*
* Ex: declare the view variables
*/
this.afterRender();
return this;
},
/**
* Opens a new modal
*
* @param {Function} modalView
* @param {Object} params
*/
openModal( modalView, params = {} ) {
if ( modalView.prototype instanceof TVE_Dash.views.Modal ) {
params =
{
...{
'max-width': 'calc(100% - 40px)',
width: '850px',
in_duration: 200,
out_duration: 300,
className: 'tvd-modal tva-modal-create',
dismissible: true
}, ...params
};
return TVE_Dash.modal( modalView, params );
}
// eslint-disable-next-line no-console
console.warn( 'Invalid type of modal view' )
},
/**
* Completely destroy the view and un-delegate any events
*/
destroy() {
this.stopListening();
this.undelegateEvents();
this.$el.removeData().off();
return this;
},
/**
* Overridden in child views
*/
afterRender: $.noop,
afterInitialize: $.noop,
} );
} )( jQuery );

View File

@@ -0,0 +1,16 @@
module.exports = require( './base' ).extend( {
/**
* Removes the view from DO because user doesn't confirm his action
* - defined in HTML
*/
cancel() {
this.remove();
},
/**
* This should be overwritten or extended when this view is initialized
* - defined in HTML
*/
confirm() {
this.remove();
}
} );

View File

@@ -0,0 +1,42 @@
module.exports = require( '../base' ).extend( {
template: '',
className: 'tvd-content-set-rule-step',
afterInitialize( options ) {
this.ruleModel = options.ruleModel;
this.step = options.step;
},
/**
* Checks if the step is completed
*
* @return {boolean}
*/
hasStoredValue() {
return this.getStoredValue().length > 0;
},
getStoredValue() {
return this.ruleModel.get( this.step );
},
/**
* Should be extended in other classes
* @param value
* @return {boolean}
*/
isValid( value ) {
return value && value.trim().length > 0;
},
change( event, dom ) {
if ( this.isValid( dom.value ) ) {
this.ruleModel.trigger( 'control-changed', this.step, this.processValue( dom.value ) );
}
},
/**
* Process the value before storing into the model
*
* @param {string|Array} value
*
* @return {string|Array}
*/
processValue( value ) {
return value;
}
} );

View File

@@ -0,0 +1,33 @@
module.exports = require( './base' ).extend( {
template: 'tvd-c-s-date-picker',
afterRender() {
this.$input = this.$( '.tvd-date-picker' );
this.$input.pickadate( {
firstDay: 1,
format: 'dd-mmm-yyyy',
formatSubmit: 'yyyy-mm-dd',
hiddenName: true,
} );
this.picker = this.$input.pickadate( 'picker' );
if ( this.hasStoredValue() ) {
this.$input.val( this.processFormat() );
}
},
processFormat() {
const date = new Date( this.getStoredValue() ),
mm = parseInt( date.getMonth() ) + 1,
yr = date.getFullYear(),
month = mm < 10 ? '0' + mm : mm,
day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
return day + '-' + month + '-' + yr;
},
processValue( value ) {
value = this.picker.get( 'select', 'yyyy-mm-dd' );
return value;
}
} );

View File

@@ -0,0 +1,80 @@
module.exports = require( './base' ).extend( {
template: 'tvd-c-s-select-multiple',
afterRender() {
this.$select = this.$( 'select' );
if ( this.$select.data( 'select2' ) ) {
this.$select.select2( 'destroy' );
}
const model = this.ruleModel;
this.$select.select2( {
placeholder: "Search for content",
minimumInputLength: 2,
delay: 250,
multiple: true,
tags: false, //TAGS: true or false. If set to false, new values can not be created
ajax: {
url: TD_SETS.routes.base,
type: 'GET',
dataType: 'json',
data: function ( obj ) {
return {...model.toJSON(), ...{_wpnonce: TD_SETS.nonce, query_string: obj.term}};
},
delay: 250,
cache: true,
processResults: function ( data ) {
return {
results: data
};
}
}
} );
if ( this.hasStoredValue() ) {
this.setSelected( this.getStoredValue() )
}
this.$select.data( 'select2' ).$container.addClass( 'tvd-content-set-select-multiple' );
this.$select.on( 'select2:select', ( e ) => {
this.change();
} ).on( 'select2:unselect', ( e ) => {
if ( ! e.params.originalEvent ) {
return;
}
e.params.originalEvent.stopPropagation();
this.change()
} ).data( 'select2' ).$dropdown.addClass( 'tvd-content-set-delete-select-multiple-dropdown' );
},
/**
* Sets value to a select that has data from remote location (dynamically from server)
*/
setSelected: function ( values ) {
const _ids = [];
this.$select.empty();
//Remove empty values
values = values.filter( ( a ) => a );
values.forEach( _val => {
_ids.push( _val.id );
this.$select.append( '<option value="' + _val.id + '">' + _val.text + '</option>' );
} );
this.$select.val( _ids ).trigger( 'change' );
},
change( event, dom ) {
const value = [];
this.$select.select2( 'data' ).forEach( data => {
value.push( {id: data.id, text: data.text} );
} );
this.ruleModel.trigger( 'control-changed', this.step, value );
}
} );

View File

@@ -0,0 +1,60 @@
module.exports = require( './base' ).extend( {
template: 'tvd-c-s-select',
afterRender() {
this.$select = this.$( 'select' );
this.model.get( 'options' ).forEach( option => {
const opt = `<option value="${option.value}"${option.disabled ? ' disabled selected' : ''}>${option.label}</option>`;
this.$select.append( opt );
} );
if ( this.hasStoredValue() ) {
this.setSelected( this.getStoredValue() )
}
if ( this.model.get( 'trigger_change' ) ) {
this.setSelected( this.getFirstOptionValue() );
setTimeout( () => {
this.triggerChange();
} )
}
this.$select.select2( {
//Disable the search input for simple select
minimumResultsForSearch: - 1,
width: '100%'
} );
this.$select.data( 'select2' ).$container.addClass( 'tvd-content-set-select-simple' );
this.$select.data( 'select2' ).$dropdown.addClass( 'tvd-content-set-select-dropdown' );
},
setSelected( selected ) {
if ( selected ) {
this.$select.find( `option[value="${selected}"]` ).attr( 'selected', 'selected' );
}
},
/**
* Returns the first non empty value
*
* @return {*}
*/
getFirstOptionValue() {
return this.$select.find( `option:eq(1)` ).val();
},
/**
* Triggers the change on the control element
*/
triggerChange() {
this.$select.trigger( 'change' );
},
/**
* A <select>ed value is always valid
*
* @return {boolean}
*/
isValid() {
return true;
}
} );

View File

@@ -0,0 +1,46 @@
module.exports = require( './base' ).extend( {
template: 'tvd-c-s-within-the-last',
afterRender() {
this.$input = this.$( 'input' );
this.$select = this.$( 'select' );
if ( this.hasStoredValue() ) {
const value = this.getStoredValue().split( ' ' );
this.$input.val( value[ 0 ].trim() );
this.$select.val( value[ 1 ].trim() );
} else {
this.$input.val( 0 );
this.$select.val( this.$select.find( 'option' ).first().attr( 'value' ) );
}
this.$select.select2();
this.$select.data( 'select2' ).$container.addClass( 'mt-10 tvd-content-set-select-simple' );
this.$select.data( 'select2' ).$dropdown.addClass( 'tvd-content-set-select-dropdown' );
},
isValid() {
const inputValue = this.$input.val();
if ( isNaN( inputValue ) || parseInt( inputValue ) < 0 ) {
this.$input.val( 1 );
return false;
}
return Number.isInteger( parseInt( this.$input.val() ) ) && this.$select.val().length > 0;
},
change( event, dom ) {
if ( this.isValid( dom.value ) ) {
const obj = {};
obj[ this.step ] = this.processValue( dom.value );
this.ruleModel.set( obj );
}
},
processValue( value ) {
value = `${this.$input.val()} ${this.$select.val()}`;
return value;
}
} );

View File

@@ -0,0 +1,135 @@
module.exports = require( './base' ).extend( {
template: 'form',
addNewRuleTemplate: 'add-new-rule',
addRuleView: null,
afterInitialize() {
this.listenTo( this.model.getRules(), 'add remove change', _.bind( this.rulesChanged, this ) );
this.listenTo( this.model.getRules(), 'add change', this.animateToBottom );
},
afterRender() {
this.$name = this.$( '#tvd-content-set-name' );
this.$rulesWrapper = this.$( '#tvd-content-set-rules' );
this.$addRuleWrapper = this.$( '#tvd-content-set-add-rule' );
this.$inner = this.$( '.tvd-content-set-inner' );
this.$saveButton = this.$( 'button' );
this.renderRules();
this.rulesChanged();
this.$name.on( 'input', () => {
if ( this.getTitle().length >= 1 ) {
this.$name.removeClass( 'tva-cset-error' );
}
} );
},
renderRules() {
this.$rulesWrapper.empty();
this.model.getRules().each( this.renderRule, this );
},
renderRule( ruleModel ) {
this.$rulesWrapper.append( ( new TD_SETS.Views.Rule( {
model: ruleModel,
collection: this.model.getRules(),
} ).render().$el ) )
},
rulesChanged() {
this.$addRuleWrapper.children().remove();
if ( this.allowAddNewRule() ) {
this.$addRuleWrapper.append( TVE_Dash.tpl( this.addNewRuleTemplate )() );
}
},
animateToBottom: function () {
this.$inner.animate( {
scrollTop: this.$inner[ 0 ].scrollTop + 1000
}, 150 );
},
addNewRule: function () {
const model = new TD_SETS.Models.Rule( {} );
this.model.getRules().add( model );
this.renderRule( model );
},
allowAddNewRule() {
let allow = true;
this.model.getRules().each( ruleModel => {
if ( ! ruleModel.isCompleted() ) {
allow = false;
}
} );
return allow;
},
getTitle() {
return this.$name.val().trim();
},
saveContentSet() {
const title = this.getTitle();
this.$( '.tva-cset-error' ).removeClass( 'tva-cset-error' );
this.$name.toggleClass( 'tva-cset-error', title.length === 0 );
if ( title.length === 0 ) {
setTimeout( () => {
this.$inner.animate( {scrollTop: 0}, 150 );
}, 200 );
TVE_Dash.err( 'Please add a name' );
return;
}
this.model.set( {'post_title': title}, {silent: true} );
if ( ! this.model.isValid() ) {
const changed = this.model.changedAttributes();
if ( changed && changed.post_title ) {
const previousAttributes = this.model.previousAttributes();
this.model.set( {'post_title': previousAttributes.post_title}, {silent: true} );
}
TVE_Dash.err( this.model.getValidationError() );
/**
* UI Validation:
*
* Highlight the empty fields
*/
this.$( 'input, select' ).each( ( index, elem ) => {
if ( elem.tagName.toLowerCase() === 'input' && elem.classList.contains( 'select2-search__field' ) ) {
const $select2 = jQuery( elem ).closest( '.tvd-content-set-rule-step' ).find( 'select' );
if ( $select2.val().length === 0 ) {
elem.classList.add( 'tva-cset-error' );
}
} else if ( ! elem.value ) {
elem.classList.add( 'tva-cset-error' );
}
} );
return;
}
this.$saveButton.addClass( 'disabled' );
TVE_Dash.showLoader();
this.model.save()
.done( response => {
this.collection.reset( response );
this.$saveButton.removeClass( 'disabled' );
TVE_Dash.hideLoader();
} )
.fail( r => {
if ( r.responseJSON && r.responseJSON.message ) {
TVE_Dash.err( 'Something went wrong: ' + r.responseJSON.message )
} else {
TVE_Dash.err( 'Something went wrong: ' + r.responseText )
}
this.$saveButton.removeClass( 'disabled' );
TVE_Dash.hideLoader();
} );
}
} );

View File

@@ -0,0 +1,43 @@
module.exports = require( './base' ).extend( {
className: 'tvd-content-set-item',
template: 'item',
/**
* Edit a content set callback
*
* Called from the UI
*/
edit: function () {
this.openModal( TD_SETS.Views.Modals.Edit, {
model: this.model,
collection: this.collection,
width: '810px',
className: 'tvd-modal tvd-content-set-edit'
} );
},
/**
* Deletes a content set callback
*
* Called from the UI
*/
delete: function () {
const confirmView = new TD_SETS.Views.Confirm( {
template: TVE_Dash.tpl( 'item-delete-confirmation' ),
className: 'tvd-content-set-delete-confirmation',
confirm: () => {
this.model.destroy( {
wait: true,
success: ( model, response ) => {
this.destroy().remove();
}
} );
},
cancel() {
this.remove();
}
} );
this.$el.append( confirmView.render().$el );
}
} );

View File

@@ -0,0 +1,80 @@
module.exports = require( './base' ).extend( {
template: 'list',
noItemsTemplate: 'tva-c-s-no-items',
noSearchItemsTemplate: 'tva-c-s-no-search-items',
pagination: null,
itemViews: {},
afterInitialize() {
this.listenTo( this.collection, 'reset', _.bind( this.renderList, this ) );
this.listenTo( this.collection, 'page-changed', _.bind( this.renderList, this ) );
this.listenTo( this.collection, 'remove', _.bind( () => {
const pageInfo = this.collection.pageInfo();
if ( pageInfo.currentPage > 1 && this.collection.paginated().toJSON().length === 0 ) {
this.collection.prev();
}
this.renderList();
this.pagination.render();
}, this ) );
},
afterRender() {
this.$list = this.$( '.tvd-content-sets-list' );
this.$pagination = this.$( '.tvd-content-sets-list-pagination' );
this.renderList();
this.renderPagination();
},
renderPagination() {
this.pagination = new TD_SETS.Views.Pagination( {
el: this.$pagination,
collection: this.collection,
} );
this.pagination.render();
},
filterSets( event, dom ) {
if ( dom.value.length ) {
this.collection.applyFilters( 'search', dom.value );
} else {
this.collection.resetFilters();
}
this.renderList();
this.pagination.toggle( ! this.collection.hasFilters() ).render();
},
renderList() {
this.$list.empty();
this.itemViews = {};
if ( this.collection.length === 0 ) {
return this.$list.html( TVE_Dash.tpl( this.noItemsTemplate ) );
}
const paginatedCollection = this.collection.paginated();
if ( this.collection.hasFilters() && paginatedCollection.size() === 0 ) {
return this.$list.html( TVE_Dash.tpl( this.noSearchItemsTemplate ) );
}
paginatedCollection.forEach( this.renderItem, this );
},
renderItem( model ) {
const view = new TD_SETS.Views.Item( {
model: model,
collection: this.collection,
} );
this.$list.append( view.render().$el );
this.itemViews[ model.get( 'ID' ) ] = view;
},
addSet() {
this.openModal( TD_SETS.Views.Modals.Edit, {
model: new TD_SETS.Models.Set( {} ),
collection: this.collection,
width: '810px',
className: 'tvd-modal tvd-content-set-edit'
} );
}
} );

View File

@@ -0,0 +1,35 @@
( function ( $ ) {
module.exports = TVE_Dash.views.Modal.extend( {
template: '',
events: {
'click .click': '_call',
'input .input': '_call',
'keyup .keyup': '_call',
'change .change': '_call',
},
/**
* Call method for specific events
*
* @param {Event} event
* @return {*} the result returned by the handler function
*/
_call( event ) {
const _method = event.currentTarget.dataset.fn;
if ( typeof this[ _method ] === 'function' ) {
return this[ _method ].call( this, event, event.currentTarget );
}
},
/**
* Called after the view has been render
*
* @return {this} fluent interface
*/
afterRender() {
return this;
},
} );
} )( jQuery );

View File

@@ -0,0 +1,14 @@
module.exports = require( './base' ).extend( {
template: 'modals/edit',
afterInitialize() {
this.listenTo( this.collection, 'reset', _.bind( this.close, this ) );
},
afterRender() {
this.$_content = this.$el;
this.$( '.tvd-content-set-form' ).html( ( new TD_SETS.Views.Form( {
model: this.model,
collection: this.collection
} ) ).render().$el );
},
} );

View File

@@ -0,0 +1,32 @@
module.exports = require( './base' ).extend( {
template: 'tva-c-s-pagination',
afterInitialize() {
this.listenTo( this.collection, 'reset', this.render );
this.listenTo( this.collection, 'page-changed', this.render );
},
/**
* Toggle Pagination
* @param {boolean} status
*/
toggle( status ) {
this.$el.toggle( !! status );
return this;
},
render() {
const pageInfo = this.collection.pageInfo();
this.$el.html( pageInfo.totalPages > 1 ? TVE_Dash.tpl( this.template )( pageInfo ) : '' );
},
/**
* Callback in case prev button is clicked
*/
previousPage() {
this.collection.prev().trigger( 'page-changed' );
},
/**
* Callback in case next button is clicked
*/
nextPage() {
this.collection.next().trigger( 'page-changed' );
},
} );

View File

@@ -0,0 +1,255 @@
module.exports = require( './base' ).extend( {
template: 'rule',
className: 'tvd-content-set-rule',
hiddenPostTypes: [ 'tvd_blog_page', 'tvd_search_result_page' ],
afterInitialize() {
this.listenTo( this.model, 'control-changed', _.bind( this.stepChanged, this ) )
this.listenTo( this.collection, 'add', _.bind( this.allowDelete, this ) );
this.listenTo( this.collection, 'remove', _.bind( this.allowDelete, this ) );
},
clearSteps() {
if ( this.stepInstances ) {
this.stepInstances.forEach( inst => {
inst.off( 'all' );
inst.destroy();
} );
}
this.stepInstances = [];
},
afterRender() {
this.$ruleWrapper = this.$( '.tvd-rule-holder' ).empty();
this.$deleteRuleWrapper = this.$( '.tvd-content-set-delete-rule-wrapper' );
this.clearSteps();
if ( this.model.isEmpty() ) {
this.computeNextStep();
} else {
this.model.getSteps().forEach( step => {
if ( this.model.get( step ).length > 0 ) {
this.addStep( step );
}
} );
this.computeNextStep();
}
this.allowDelete();
},
stepChanged( stepChanged, stepChangedValue ) {
this.clearSteps();
this.$ruleWrapper.empty();
const removeStepFromIndex = this.getStepIndex( stepChanged ) + 1;
if ( this.getStepsLength() !== removeStepFromIndex ) {
for ( let i = removeStepFromIndex; i < this.getStepsLength(); i ++ ) {
const step = this.model.getSteps()[ i ];
if ( this.model.get( step ).toString().length ) {
const obj = {};
obj[ step ] = '';
this.model.set( obj, {silent: true} );
}
}
}
this.model.set( this.getRuleModelParams( stepChanged, stepChangedValue ) );
this.model.getSteps().forEach( step => {
if ( this.model.get( step ).length > 0 ) {
this.addStep( step );
}
} );
this.computeNextStep( 'field' === stepChanged && this.getNextStep().length );
},
/**
* Do some particular things here
* Based on the step, return the proper params
*
* @param {string} step
* @param {string} value
* @return {Object}
*/
getRuleModelParams( step, value ) {
const params = {};
params[ step ] = value;
if ( step === 'content' ) {
if ( [ 'tvd_search_result_page', 'tvd_blog_page' ].includes( params.content ) ) {
params.content_type = params.content;
params.field = - 1;
} else if ( params.content === 'archive' ) {
params.content_type = params.content;
} else {
params.content_type = TD_SETS.post_types.includes( value ) ? 'post' : 'term';
}
}
if ( step === 'field' ) {
if ( params.field === 'title' && this.model.get( 'content_type' ) === 'archive' ) {
params.content_type = 'term';
params.content = 'category';
}
if ( params.field === 'author' && [ 'archive', 'term' ].includes( this.model.get( 'content_type' ) ) ) {
params.content_type = 'archive';
params.content = 'archive';
}
}
return params
},
/**
* Computes the next step in building the rule
*
* @param {boolean} triggerChange
*/
computeNextStep( triggerChange = false ) {
const nextStep = this.getNextStep();
if ( nextStep.length ) {
this.addStep( this.getNextStep(), triggerChange );
}
},
getStepIndex( step ) {
return this.model.getSteps().indexOf( step );
},
getStepsLength() {
return this.model.getSteps().length;
},
/**
* Returns the next step in the rule
*
* @return {string}
*/
getNextStep() {
let nextStep = '';
if ( this.forceCompleteSteps() ) {
return nextStep;
}
this.model.getSteps().some( step => {
if ( this.model.get( step ).length === 0 ) {
nextStep = step;
return true;
}
} );
return nextStep;
},
/**
* Force complete steps
* Ex: when the field dropdown has "ALL" in it
*
* @return {boolean}
*/
forceCompleteSteps() {
return ( this.model.get( 'field' ) && parseInt( this.model.get( 'field' ) ) === - 1 ) || this.hiddenPostTypes.includes( this.model.get( 'content' ) );
},
/**
* Add a new step in the rule
*
* @param {string} step
* @param {boolean} triggerChange
*/
addStep( step, triggerChange = false ) {
const View = this.getStepView( step );
const instance = new View( {
ruleModel: this.model,
step,
model: new Backbone.Model( {
options: this.getStepOptions( step ),
trigger_change: triggerChange,
} )
} );
if ( step === 'content' && this.model.get( 'content' ) === 'category' ) {
instance.getStoredValue = function () {
if ( this.ruleModel.get( 'content_type' ) === 'term' && this.ruleModel.get( 'content' ) === 'category' ) {
return 'archive';
}
return this.ruleModel.get( this.step );
}
}
this.stepInstances.push( instance );
this.$ruleWrapper.append( instance.render().$el );
},
/**
* Do some particular things
*
* @param {string} step
*
* @return {*}
*/
getStepOptions( step ) {
let options = TD_SETS.options.general[ step ];
if ( step === 'field' && Array.isArray( TD_SETS.options.exceptions[ this.model.get( 'content' ) ] ) ) {
options = TD_SETS.options.exceptions[ this.model.get( 'content' ) ];
} else if ( step === 'operator' && Array.isArray( TD_SETS.options.exceptions[ this.model.get( 'field' ) ] ) ) {
options = TD_SETS.options.exceptions[ this.model.get( 'field' ) ];
}
return options;
},
/**
* Returns the Step View
*
* @param {string} step
*
* @return {Backbone.View}
*/
getStepView( step ) {
let View = require( './base' );
switch ( step ) {
case 'content':
case 'field':
case 'operator':
View = require( `./controls/select` );
break;
case 'value':
if ( this.model.get( 'field' ) === TD_SETS.fields.published_date ) {
if ( this.model.get( 'operator' ) === TD_SETS.operators.within_last ) {
View = require( `./controls/within-the-last` );
} else {
View = require( `./controls/date-picker` );
}
} else {
View = require( `./controls/select-multiple` );
}
default:
break;
}
return View;
},
allowDelete() {
this.$deleteRuleWrapper.toggle( this.collection.length > 1 );
},
deleteRule() {
const confirmView = new TD_SETS.Views.Confirm( {
template: TVE_Dash.tpl( 'rule-delete-confirmation' ),
className: 'tvd-content-set-delete-confirmation',
confirm: () => {
this.model.destroy();
this.destroy().remove();
},
cancel() {
this.remove();
}
} );
this.$el.append( confirmView.render().$el );
}
} );

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
!function(e){e((function(){TD.matches=new Backbone.Collection(TD.matches||[]),TD.sets=new Backbone.Collection(TD.sets||[]),new t({el:e("#tvd-contents-sets")})}));const t=Backbone.View.extend({events:{"click .removeMatchedTag":e=>{const t=parseInt(e.currentTarget.parentNode.getAttribute("data-id")),n=TD.matches.findWhere({id:t});n&&TD.matches.remove(n)}},initialize:function(t){e.extend(this,!0,t),this.listenTo(TD.matches,"add remove",this.renderMatchedTags),this.$autocomplete=this.$("#tvd-content-sets-autocomplete"),this.$matchedWrapper=this.$("#tvd-matched-content-sets"),this.renderMatchedTags()},renderMatchedTags:function(){this.$matchedWrapper.empty();const e=[];TD.matches.each((t=>{this.$matchedWrapper.append(`<div data-id="${t.get("id")}"><span>${t.get("text")}</span><span class="removeMatchedTag"></span></div>`),e.push(t.get("id"))})),this.bindAutocomplete(),e.length&&this.$matchedWrapper.append(`<input type="hidden" name="tvd_matched_content_sets_id" value="${e.toString()}" />`)},_escape:function(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},bindAutocomplete:function(){this.$autocomplete.data("autocomplete")&&(this.$autocomplete.autocomplete("destroy"),this.$autocomplete.removeData("autocomplete")),this.$autocomplete.autocomplete({appendTo:this.$autocomplete.parent(),minLength:0,classes:{"ui-autocomplete":"tvd-content-sets-dropdown"},source:(t,n)=>{const a=new RegExp(this._escape(t.term),"i"),s=TD.sets.toJSON().filter((e=>void 0===TD.matches.findWhere({id:e.id}))),o=0===t.term.trim().length?s:e.grep(s,(function(e){return a.test(e.text)}));n(e.map(o,(function(e){return{label:e.text,key:e.id}})))},select:(e,t)=>{this.$autocomplete.val("");const n=TD.sets.findWhere({id:t.item.key});return n&&TD.matches.add(n.clone()),!1}})}})}(jQuery);

View File

@@ -0,0 +1,203 @@
var TVD_SS = TVD_SS || {};
// phpcs:disable
( function ( $ ) {
var userDataWithLink = [ 'username', 'first_name', 'last_name' ];
if ( typeof TVE !== 'undefined' ) {
TVE.add_filter( 'tcb.inline_shortcodes.insert', tvdShapeShortcode );
TVE.add_filter( 'tve.froala.shortcode.init', tvdBackwardsCompat );
TVE.add_filter( 'tve.shortcode.options.html', tvdShortcodeHtmlOptions );
TVE.add_action( 'tcb.froala.after_shortcode_select', tvdShortcodeSelect );
TVE.add_filter( 'tcb.inline_shortcodes.shortcode_value', tvdBeforeInsert );
}
/**
* Change the content on insert of inline-shortcode
* @param content - to be inserted
* @param shortcodeData
* @returns {string}
*/
function tvdBeforeInsert( content, shortcodeData ) {
/* wrap the content so it behaves like a link */
if ( shortcodeData.key === 'thrv_dynamic_data_user' && userDataWithLink.includes( shortcodeData.extra_key ) ) {
const link = shortcodeData.configOptions.find( opt => opt.key === 'link' );
if ( link && link.value === '1' ) {
content = '<a href="#">' + content + '</a>'
}
} else if ( shortcodeData.key === 'thrive_global_fields' ) {
if ( ! content.includes( 'thrive-shortcode-notice' ) ) {
const call = shortcodeData.configOptions.find( opt => opt.key === 'enable_call' );
const mail = shortcodeData.configOptions.find( opt => opt.key === 'enable_email' );
if ( call && call.value === '1' ) {
content = `<a href="tel:${content}">${content}</a>`
} else if ( mail && mail.value === '1' ) {
content = `<a href="mailto:${content}">${content}</a>`
}
}
}
return content;
}
/**
* Change the select template for Global fields
* @param html
* @param key
* @returns {*}
*/
function tvdShortcodeHtmlOptions( html, key ) {
if ( key === 'Global fields' ) {
html = TVE.tpl( 'inline/shortcodes/global-fields-options' )( {
shortcodes: TVE.CONST.inline_shortcodes[ key ]
} );
}
return html;
}
/**
* Custom handling for global fields inline shortcodes select
* @param $FE
* @param data
*/
function tvdShortcodeSelect( $FE, data ) {
if ( data && data.selectedData ) {
var selectedData = data.selectedData;
if ( selectedData.key === 'thrive_global_fields' ) {
var selectValue;
if ( ! selectedData.configOptions ) {
selectValue = $FE.find( '#fr-dropdown-shortcode-list' ).find( 'option:selected' ).attr( 'data-field-value' );
} else {
selectValue = selectedData.configOptions.find( function ( object ) {
return object.key === 'id';
} ).value;
/* Set the proper value for the first select on shortcode is already inserted */
$FE.find( `#fr-dropdown-shortcode-list option[data-field-value="${selectValue}"]` ).prop( 'selected', true );
}
/* hide the actual shortcode select but set the new value to make sure that shortcode is properly generated */
$FE.find( '#fr-dropdown-list-id' ).val( selectValue ).trigger( 'change' ).hide();
$FE.find( 'label[for="fr-dropdown-list-id"]' ).hide();
}
}
}
/**
* Replace old username, first_name, last_name shortcode with the new ones and make sure the options set are kept
* @param {HTMLElement} shortcode
* @returns {HTMLElement}
*/
function tvdBackwardsCompat( shortcode ) {
var shortcodeName = shortcode.getAttribute( 'data-shortcode' ),
compatShortcode,
cfg = {
key: 'thrv_dynamic_data_user'
};
if ( [ 'tcb_username_field', 'tcb_first_name_field', 'tcb_last_name_field' ].includes( shortcodeName ) ) {
shortcode.setAttribute( 'data-attr-link', shortcode.getAttribute( 'data-attr-link_to_profile' ) );
shortcode.setAttribute( 'data-attr-default', shortcode.getAttribute( 'data-attr-text_not_logged' ) );
shortcode.removeAttribute( 'data-attr-link_to_profile' );
shortcode.removeAttribute( 'data-attr-text_not_logged' );
switch ( shortcodeName ) {
case 'tcb_username_field':
cfg.extra_key = 'username';
break;
case 'tcb_first_name_field':
cfg.extra_key = 'first_name';
break;
case 'tcb_last_name_field':
cfg.extra_key = 'last_name';
break;
default:
break;
}
shortcode.setAttribute( 'data-attr-id', cfg.extra_key );
shortcode.setAttribute( 'data-extra_key', cfg.extra_key );
compatShortcode = TVE.inlineShortcodeFn.getShortcodeByValue( cfg );
if ( compatShortcode ) {
shortcode.setAttribute( 'data-shortcode', compatShortcode.config.value );
shortcode.setAttribute( 'data-shortcode-name', compatShortcode.config.option );
shortcode.innerText = compatShortcode.config.input.id.real_data[ cfg.extra_key ];
}
}
return shortcode;
}
/**
* Before shortcode insertion we do some processing of the data which will later shape the shortcode element ( shortcodeData structure can be seen bellow )
*
* @param shortcodeData
* @returns {*}
*/
// shortcodeData = {
// key: shortcode_key,
// extra_key: shortcode_extra_key,
// name: name,
// shortcodeName: name,
// class: SHORTCODE_CLASS,
// content_class: SHORTCODE_CONTENT_CLASS,
// configOptions: [ )
// { )
// key: '', ) used for inputs that require further configuration
// value: '', ) these will generate inputs inside the froala shortcode dropdown
// } )
// ] )
// options: [ ]
// { ]
// key: '', ] used for additional information passed through the shortcode itself
// value: '', ] these don't do much but will b part of the final shortcode structure
// } ]
// ] ]
// };
function tvdShapeShortcode( shortcodeData ) {
var shortcode, name, shortcodeName;
_.each( TVE.CONST.inline_shortcodes, function ( group, group_name ) {
if ( ! shortcode ) {
shortcode = group.find( function ( item ) {
return shortcodeData.extra_key && item.extra_param === shortcodeData.extra_key;
} );
}
} );
if ( shortcode ) {
shortcodeName = shortcode.input.id.value[ shortcodeData.configOptions.find( function ( item ) {
return item.key === 'id';
} ).value ];
if ( shortcodeName ) {
shortcodeData.shortcodeName = '[' + shortcodeData.shortcodeName + '] ' + shortcodeName;
shortcodeData.name = shortcodeData.shortcodeName;
}
name = shortcode.input.id.real_data[ shortcodeData.configOptions.find( function ( item ) {
return item.key === 'id';
} ).value ];
if ( name ) {
shortcodeData.name = name;
}
var multiline = shortcodeData.configOptions.find( function ( item ) {
return item.key === 'multiline';
} );
if ( multiline && multiline.value ) {
shortcodeData.name = shortcodeData.name.split( ',' ).join( '<br/>' );
}
var defaultData = shortcodeData.configOptions.filter( opt => opt.key === 'default' )[ 0 ];
if ( defaultData && defaultData.value &&
( shortcodeData.key === 'thrv_dynamic_data_request' /* url querystrings cookie ...*/
|| ( shortcodeData.key === 'thrv_dynamic_data_user' && shortcodeData.name.toLowerCase().includes( 'wordpress' ) ) ) /* user data not defined*/
) {
shortcodeData.name = defaultData.value;
}
}
return shortcodeData;
}
} )( jQuery );

View File

@@ -0,0 +1,40 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
final class TVD_Smart_Const {
/**
* Rest namespage
*
*/
const REST_NAMESPACE = 'tss/v1';
/**
* Smart Site url with appended file if passed as parameter
*
* @param string $file
*
* @return string
*/
public static function url( $file = '' ) {
return untrailingslashit( TVE_DASH_URL ) . '/inc/smart-site' . ( ! empty( $file ) ? '/' : '' ) . ltrim( $file, '\\/' );
}
/**
* Smart Site path with appended file if passed as parameter
*
* @param string $file
*
* @return string
*/
public static function path( $file = '' ) {
return untrailingslashit( plugin_dir_path( __FILE__ ) ) . ( ! empty( $file ) ? '/' : '' ) . ltrim( $file, '\\/' );
}
}

View File

@@ -0,0 +1,402 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
use function TVD\Cache\content_set_cache;
use function TVD\Cache\meta_cache;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TVD_Content_Sets
*
* @project: thrive-dashboard
*/
class TVD_Content_Sets {
/**
* Nonce that should be checked for meta boxes request
*/
const META_BOX_NONCE = 'thrive-dashboard-sets-meta-box-nonce';
/**
* Singleton
*/
use TD_Singleton;
/**
* TVD_Content_Sets constructor.
*/
private function __construct() {
//Maybe do something like on smart site add check before enqueue & output templates
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'admin_print_footer_scripts', array( $this, 'backbone_templates' ) );
add_action( 'rest_api_init', array( $this, 'admin_create_rest_routes' ) );
/* cache-related actions */
add_action( 'save_post', array( $this, 'on_save_post_clear_cache' ), 10, 2 );
add_action( 'saved_term', array( $this, 'on_save_term_clear_cache' ) );
add_action( 'delete_post', array( $this, 'on_delete_post_clear_cache' ), 10, 2 );
if ( $this->show_ui() ) {
add_action( 'wp_after_insert_post', array( $this, 'after_save_post' ), 10, 2 );
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
add_action( 'admin_print_scripts-post-new.php', array( $this, 'edit_post_admin_script' ) );
add_action( 'admin_print_scripts-post.php', array( $this, 'edit_post_admin_script' ) );
}
require_once __DIR__ . '/content-set/class-utils.php';
require_once __DIR__ . '/content-set/class-set.php';
require_once __DIR__ . '/content-set/class-rule.php';
require_once __DIR__ . '/content-set/class-post-rule.php';
require_once __DIR__ . '/content-set/class-term-rule.php';
require_once __DIR__ . '/content-set/class-blog-rule.php';
require_once __DIR__ . '/content-set/class-search-result-rule.php';
require_once __DIR__ . '/content-set/class-archive-rule.php';
\TVD\Content_Sets\Set::init();
}
/**
* Adds the admin meta boxes
*/
public function add_meta_boxes() {
add_meta_box(
'tvd_content_sets',
esc_html__( 'Thrive Content Sets', 'thrive-dash' ),
array( $this, 'content_sets_meta_box' ),
null,
'advanced',
'high'
);
}
/**
* Callback for adding meta boxes for content sets
*
* @return mixed
*/
public function content_sets_meta_box() {
return require dirname( __FILE__ ) . '/meta-boxes/content-sets.php';
}
/**
* Edit post - scripts + styles
*/
public function edit_post_admin_script() {
$matches = \TVD\Content_Sets\Set::get_items_that_static_match( get_post() );
tve_dash_enqueue_style( 'thrive-admin-dashboard-edit-post', TVD_Smart_Const::url( 'assets/admin/css/styles-edit-post.css' ) );
tve_dash_enqueue_script( 'thrive-admin-dashboard-edit-post', TVD_Smart_Const::url( 'assets/admin/js/dist/edit-post.min.js' ), array( 'jquery' ), false, true );
wp_localize_script( 'thrive-admin-dashboard-edit-post', 'TD', array(
'sets' => \TVD\Content_Sets\Set::get_items_for_dropdown(),
'matches' => \TVD\Content_Sets\Set::get_items_for_dropdown( $matches ),
) );
}
/**
* Enqueue Content Sets scripts & styles
*/
public function enqueue_scripts() {
if ( $this->allow_enqueue_scripts( tve_get_current_screen_key() ) ) {
tve_dash_enqueue_style( 'tvd-content-sets-admin', TVD_Smart_Const::url( 'assets/admin/css/styles-content-sets.css' ) );
tve_dash_enqueue_script( 'tvd-content-sets-admin', TVD_Smart_Const::url( 'assets/admin/js/dist/content-sets-admin.min.js' ), array(
'jquery',
'backbone',
'tve-dash-main-js',
), false, true );
$public_post_types = \TVD\Content_Sets\Post_Rule::get_content_types();
wp_localize_script( 'tvd-content-sets-admin', 'TD_SETS', array(
'routes' => array(
'base' => get_rest_url() . 'tss/v1/content-sets',
),
'post_types' => array_keys( $public_post_types ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'fields' => array(
'title' => \TVD\Content_Sets\Rule::FIELD_TITLE,
'tag' => \TVD\Content_Sets\Rule::FIELD_TAG,
'category' => \TVD\Content_Sets\Rule::FIELD_CATEGORY,
'published_date' => \TVD\Content_Sets\Rule::FIELD_PUBLISHED_DATE,
'topic' => \TVD\Content_Sets\Rule::FIELD_TOPIC,
'difficulty' => \TVD\Content_Sets\Rule::FIELD_DIFFICULTY,
'label' => \TVD\Content_Sets\Rule::FIELD_LABEL,
'author' => \TVD\Content_Sets\Rule::FIELD_AUTHOR,
),
'operators' => array(
'is' => \TVD\Content_Sets\Rule::OPERATOR_IS,
'not_is' => \TVD\Content_Sets\Rule::OPERATOR_NOT_IS,
'grater_equal' => \TVD\Content_Sets\Rule::OPERATOR_GRATER_EQUAL,
'lower_equal' => \TVD\Content_Sets\Rule::OPERATOR_LOWER_EQUAL,
'within_last' => \TVD\Content_Sets\Rule::OPERATOR_WITHIN_LAST,
),
'options' => array(
'general' => array(
'content' => $this->localize_content_values( $public_post_types ),
'field' => array(
array(
'value' => '',
'disabled' => true,
'label' => __( 'Select your field or taxonomy', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_ALL,
'label' => __( 'All', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_TITLE,
'label' => __( 'Title', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_TAG,
'label' => __( 'Tag', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_CATEGORY,
'label' => __( 'Category', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_PUBLISHED_DATE,
'label' => __( 'Published', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_AUTHOR,
'label' => __( 'Author', 'thrive-dash' ),
),
),
'operator' => array(
array(
'value' => '',
'disabled' => true,
'label' => __( 'Choose your condition', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::OPERATOR_IS,
'label' => __( 'Is', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::OPERATOR_NOT_IS,
'label' => __( 'Is not', 'thrive-dash' ),
),
),
),
/**
* Allow Protected files to hook here and modify the exceptions list
*
* @param array $exceptions
*/
'exceptions' => apply_filters( 'tvd_content_sets_get_content_types_exceptions', array(
'tva_courses' => array(
array(
'value' => '',
'disabled' => true,
'label' => __( 'Select your field or taxonomy', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_ALL,
'label' => __( 'All', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_TITLE,
'label' => __( 'Title', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_TOPIC,
'label' => __( 'Topic', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_DIFFICULTY,
'label' => __( 'Difficulty', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_LABEL,
'label' => __( 'Label', 'thrive-dash' ),
),
),
'published_date' => array(
array(
'value' => '',
'disabled' => true,
'label' => __( 'Choose your condition', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::OPERATOR_LOWER_EQUAL,
'label' => __( 'On or before', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::OPERATOR_GRATER_EQUAL,
'label' => __( 'On or after', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::OPERATOR_WITHIN_LAST,
'label' => __( 'Within the last', 'thrive-dash' ),
),
),
) ),
),
'sets' => \TVD\Content_Sets\Set::get_items(),
) );
}
}
/**
* Add backbone templates
*/
public function backbone_templates() {
if ( $this->allow_enqueue_scripts( tve_get_current_screen_key() ) ) {
$templates = tve_dash_get_backbone_templates( TVD_Smart_Const::path( 'views/admin/content-sets-templates' ), 'content-sets-templates' );
tve_dash_output_backbone_templates( $templates );
}
}
/**
* Register the rest route
*/
public function admin_create_rest_routes() {
require_once __DIR__ . '/endpoints/class-tvd-content-sets-controller.php';
$controller = new TVD_Content_Sets_Controller();
$controller->register_routes();
}
/**
* @param array $public_post_types
*
* @return array[]
*/
private function localize_content_values( $public_post_types = array() ) {
$values = array(
array(
'value' => '',
'disabled' => true,
'label' => __( 'Choose content type', 'thrive-dash' ),
),
);
foreach ( $public_post_types as $key => $name ) {
$values[] = array(
'value' => $key,
'label' => $name,
);
}
$values[] = array(
'value' => 'tva_courses',
'label' => __( 'Apprentice Course', 'thrive-dash' ),
);
$values[] = array(
'value' => \TVD\Content_Sets\Rule::HIDDEN_POST_TYPE_BLOG,
'label' => __( 'Blog Page', 'thrive-dash' ),
);
$values[] = array(
'value' => \TVD\Content_Sets\Rule::HIDDEN_POST_TYPE_SEARCH_RESULTS,
'label' => __( 'Search Results Page', 'thrive-dash' ),
);
$values[] = array(
'value' => 'archive',
'label' => __( 'Archive', 'thrive-dash' ),
);
return $values;
}
/**
* Checks if the system allows to include the content sets scripts
* Calls a filter that is implemented in other plugins (ex: Thrive Apprentice)
*
* @param string $screen_id
*
* @return boolean
*/
private function allow_enqueue_scripts( $screen_id ) {
return apply_filters( 'tvd_content_sets_allow_enqueue_scripts', $screen_id === 'admin_page_tve_dash_smart_site', $screen_id );
}
/**
* When a post is saved, clear the content sets cache associated with it
*
* @param int $post_id
* @param \WP_Post $post
*/
public function on_save_post_clear_cache( $post_id, $post ) {
if ( $post->post_type === \TVD\Content_Sets\Set::POST_TYPE ) {
// need to clear everything
content_set_cache( 'term' )->clear(); // delete any stored term meta
content_set_cache( 'post' )->clear(); // delete any stored post meta
} else {
content_set_cache( $post )->clear( $post_id );
}
}
/**
* When a term is saved, clear the content sets cache associated with it
*
* @param int $term_id
*/
public function on_save_term_clear_cache( $term_id ) {
content_set_cache( 'term' )->clear( $term_id ); // delete any stored term meta
}
/**
* When a content set is deleted, clear content set cache
*
* @param int $post_id
* @param \WP_Post $post
*/
public function on_delete_post_clear_cache( $post_id, $post ) {
if ( $post->post_type === \TVD\Content_Sets\Set::POST_TYPE ) {
content_set_cache( 'term' )->clear();
content_set_cache( 'post' )->clear();
}
}
/**
* After the post is saved, we update the content sets that have been modified from /wp-admin/post.php UI
*
* @param int $post_id
* @param WP_Post $post
*/
public function after_save_post( $post_id, $post ) {
// Security check: verify meta box nonce
if ( ! isset( $_POST['tvd_content_sets_meta_box'] ) || ! wp_verify_nonce( $_POST['tvd_content_sets_meta_box'], self::META_BOX_NONCE ) ) {
return;
}
$content_sets_ids = isset( $_POST['tvd_matched_content_sets_id'] ) ? $_POST['tvd_matched_content_sets_id'] : '';
\TVD\Content_Sets\Set::toggle_object_to_set_static_rules( $post, array_filter( explode( ',', $content_sets_ids ) ) );
}
/**
* Returns true if the system can show the content sets UI
*
* @return bool
*/
public function show_ui() {
return defined( 'TVD_CONTENT_SETS_SHOW_UI' ) && TVD_CONTENT_SETS_SHOW_UI;
}
}
/**
* @return TVD_Content_Sets
*/
function tvd_content_sets() {
return TVD_Content_Sets::get_instance();
}
add_action( 'init', 'tvd_content_sets', 8 );

View File

@@ -0,0 +1,737 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class TVD_Global_Shortcodes
*/
class TVD_Global_Shortcodes {
public static $dynamic_shortcodes = array(
'thrive_global_shortcode_url' => 'global_shortcode_url',
'thrv_dynamic_data_request' => 'request_data_shortcode',
'thrv_dynamic_data_date' => 'date_shortcode',
'thrv_dynamic_data_content' => 'content_shortcode',
'thrv_dynamic_data_user' => 'user_data_shortcode',
'thrv_dynamic_data_source' => 'source_shortcode',
'thrv_dynamic_data_user_acf' => 'acf_user_field',
);
public function __construct() {
add_filter( 'tcb_content_allowed_shortcodes', array( $this, 'allowed_shortcodes' ) );
add_filter( 'tcb_dynamiclink_data', array( $this, 'links_shortcodes' ) );
add_filter( 'tcb_inline_shortcodes', array( $this, 'tcb_inline_shortcodes' ), 99, 1 );
$this->add_shortcodes();
}
public function add_shortcodes() {
foreach ( static::$dynamic_shortcodes as $shortcode => $func ) {
$function = array( $this, $func );
add_shortcode( $shortcode, static function ( $attr ) use ( $function ) {
$output = call_user_func_array( $function, func_get_args() );
return TVD_Global_Shortcodes::maybe_link_wrap( $output, $attr );
} );
}
}
/**
* Checks if shortcode content needs to be wrapped in a link (if a custom json-encoded attribute exists in the shortcode)
*
* @param string $shortcode_content
* @param array $attr
*
* @return string
*/
public static function maybe_link_wrap( $shortcode_content, $attr ) {
/**
* If a static link is detected in config, we need to wrap $content in that link (only if a link doesn't already exist in $shortcode_content ..)
*/
if ( $shortcode_content && ! empty( $attr['static-link'] ) ) {
$link_attr = json_decode( htmlspecialchars_decode( $attr['static-link'] ), true );
if ( strpos( $shortcode_content, '</a>' ) === false ) {
if ( ! empty( $link_attr ) && ! empty( $link_attr['href'] ) ) {
/* replacement for shortcode open "[" */
if ( strpos( $link_attr['href'], '((' ) === 0 ) {
$link_attr['href'] = str_replace( array( '((', '))' ), array( '[', ']' ), $link_attr['href'] );
$link_attr['href'] = do_shortcode( $link_attr['href'] );
}
/* put the brackets back in the title attribute if they were replaced before */
if ( ! empty( $link_attr['title'] ) && strpos( $link_attr['title'], '((' ) !== false ) {
$link_attr['title'] = str_replace( array( '((', '))' ), array(
'[',
']',
), $link_attr['title'] );
}
$attributes = array();
foreach ( $link_attr as $attr_name => $value ) {
$attributes[] = ( $attr_name === 'className' ? 'class' : $attr_name ) . '="' . esc_attr( $value ) . '"';
}
$shortcode_content = '<a ' . implode( ' ', $attributes ) . '>' . $shortcode_content . '</a>';
}
} elseif ( extension_loaded( 'dom' ) && isset( $link_attr['className'] ) && function_exists( 'mb_convert_encoding' ) ) {
/**
* For elements already containing a link just add the old classes (e.g. global styles)
*/
$dom = new DOMDocument;
@$dom->loadHTML( mb_convert_encoding( $shortcode_content, 'HTML-ENTITIES', 'UTF-8' ) );
$dom->encoding = 'UTF-8';
$items = $dom->getElementsByTagName( 'a' );
for ( $i = 0; $i < $items->length; $i ++ ) {
$link = $items->item( $i );
if ( $link ) {
$link->setAttribute( 'class', $link_attr['className'] );
if ( isset( $link_attr['data-css'] ) ) {
$link->setAttribute( 'data-css', $link_attr['data-css'] );
}
}
}
$body = $dom->getElementsByTagName( 'body' );
if ( $body && $body->length ) {
$shortcode_content = '';
foreach ( $body[0]->childNodes as $child ) {
$shortcode_content .= $dom->saveHTML( $child );
}
} else {
$shortcode_content = $dom->saveHTML();
}
}
}
return $shortcode_content;
}
public function tcb_inline_shortcodes( $shortcodes ) {
return array_merge_recursive( TVD_Global_Shortcodes::get_inline_shortcodes(), $shortcodes );
}
public static function get_inline_shortcodes() {
$inline_shortcodes = array();
$shortcodes_without_params = array(
/* Content */
'the_ID' => array(
'name' => __( 'Post ID', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_content',
'fn' => 'content_shortcode',
'group' => 'Content',
),
'the_title' => array(
'name' => __( 'Post title', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_content',
'fn' => 'content_shortcode',
'group' => 'Content',
),
'post_type' => array(
'name' => __( 'Post type', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_content',
'fn' => 'content_shortcode',
'group' => 'Content',
),
'permalink' => array(
'name' => __( 'Post URL', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_content',
'fn' => 'content_shortcode',
'group' => 'Content',
),
/* Time & date*/
'd M Y' => array(
'name' => esc_html__( 'Date (14 Aug 2029)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'd.n.Y' => array(
'name' => esc_html__( 'Date (14.8.2029)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'd-m-Y' => array(
'name' => esc_html__( 'Date (14-08-2029)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'd/m/Y' => array(
'name' => esc_html__( 'Date (14/08/2029)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'G:i:s' => array(
'name' => esc_html__( 'Time (23:59:59)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'G:i' => array(
'name' => esc_html__( 'Time (23:59)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'd' => array(
'name' => esc_html__( 'Day (0131)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'jS' => array(
'name' => esc_html__( 'Day (1st, 2nd, 15th - 31st)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'l' => array(
'name' => esc_html__( 'Day of the week (Monday)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'D' => array(
'name' => esc_html__( 'Day of the week (Mon)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'm' => array(
'name' => esc_html__( 'Month (01-12)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'F' => array(
'name' => esc_html__( 'Month (January - December)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'M' => array(
'name' => esc_html__( 'Month (Jan Dec)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'Y' => array(
'name' => esc_html__( 'Year (2029)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
);
$resources = array(
'get' => __( 'URL QueryString', 'thrive-dash' ),
'post' => __( 'POST variable', 'thrive-dash' ),
'cookie' => __( 'Cookie', 'thrive-dash' ),
);
$shortcodes_with_default = array(
/* User Data */
'username' => array(
'name' => __( 'WordPress username', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
'link' => __( 'Link to user profile', 'thrive-dash' ),
),
'user_email' => array(
'name' => __( 'WordPress user email', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'role' => array(
'name' => __( 'WordPress user role', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'first_name' => array(
'name' => __( 'WordPress user first name', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
'link' => __( 'Link to user profile', 'thrive-dash' ),
),
'last_name' => array(
'name' => __( 'WordPress user last name', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
'link' => __( 'Link to user profile', 'thrive-dash' ),
),
'nickname' => array(
'name' => __( 'WordPress user nickname', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'display_name' => array(
'name' => __( 'WordPress user public name', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'website' => array(
'name' => __( 'WordPress user website', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'user_bio' => array(
'name' => __( 'WordPress user bio', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'ip' => array(
'name' => __( 'IP', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'browser' => array(
'name' => __( 'Browser', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
/* Source */
'HTTP_REFERER' => array(
'name' => __( 'Referring URL', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_source',
'fn' => 'source_shortcode',
'group' => 'Source',
),
);
if ( tvd_has_external_fields_plugins() ) {
// if is acf plugin active, run through all of them and update the custom fields for the user
foreach ( tvd_get_acf_user_external_fields() as $field ) {
$shortcodes_with_default[ $field['name'] ] = array(
'name' => $field['label'],
'shortcode' => 'thrv_dynamic_data_user_acf',
'fn' => 'acf_user_field',
'group' => 'User data',
);
}
}
foreach ( $shortcodes_with_default as $key => $data ) {
$shortcode = array(
'name' => $data['name'],
'option' => $data['name'],
'value' => $data['shortcode'],
'extra_param' => $key,
'input' => array(
'default' => array(
'type' => 'input',
'label' => __( 'Default Value', 'thrive-dash' ),
'value' => '',
),
'id' => array(
'extra_options' => array(),
'real_data' => array(
$key => call_user_func( 'TVD_Global_Shortcodes::' . $data['fn'], array( 'id' => $key ) ),
),
'type' => 'hidden',
'value' => $key,
),
),
);
/* Support shortcodes link*/
if ( isset( $data['link'] ) ) {
$shortcode['input']['link'] = array(
'type' => 'checkbox',
'label' => $data['link'],
'value' => true,
);
}
$inline_shortcodes[ $data['group'] ][] = $shortcode;
}
foreach ( $shortcodes_without_params as $key => $data ) {
$shortcode = array(
'name' => $data['name'],
'option' => $data['name'],
'value' => $data['shortcode'],
'extra_param' => $key,
'input' => array(
'id' => array(
'extra_options' => array(),
'real_data' => array(
$key => call_user_func( 'TVD_Global_Shortcodes::' . $data['fn'], array( 'id' => $key ) ),
),
'type' => 'hidden',
'value' => $key,
),
),
);
$inline_shortcodes[ $data['group'] ][] = $shortcode;
}
foreach ( $resources as $key => $name ) {
$inline_shortcodes['Request data'][] = array(
'name' => $name,
'option' => $name,
'value' => 'thrv_dynamic_data_request',
'extra_param' => $key,
'input' => array(
'var_name' => array(
'type' => 'input',
'label' => __( 'Variable name', 'thrive-dash' ),
'value' => '',
),
'default' => array(
'type' => 'input',
'label' => __( 'Default Value', 'thrive-dash' ),
'value' => '',
),
'id' => array(
'extra_options' => array(),
'real_data' => array(
$key => TVD_Global_Shortcodes::request_data_shortcode( array(
'id' => $key,
'var_name' => '',
) ),
),
'type' => 'hidden',
'value' => $key,
),
),
);
}
return $inline_shortcodes;
}
/**
* Filter allowed shortcodes for tve_do_wp_shortcodes
*
* @param $shortcodes
*
* @return array
*/
public function allowed_shortcodes( $shortcodes ) {
return array_merge( $shortcodes, array_keys( TVD_Global_Shortcodes::$dynamic_shortcodes ) );
}
/**
* Add global shortcodes to be used in dynamic links
*
* @param $links
*
* @return mixed
*/
public function links_shortcodes( $links ) {
$global_links = array();
foreach ( $this->global_data() as $index => $value ) {
$value['id'] = $index;
$global_links[] = $value;
}
$links['Site'] = array(
'links' => array( $global_links ),
'shortcode' => 'thrive_global_shortcode_url',
);
return $links;
}
/**
* Global data related to the site
*
* @return array
*/
public function global_data() {
// phpcs:disable
return apply_filters( 'tvd_global_data', array(
array(
'name' => __( 'Homepage', 'thrive-dash' ),
'url' => get_home_url(),
'show' => true,
),
array(
'name' => __( 'Blog', 'thrive-dash' ),
'url' => get_option( 'page_for_posts' ) ? get_permalink( get_option( 'page_for_posts' ) ) : get_home_url(),
'show' => true,
),
array(
'name' => __( 'RSS Feed', 'thrive-dash' ),
'url' => get_home_url() . '/feed',
'show' => true,
),
array(
'name' => __( 'Login', 'thrive-dash' ),
'url' => wp_login_url(),
'show' => true,
),
array(
'name' => __( 'Logout', 'thrive-dash' ),
'url' => wp_logout_url(),
'show' => true,
),
) );
// phpcs:enable
}
/**
* Replace the shortcode with its content
*
* @param $args
*
* @return mixed|string
*/
public function global_shortcode_url( $args ) {
$data = '';
if ( isset( $args['id'] ) ) {
$groups = $this->global_data();
$id = (int) $args['id'];
$data = empty( $groups[ $id ] ) ? '' : $groups[ $id ]['url'];
}
if ( isset( $args['logout-redirect'] ) ) {
$data .= '&redirect_to=' . $args['logout-redirect'];
}
return $data;
}
/**
* Shortcode render for user data
*
* @param $args
*
* @return mixed|string
*/
public static function user_data_shortcode( $args ) {
$user_data = tve_current_user_data();
$value = '';
if ( isset( $args['id'] ) ) {
if ( $args['id'] === 'browser' ) {
$value = '<span class="tve-browser-data"></span >'; /* Replace this with JS because PHP get_browser doesnt work all the time */
}
if ( isset( $user_data[ $args['id'] ] ) ) {
$value = $user_data[ $args['id'] ];
if ( $args['id'] === 'website' ) {
/* create link with user website */
$value = sprintf( '<a href="%s" target="_blank">%s</a>', $value, $value );
} elseif ( isset( $args['link'] ) && $args['link'] === '1' ) {
$value = sprintf( '<a href="%s" target="_blank">%s</a>', $user_data['edit_url'], $value );
}
}
}
if ( empty( $value ) && isset( $args['default'] ) ) {
$value = $args['default'];
}
return $value;
}
/**
* Shortcode render for user data from acf
*
* @param $args
*
* @return mixed|string
*/
public static function acf_user_field( $args ) {
$value = '';
if ( tvd_has_external_fields_plugins() ) {
$value = get_field( $args['id'], 'user_' . get_current_user_id() );
if ( empty( $value ) && isset( $args['default'] ) ) {
$value = $args['default'];
}
}
return $value;
}
/**
* Shortcode render for post data
*
* @param $args
*
* @return mixed|string
*/
public static function content_shortcode( $args ) {
$value = '';
global $post;
if ( is_singular() || ( wp_doing_ajax() && ! empty( $post ) ) ) {
if ( isset( $args['id'] ) ) {
$func = "get_{$args['id']}";
if ( function_exists( $func ) ) {
$value = $func();
}
}
if ( empty( $value ) && isset( $args['default'] ) ) {
$value = $args['default'];
}
}
return $value;
}
public static function date_shortcode( $args ) {
/**
* The hour should be in 24h format
*/
$format = str_replace( 'g', 'G', $args['id'] );
if ( function_exists( 'wp_date' ) ) {
$result = wp_date( $format );
} else {
$result = date_i18n( $format );
}
return trim( $result );
}
/**
* Shortcode render for data from page source
*
* @param $args
*
* @return mixed|string
*/
public static function source_shortcode( $args ) {
$allowed = array( 'HTTP_REFERER' );
$value = '';
if ( isset( $args['id'], $_SERVER[ $args['id'] ] ) && in_array( $args['id'], $allowed, true ) ) {
$value = sanitize_text_field( $_SERVER[ $args['id'] ] );
}
if ( empty( $value ) && isset( $args['default'] ) ) {
$value = $args['default'];
}
return $value;
}
/**
* Try to get a value deep down from a variable
* e.g a[b][c]
*
* @param $original_key
* @param $params
*
* @return mixed|string
*/
public static function get_deep_value( $original_key, $params ) {
$original_key = str_replace( ']', '', $original_key );
$ref = $params;
foreach ( explode( '[', $original_key ) as $key ) {
if ( isset( $ref[ $key ] ) ) {
$ref = $ref[ $key ];
} else {
$ref = '';
}
}
return $ref;
}
/**
* Shortcode render for data from $_REQUEST
*
* @param array $args
* @param string $context
* @param string $tag
*
* @return mixed|string
*/
public static function request_data_shortcode( $args = array(), $context = '', $tag = '' ) {
$value = '';
$should_load_value = true;
if ( ! empty( $tag ) && function_exists( 'is_editor_page' ) && is_editor_page() ) {
//preserve shortcode in case someone adds it as dynamic link cuz why not
if ( empty( $args['inline'] ) ) {
$attributes = '';
foreach ( array( 'var_name', 'id', 'default', 'inline' ) as $key ) {
if ( ! empty( $args[ $key ] ) ) {
$attributes .= $key . '=' . $args[ $key ] . ' ';
}
}
return '[' . $tag . ' ' . trim( $attributes ) . ']';
}
$should_load_value = false;
}
if ( $should_load_value && ! empty( $args['var_name'] ) ) {
/**
* just in case the var_name has spaces check var_Name with underscore
* e.g test 01 comes as test_01
*/
$var_name = $args['var_name'];
$fallback_var = strpos( $var_name, ' ' ) !== false ? preg_replace( "/\s/", "_", $var_name ) : '';
if ( strpos( $var_name, '((' ) !== false ) {
$var_name = preg_replace( '#\\(\\(#', '[', $var_name );
$var_name = preg_replace( '#\\)\\)#', ']', $var_name );
}
$global = [];
switch ( $args['id'] ) {
case 'post':
$global = $_POST;
break;
case 'get':
$global = $_GET;
break;
case 'cookie':
$global = $_COOKIE;
break;
default:
break;
}
$value = isset( $global[ $var_name ] ) ? $global[ $var_name ] : '';
if ( empty( $value ) ) {
$value = static::get_deep_value( $var_name, $global );
}
if ( empty( $value ) && ! empty( $fallback_var ) ) {
$value = isset( $global[ $fallback_var ] ) ? $global[ $fallback_var ] : '';
}
if ( empty( $value ) ) {
$value = static::get_deep_value( $fallback_var, $global );
}
$value = sanitize_text_field( $value );
}
if ( empty( $value ) && isset( $args['default'] ) ) {
$value = $args['default'];
}
return esc_html( stripslashes( $value ) );
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Base class for extending the WP_REST_Controller
*/
class TVD_REST_Controller {
public static $version = 1;
public static $namespace = 'tss/v';
public $base = '';
/**
* Register the routes for the objects of the controller.
*/
public function register_routes() {}
}

View File

@@ -0,0 +1,781 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class TVD_Smart_DB
*/
class TVD_Smart_DB {
/**
* Groups table name
*
* @var string
*/
private $groups_table_name;
/**
* Fields table name
*
* @var string
*/
private $fields_table_name;
/**
* WordPress Database
*
* @var wpdb
*/
private $wpdb;
/**
* Default fields and data
*
* @var array
*/
private $default_groups;
/**
* Icons specific to type
*/
public static $icons;
/**
* Types of fields
*/
public static $types
= array(
'text' => 0,
'address' => 1,
'phone' => 2,
'email' => 3,
'link' => 4,
// 'location' => 5,
);
/**
* TVD_Smart_DB constructor.
*/
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
$this->groups_table_name = $this->wpdb->prefix . 'td_groups';
$this->fields_table_name = $this->wpdb->prefix . 'td_fields';
$this->default_groups = $this->groups();
static::$icons = $this->icons();
}
/**
* Default icons
*
* return array
*/
public function icons() {
return array(
static::$types['text'] => 'text-new',
static::$types['address'] => 'address-new',
static::$types['phone'] => 'phone-new',
static::$types['email'] => 'envelope-new',
static::$types['link'] => 'link-new',
// TVD_Smart_DB::$types['location'] => 'map-marker-solid',
);
}
/**
* Default types
*
* return array
*/
public static function field_types() {
$types = array();
foreach ( static::$types as $key => $type ) {
$types[ static::$types[ $key ] ] = array(
'name' => $key,
'icon' => static::field_icon( static::$types[ $key ] ),
'key' => static::$types[ $key ],
);
}
return $types;
}
/**
* Default fields and data
*
* return array
*/
public function groups() {
return array(
'Company' => array(
array(
'name' => 'Name',
'type' => static::$types['text'],
'identifier' => 'name',
),
array(
'name' => 'Address',
'type' => static::$types['address'],
'identifier' => 'addr',
),
array(
'name' => 'Phone number',
'type' => static::$types['phone'],
'data' => [ 'text' => 'Call', 'url' => '' ],
'identifier' => 'phone',
),
array(
'name' => 'Alternative phone number',
'type' => static::$types['phone'],
'data' => [ 'text' => 'Call', 'url' => '' ],
'identifier' => 'alt_phone',
),
array(
'name' => 'Email address',
'type' => static::$types['email'],
'data' => [ 'text' => 'Email', 'url' => '' ],
'identifier' => 'mail',
),
// array(
// 'name' => 'Map Location',
// 'type' => TVD_Smart_DB::$types['location'],
// ),
),
'Legal' => array(
array(
'name' => 'Privacy policy',
'type' => static::$types['link'],
'data' => array( 'text' => 'Privacy policy', 'url' => '' ),
'identifier' => 'priv',
),
array(
'name' => 'Disclaimer',
'type' => static::$types['link'],
'data' => array( 'text' => 'Disclaimer', 'url' => '' ),
'identifier' => 'disc',
),
array(
'name' => 'Terms and conditions',
'type' => static::$types['link'],
'data' => array( 'text' => 'Terms and conditions', 'url' => '' ),
'identifier' => 'toc',
),
array(
'name' => 'Contact',
'type' => static::$types['link'],
'data' => array( 'text' => 'Contact', 'url' => '' ),
'identifier' => 'contact',
),
),
'Social' => array(
array(
'name' => 'Facebook page',
'icon' => 'facebook-brands-new',
'type' => static::$types['link'],
'identifier' => 'fb',
),
array(
'name' => 'YouTube',
'icon' => 'youtube-brands-new',
'type' => static::$types['link'],
'identifier' => 'yt',
),
array(
'name' => 'LinkedIn',
'icon' => 'linkedin-brands-new',
'type' => static::$types['link'],
'identifier' => 'in',
),
array(
'name' => 'Pinterest',
'icon' => 'pinterest-brands-new',
'type' => static::$types['link'],
'identifier' => 'pin',
),
array(
'name' => 'Instagram',
'icon' => 'instagram-brands-new',
'type' => static::$types['link'],
'identifier' => 'ig',
),
array(
'name' => 'Xing',
'icon' => 'xing-brands-new',
'type' => static::$types['link'],
'identifier' => 'xing',
),
array(
'name' => 'X',
'icon' => 'x-brands-new',
'type' => static::$types['link'],
'identifier' => 't',
),
array(
'name' => 'TikTok',
'icon' => 'tiktok-brands-new',
'type' => static::$types['link'],
'identifier' => 'tiktok',
),
array(
'name' => 'Bluesky',
'icon' => 'bluesky-brands-new',
'type' => static::$types['link'],
'identifier' => 'bluesky',
),
),
);
}
/**
* Insert the default data in the db
*/
public function insert_default_data() {
/**
* We can't use the migration queries in the migration file because we have relationships, so we insert the data here
*/
$result = $this->wpdb->get_row( "SELECT `id` FROM $this->groups_table_name LIMIT 0,1", ARRAY_A );
/* if there's no entries in the table, this is the first ( 1.0.0 ) db update */
if ( empty( $result ) ) {
foreach ( $this->default_groups as $group => $fields ) {
/**
* Insert the group
*/
$result = $this->wpdb->insert(
$this->groups_table_name,
array(
'name' => $group,
'is_default' => 1,
),
array(
'%s',
)
);
$id = $this->wpdb->insert_id;
if ( $result ) {
/**
* Insert the fields
*/
foreach ( $fields as $field ) {
$this->wpdb->insert(
$this->fields_table_name,
array(
'name' => $field['name'],
'type' => $field['type'],
'identifier' => $field['identifier'],
'data' => empty( $field['data'] ) ? null : maybe_serialize( $field['data'] ),
'is_default' => 1,
'group_id' => $id,
),
array(
'%s',
'%d',
)
);
}
}
}
} else {
/*
* there are entries in the table => this is the 1.0.X -> 1.0.(X+1) db upgrade
* Insert 'Xing' and 'Twitter' manually, and populate the newly added 'identifier' column for each default field
*/
$xing_twitter_row = $this->wpdb->get_row( "SELECT * FROM $this->fields_table_name WHERE `identifier`='xing' OR `identifier`='t' LIMIT 0,1", ARRAY_A );
/* only continue if xing and twitter aren't already inserted */
if ( empty( $xing_twitter_row ) ) {
$this->insert_xing_and_twitter_default_fields();
$this->add_string_identifier_to_fields();
}
$tiktok_row = $this->wpdb->get_row( "SELECT * FROM $this->fields_table_name WHERE `identifier`='tiktok' LIMIT 0,1", ARRAY_A );
/* only continue if tiktok isn't inserted */
if ( empty( $tiktok_row ) ) {
$this->add_tiktok_field();
}
$bluesky_row = $this->wpdb->get_row( "SELECT * FROM $this->fields_table_name WHERE `identifier`='bluesky' LIMIT 0,1", ARRAY_A );
/* only continue if bluesky isn't inserted */
if ( empty( $bluesky_row ) ) {
$this->add_bluesky_field();
}
}
}
public function add_tiktok_field() {
$format = array(
'%s',
'%d',
);
$this->wpdb->insert(
$this->fields_table_name,
array(
'name' => 'TikTok',
'type' => static::$types['link'],
'identifier' => 'tiktok',
'is_default' => 1,
'group_id' => 3,
),
$format
);
}
public function add_bluesky_field() {
$format = array(
'%s',
'%d',
);
$this->wpdb->insert(
$this->fields_table_name,
array(
'name' => 'Bluesky',
'type' => static::$types['link'],
'identifier' => 'bluesky',
'is_default' => 1,
'group_id' => 3,
),
$format
);
}
/**
* Insert the xing and twitter social fields and add their IDs to the 'id - string identifier' map
*/
private function insert_xing_and_twitter_default_fields() {
$format = array(
'%s',
'%d',
);
$this->wpdb->insert(
$this->fields_table_name,
array(
'name' => 'Xing',
'type' => static::$types['link'],
'identifier' => 'xing',
'is_default' => 1,
'group_id' => 3,
),
$format
);
$this->wpdb->insert(
$this->fields_table_name,
array(
'name' => 'X',
'type' => static::$types['link'],
'identifier' => 't',
'is_default' => 1,
'group_id' => 3,
),
$format
);
}
/**
* Add a string 'identifier' to the proper column for all the default field rows
*/
private function add_string_identifier_to_fields() {
foreach ( $this->type_id_map as $id => $identifier ) {
$this->wpdb->update( $this->fields_table_name, array( 'identifier' => $identifier ), array( 'id' => $id ) );
}
}
/**
* Get groups with fields
*
* @param int $id
* @param boolean $with_fields
*
* @return array|object|null
*/
public function get_groups( $id = 0, $with_fields = true ) {
$args = array();
$query = 'SELECT * FROM ' . $this->groups_table_name;
if ( $id ) {
$where = ' WHERE id = %d';
$args[] = $id;
} else {
/**
* We need this so WPDB won't complain about not preparing the data correctly
*/
$where = ' WHERE 1 = %d';
$args[] = 1;
}
$query .= $where;
$results = $this->wpdb->get_results( $this->wpdb->prepare( $query, $args ), ARRAY_A );
/* I did not write this, I only rewrote it a bit, please don't assassinate me */
if ( $results && $with_fields ) {
foreach ( $results as $group_index => $group ) {
$group_name = $results[ $group_index ]['name'];
$group_config = empty( $this->default_groups[ $group_name ] ) ? array() : $this->default_groups[ $group_name ];
$fields = $this->get_fields( $group );
if ( ! empty( $fields ) ) {
foreach ( $fields as $index => $field ) {
$field['group_name'] = $group['name'];
if ( ! empty( $fields[ $index ]['name'] ) ) {
$fields[ $index ]['name'] = $this->ensure_lowercase_label( $fields[ $index ]['name'] );
}
$fields[ $index ]['formated_data'] = empty( $field['data'] ) ? '' : static::format_field_data( maybe_unserialize( $field['data'] ), $field );
$fields[ $index ]['data'] = empty( $field['data'] ) ? '' : maybe_unserialize( $field['data'] );
$field_config = empty( $field['identifier'] ) ? array() : $this->get_config_for_identifier( $group_config, $field['identifier'] );
if ( empty( $field_config ) || empty( $field_config['icon'] ) ) {
$icon = static::field_icon( $field['type'] );
} else {
$icon = dashboard_icon( $field_config['icon'], true );
}
$fields[ $index ]['icon'] = $icon;
$fields[ $index ]['default_field'] = empty( $field_config ) ? 0 : 1;
}
}
$results[ $group_index ]['default_field'] = empty( $group_config ) ? 0 : 1;
$results[ $group_index ]['fields'] = $fields;
}
}
return $results;
}
/**
* For the current identifier, search the array of configs and return the right one
*
* @param $default_group
* @param $identifier
*
* @return array|mixed
*/
private function get_config_for_identifier( $default_group, $identifier ) {
$config = array();
foreach ( $default_group as $index => $field ) {
if ( ! empty( $field['identifier'] ) && $field['identifier'] === $identifier ) {
$config = $field;
break;
}
}
return $config;
}
/**
* Make sure some specific default labels ( that are stored in the DB ) are lowercase
*
* @param string $label
*
* @return string
*/
private function ensure_lowercase_label( $label ) {
if ( $label === 'Terms and Conditions' ) {
$label = 'Terms and conditions';
} else if ( $label === 'Facebook Page' ) {
$label = 'Facebook page';
}
return $label;
}
/**
* @param $field_type
*
* @return mixed
*/
public static function field_icon( $field_type ) {
return dashboard_icon( static::$icons[ $field_type ], true );
}
public static function format_field_data( $field_data, $field, $args = array() ) {
$data = '';
$unavailable = '';
if ( apply_filters( 'td_smartsite_shortcode_tooltip', false ) ) {
$unavailable_name = empty( $field['group_name'] ) ? $field['name'] : '[' . $field['group_name'] . '] ' . $field['name'];
$unavailable = '<span class="thrive-inline-shortcode-unavailable">' .
'<span class="thrive-shortcode-notice">' .
'!' .
'</span>' .
$unavailable_name .
'</span>';
if ( empty( $args['hide-tooltip'] ) ) {
$unavailable .= '<span class="thrive-tooltip-wrapper">' .
'<span class="thrive-shortcode-tooltip">' .
__( 'This global variable hasn\'t been set. Define your global variables in the', 'thrive-dash' ) .
'<br>' .
'<a><span onClick=window.open("' . add_query_arg( 'page', 'tve_dash_smart_site', admin_url( 'admin.php' ) ) . '","_blank") >' . ' ' . __( ' Global Fields dashboard', 'thrive-dash' ) . '</span></a>' .
'</span>' .
'</span>';
}
}
switch ( (int) $field['type'] ) {
// text field
case TVD_Smart_DB::$types['text']:
$data = empty( $field_data['text'] ) ? $unavailable : $field_data['text'];
break;
//address field
case TVD_Smart_DB::$types['address']:
if ( empty( $field_data['address1'] ) ) {
$data = $unavailable;
} else {
$address_fields = [ 'address1', 'address2', 'city', 'state', 'zip', 'country' ];
$field_data = array_replace( array_fill_keys( $address_fields, null ), $field_data );
$data = implode( empty( $args['multiline'] ) ? ', ' : '<br>', array_filter( $field_data ) );
}
break;
// phone field
case TVD_Smart_DB::$types['phone']:
$data = empty( $field_data['phone'] ) ? $unavailable : $field_data['phone'];
break;
// email field
case TVD_Smart_DB::$types['email']:
$data = empty( $field_data['email'] ) ? $unavailable : $field_data['email'];
break;
//link field
case TVD_Smart_DB::$types['link']:
if ( empty( $field_data['url'] ) ) {
$data = $unavailable;
} else {
if ( empty( $args['prevent-link'] ) ) {
$link_attr = TVD_Smart_Shortcodes::tvd_decode_link_attributes( $args );
$field_value = '<a ' . ( ! empty( $args['link-css-attr'] ) ? 'data-css="' . $args['link-css-attr'] . '"' : '' ) . ' href="' . $field_data['url'] . '" target="' . ( ! empty( $link_attr['target'] ) ? $link_attr['target'] : '' ) . '">' . $field_data['text'] . '</a>';
} else {
$field_value = $field_data['text'];
}
$data = $field_value;
}
break;
// location field
case TVD_Smart_DB::$types['location']:
$url = 'https://maps.google.com/maps?q=' . urlencode( empty( $field_data['location'] ) ? 'New York' : $field_data['location'] ) . '&t=m&z=10&output=embed&iwloc=near';
$data = '<iframe frameborder="0" marginheight="0" marginwidth="0" src="' . $url . '"></iframe>';
break;
}
return $data;
}
/**
* Get fields for group or by ID
*
* @param array $group
* @param int $id
*
* @return array|object|null
*/
public function get_fields( $group = array(), $id = 0 ) {
if ( $group ) {
$where = ' WHERE group_id = %d';
$args[] = $group['id'];
} else {
/**
* We need this so WPDB won't complain about not preparing the data correctly
*/
$where = ' WHERE 1 = %d';
$args[] = 1;
}
if ( $id ) {
if ( is_numeric( $id ) ) {
$where .= ' AND id = %d';
} else {
$where .= ' AND identifier = %s';
}
$args[] = $id;
}
$query = $this->wpdb->prepare( 'SELECT * FROM ' . $this->fields_table_name . $where, $args );
if ( ! $id ) {
$results = $this->wpdb->get_results( $query, ARRAY_A );
} else {
$results = $this->wpdb->get_row( $query, ARRAY_A );
}
return $results;
}
/**
* Get fields of a specific type which have some data
*
* @param array $group_id
* @param int $type
*
* @return array|object|null
*/
public function get_fields_by_type( $group_id = array(), $type = 0 ) {
if ( $type ) {
$where = ' WHERE type = %d';
$args[] = $type;
} else {
return array();
}
if ( $group_id ) {
$where .= ' AND group_id = %d';
$args[] = $group_id;
} else {
$where .= ' AND 1 = %d';
$args[] = 1;
}
$query = $this->wpdb->prepare( 'SELECT * FROM ' . $this->fields_table_name . $where, $args );
return $this->wpdb->get_results( $query, ARRAY_A );
}
public static function save_field( $model, $action ) {
$rep = array(
'%d',
'%s',
'%s',
'%s',
'%s',
);
global $wpdb;
// Add new field
if ( $action === 'insert' ) {
$model['created_at'] = date( 'Y-m-d h:i:s' );
$result = $wpdb->insert(
$wpdb->prefix . 'td_fields',
array(
'group_id' => $model['group_id'],
'name' => $model['name'],
'type' => $model['type'],
'data' => maybe_serialize( $model['data'] ),
'created_at' => $model['created_at'],
),
$rep
);
$model['id'] = (string) $wpdb->insert_id;
} else {
// Update existing field
$model['updated_at'] = date( 'Y-m-d h:i:s' );
$result = $wpdb->update(
$wpdb->prefix . 'td_fields',
array(
'group_id' => (int) $model['group_id'],
'name' => $model['name'],
'type' => $model['type'],
'data' => maybe_serialize( $model['data'] ),
'updated_at' => $model['updated_at'],
),
array( 'id' => $model['id'] ),
$rep,
array( '%d' )
);
}
$model['formated_data'] = static::format_field_data( $model['data'], $model );
$model['icon'] = empty( $model['icon'] ) ? static::field_icon( $model['type'] ) : $model['icon'];
return $result ? $model : false;
}
public static function delete_field( $id ) {
global $wpdb;
return $wpdb->delete( $wpdb->prefix . 'td_fields', array( 'id' => $id ) );
}
public static function insert_group( $model ) {
$model['created_at'] = date( 'Y-m-d h:i:s' );
global $wpdb;
$result = $wpdb->insert(
$wpdb->prefix . 'td_groups',
array(
'name' => $model['name'],
'created_at' => $model['created_at'],
),
array(
'%s',
'%s',
)
);
$model['id'] = (string) $wpdb->insert_id;
return $result ? $model : false;
}
public static function update_group( $model ) {
global $wpdb;
$model['updated_at'] = date( 'Y-m-d h:i:s' );
$result = $wpdb->update(
$wpdb->prefix . 'td_groups',
array(
'name' => $model['name'],
'updated_at' => $model['updated_at'],
),
array( 'id' => $model['id'] ),
array( '%s', '%s', ),
array( '%d' )
);
return $result ? $model : false;
}
public static function delete_group( $id ) {
global $wpdb;
$result = $wpdb->delete( $wpdb->prefix . 'td_groups', array( 'id' => $id ) );
if ( $result ) {
$wpdb->delete( $wpdb->prefix . 'td_fields', array( 'group_id' => $id ), array( '%d' ) );
}
return $result;
}
/*
* we're certain that these IDs have these identifiers because they were there from the start and were inserted in this specific order
* the 'identifier' is the same as the one from the groups() function
* in theory this should never be modified again, it's used only for backwards compatibility when upgrading the DB from 1.0.0 to 1.0.1
*/
public $type_id_map = array(
1 => 'name',
2 => 'addr',
3 => 'phone',
4 => 'alt_phone',
5 => 'mail',
6 => 'priv',
7 => 'disc',
8 => 'toc',
9 => 'contact',
10 => 'fb',
11 => 'yt',
12 => 'in',
13 => 'pin',
14 => 'ig',
);
}

View File

@@ -0,0 +1,116 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class TVD_Smart_Shortcodes
*/
final class TVD_Smart_Shortcodes {
/**
* Database instance for Smart Site
*
* @var TVD_Smart_DB
*/
private $db;
public static $smart_shortcodes = array(
TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE => 'tvd_tss_smart_fields',
TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE_URL => 'tvd_tss_smart_url',
);
/**
* TVD_Smart_Shortcodes constructor.
*/
public function __construct() {
$this->db = new TVD_Smart_DB();
foreach ( static::$smart_shortcodes as $shortcode => $func ) {
$function = array( $this, $func );
add_shortcode( $shortcode, static function ( $attr ) use ( $function ) {
$output = call_user_func_array( $function, func_get_args() );
return TVD_Global_Shortcodes::maybe_link_wrap( $output, $attr );
} );
}
}
/**
* Execute smart fields shortcode
*
* @param $args
*
* @return string
*/
public function tvd_tss_smart_fields( $args ) {
$data = '';
if ( $args['id'] ) {
$field = $this->db->get_fields( array(), $args['id'] );
if ( ! empty( $field ) ) {
$groups = $this->db->get_groups( $field['group_id'], false );
$group = array_pop( $groups );
$field['group_name'] = $group['name'];
$field_data = maybe_unserialize( $field['data'] );
$data = TVD_Smart_DB::format_field_data( $field_data, $field, $args );
}
}
return $data;
}
/**
* Execute smart url shortcode
*
* @param $args
*
* @return string
*/
public function tvd_tss_smart_url( $args ) {
$data = '';
if ( ! empty( $args['id'] ) ) {
$field = $this->db->get_fields( array(), $args['id'] );
if ( ! empty( $field['data'] ) ) {
$field_data = maybe_unserialize( $field['data'] );
if ( isset( $field_data['phone'] ) ) {
$data = 'tel:' . $field_data['phone'];
} elseif ( isset( $field_data['email'] ) ) {
$data = 'mailto:' . $field_data['email'];
} else {
$data = $field_data['url'];
}
}
}
return ( ! empty( $field_data ) ) ? $data : '';
}
/**
* Decode the link settings attributes into an array
*
* @param $link_attr
*
* @return array|mixed
*/
public static function tvd_decode_link_attributes( $link_attr ) {
$data = [];
if ( ! empty( $link_attr['static-link'] ) ) {
$data = json_decode( htmlspecialchars_decode( $link_attr['static-link'] ), true );
}
return $data;
}
}

View File

@@ -0,0 +1,432 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
if ( ! class_exists( 'TVD_Smart_Site' ) ) :
final class TVD_Smart_Site {
/**
* Global fields shortcode key.
*/
const GLOBAL_FIELDS_SHORTCODE = 'thrive_global_fields';
/**
* Global fields url shortcode key.
*/
const GLOBAL_FIELDS_SHORTCODE_URL = 'thrive_global_fields_url';
/**
* Database instance for Smart Site
*
* @var TVD_Smart_DB
*/
private $db;
/**
* @var string
*/
private $_dashboard_page = 'tve_dash_smart_site';
/**
* @var TVD_Smart_Shortcodes
*/
public $shortcodes;
public $global_shortcodes;
/**
* TVD_Smart_Site constructor.
*
* @throws Exception
*/
public function __construct() {
$this->db = new TVD_Smart_DB();
$this->action_filters();
$this->shortcodes = new TVD_Smart_Shortcodes();
$this->global_shortcodes = new TVD_Global_Shortcodes();
}
/**
* Add actiions and filters
*/
private function action_filters() {
add_action( 'current_screen', array( $this, 'conditional_hooks' ) );
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
add_action( 'tve_after_db_migration', array( $this, 'insert_default_data' ), 10, 1 );
add_action( 'rest_api_init', array( $this, 'create_initial_rest_routes' ) );
add_filter( 'tve_dash_filter_features', array( $this, 'smart_site_feature' ) );
add_filter( 'tcb_inline_shortcodes', array( $this, 'smart_site_tcb_shortcodes' ), 10, 1 );
add_filter( 'tcb_dynamiclink_data', array( $this, 'smart_site_links_shortcodes' ) );
add_filter( 'tcb_content_allowed_shortcodes', array( $this, 'allowed_shotcodes' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'frontend_enqueue_scripts' ), 11 );
}
/**
* Filter allowed shortcodes for tve_do_wp_shortcodes
*
* @param $shortcodes
*
* @return array
*/
public function allowed_shotcodes( $shortcodes ) {
return array_merge( $shortcodes, array( TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE, TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE_URL ) );
}
/**
* Hooks for the edit screen
*
*/
public function conditional_hooks() {
$screen = tve_get_current_screen_key();
/**
* Main Dashboard section
*/
if ( $screen === 'toplevel_page_tve_dash_section' ) {
add_filter( 'tve_dash_filter_features', array( $this, 'smart_site_feature' ) );
}
/**
* Smart Site Dashboard
*/
if ( $screen === 'admin_page_' . $this->_dashboard_page ) {
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
add_action( 'admin_print_footer_scripts', array( $this, 'backbone_templates' ) );
}
}
/**
* Add Card to dashboard
*
* @param $features
*
* @return mixed
*/
public function smart_site_feature( $features ) {
$features['smart_site'] = array(
'icon' => 'tvd-smart-site',
'title' => 'Smart Site',
'description' => __( 'Define variables and values sitewide to use when building content', 'thrive-dash' ),
'btn_link' => add_query_arg( 'page', $this->_dashboard_page, admin_url( 'admin.php' ) ),
'btn_text' => __( 'Smart Settings', 'thrive-dash' ),
);
return $features;
}
/**
* Add to admin menu
*/
public function admin_menu() {
add_submenu_page( '', __( 'Smart Site', 'thrive-dash' ), __( 'Smart Site', 'thrive-dash' ), TVE_DASH_CAPABILITY, $this->_dashboard_page, array(
$this,
'admin_dashboard',
) );
}
/**
* Main Smart Site page content
*/
public function admin_dashboard() {
include TVE_DASH_PATH . '/inc/smart-site/views/admin/templates/dashboard.phtml';
}
/**
* Insert the default data in the DB
*
* @param string $option_name
*/
public function insert_default_data( $option_name ) {
if ( $option_name === 'tve_td_db_version' ) {
$db = new TVD_Smart_DB();
$db->insert_default_data();
}
}
/**
* Enqueue admin styles
*/
public function enqueue_styles() {
tve_dash_enqueue_style( 'tvd-ss-admin', TVD_Smart_Const::url( 'assets/admin/css/styles.css' ) );
}
/**
* Enqueue admin scripts
*/
public function enqueue_scripts() {
if ( tve_get_current_screen_key() === 'admin_page_' . $this->_dashboard_page ) {
remove_all_actions( 'admin_notices' );
tve_dash_enqueue();
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'backbone' );
tve_dash_enqueue_script( 'tvd-ss-admin', TVD_Smart_Const::url( 'assets/admin/js/dist/admin.min.js' ), array(
'jquery',
'backbone',
'tve-dash-main-js',
), false, true );
wp_localize_script( 'tvd-ss-admin', 'SmartSite', $this->localize() );
}
}
/**
* Enqueue scripts in the frontend
*/
public function frontend_enqueue_scripts() {
if ( function_exists( 'is_editor_page' ) && is_editor_page() ) {
tve_dash_enqueue_script( 'tvd-ss-tcb-hooks', TVD_Smart_Const::url( 'assets/js/tcb_hooks.js' ), array(
'jquery',
), false, true );
}
}
/**
* Localize admin data
*
* @return array
*/
public function localize() {
return array(
't' => include TVD_Smart_Const::path( 'i18n.php' ),
'dash_url' => admin_url( 'admin.php?page=tve_dash_section' ),
'url' => TVD_Smart_Const::url(),
'nonce' => wp_create_nonce( 'wp_rest' ),
'routes' => array(
'groups' => $this->get_route_url( 'groups' ),
'fields' => $this->get_route_url( 'fields' ),
),
'data' => array(
'groups' => $this->db->get_groups(),
'fieldTypes' => TVD_Smart_DB::field_types(),
),
);
}
/**
* Add backbone templates
*/
public function backbone_templates() {
$templates = tve_dash_get_backbone_templates( TVD_Smart_Const::path( 'views/admin/templates' ), 'templates' );
tve_dash_output_backbone_templates( $templates );
}
/**
* Create the rest routes
*/
public function create_initial_rest_routes() {
$endpoints = array(
'TVD_Groups_Controller',
'TVD_Fields_Controller',
);
foreach ( $endpoints as $e ) {
/** @var TVD_REST_Controller $controller */
$controller = new $e();
$controller->register_routes();
}
}
/**
* Get the route url
*
* @param $endpoint
* @param int $id
* @param array $args
*
* @return string
*/
private function get_route_url( $endpoint, $id = 0, $args = array() ) {
$url = get_rest_url() . TVD_Smart_Const::REST_NAMESPACE . '/' . $endpoint;
if ( ! empty( $id ) && is_numeric( $id ) ) {
$url .= '/' . $id;
}
if ( ! empty( $args ) ) {
add_query_arg( $args, $url );
}
return $url;
}
public function add_phone_link( $fields_phone ) {
foreach ( $fields_phone as &$field ) {
$field['name'] .= ' (Click to call)';
if ( ! empty( $field['data'] ) ) {
$field_data = maybe_unserialize( $field['data'] );
if ( ! empty( $field_data['phone'] ) ) {
$field_data['url'] = 'tel:' . $field_data['phone'];
$field['data'] = maybe_serialize( $field_data );
}
}
}
return $fields_phone;
}
public function add_email_link( $field_email ) {
foreach ( $field_email as &$field ) {
$field['name'] .= ' (Click to email)';
if ( ! empty( $field['data'] ) ) {
$field_data = maybe_unserialize( $field['data'] );
if ( ! empty( $field_data['email'] ) ) {
$field_data['url'] = 'mailto:' . $field_data['email'];
$field['data'] = maybe_serialize( $field_data );
}
}
}
return $field_email;
}
/**
* Get the data from global fields for the links that have a value set
*
* @param $dynamic_links
*
* @return mixed
*/
public function smart_site_links_shortcodes( $dynamic_links ) {
if ( ! empty( $_REQUEST[ TVE_FRAME_FLAG ] ) ) {
$groups = $this->db->get_groups( 0, false );
$links = array();
foreach ( $groups as $group ) {
$fields_link = $this->db->get_fields_by_type( $group['id'], TVD_Smart_DB::$types['link'] );
$fields_phone = $this->db->get_fields_by_type( $group['id'], TVD_Smart_DB::$types['phone'] );
$field_email = $this->db->get_fields_by_type( $group['id'], TVD_Smart_DB::$types['email'] );
$fields_phone = $this->add_phone_link( $fields_phone );
$field_email = $this->add_email_link( $field_email );
$new_fields = array_merge( $fields_phone, $field_email );
$fields = array_merge( $new_fields, $fields_link );
$name = $group['name'];
/* Add the fields in the group only when the group is empty */
if ( ! empty( $fields ) && empty( $links[ $name ] ) ) {
$links[ $name ] = array();
foreach ( $fields as $global_field ) {
$data = maybe_unserialize( $global_field['data'] );
$inserted_id = $global_field['id'];
$links[ $name ][] = array(
'name' => $global_field['name'],
'id' => empty( $global_field['identifier'] ) ? $inserted_id : $global_field['identifier'],
'inserted_id' => $inserted_id, /* used for backwards compatibility, mostly */
'identifier' => $global_field['identifier'],
'url' => empty( $data['url'] ) ? '' : $data['url'],
);
}
}
if ( empty( $links[ $name ] ) ) {
unset( $links[ $name ] );
}
}
if ( ! empty( $links ) ) {
$dynamic_links['Global Fields'] = array(
'links' => $links,
'shortcode' => TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE_URL,
);
}
}
return $dynamic_links;
}
/**
* Add the shortcodes to the froala editor
*
* @param $shortcodes
*
* @return array
*/
public function smart_site_tcb_shortcodes( $shortcodes ) {
if ( ! empty( $_REQUEST[ TVE_FRAME_FLAG ] ) ) {
$groups = $this->db->get_groups();
$result_shortcodes = array();
foreach ( $groups as $group ) {
if ( ! empty( $group['fields'] ) ) {
$result_shortcodes[ $group['id'] ] = array(
'name' => $group['name'],
'option' => $group['name'],
'value' => TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE,
'extra_param' => $group['id'],
'input' => array(
'id' => array(
'type' => 'select',
'label' => __( 'Field', 'thrive-dash' ),
'value' => array(),
'extra_options' => array(
'multiline' => array(
'available_for' => array( TVD_Smart_DB::$types['address'] ),
'type' => 'checkbox',
'label' => __( 'Include line breaks', 'thrive-dash' ),
'value' => false,
),
'enable_call' => array(
'available_for' => array( TVD_Smart_DB::$types['phone'] ),
'type' => 'checkbox',
'label' => __( 'Enable click to call', 'thrive-dash' ),
'value' => false,
),
'enable_email' => array(
'available_for' => array( TVD_Smart_DB::$types['email'] ),
'type' => 'checkbox',
'label' => __( 'Enable click to email', 'thrive-dash' ),
'value' => false,
),
),
),
),
);
foreach ( $group['fields'] as $field ) {
$result_shortcodes[ $group['id'] ]['input']['id']['value'][ $field['id'] ] = $field['name'];
$result_shortcodes[ $group['id'] ]['input']['id']['value_type'][ $field['id'] ] = $field['type'];
$result_shortcodes[ $group['id'] ]['input']['id']['real_data'][ $field['id'] ] = $this->shortcodes->tvd_tss_smart_fields( array( 'id' => $field['id'] ) );
}
}
}
$shortcode['Global fields'] = array_values( $result_shortcodes );
$shortcodes = array_merge( $shortcodes, $shortcode );
}
return $shortcodes;
}
}
endif;
/**
* Start Smart Site
*/
new TVD_Smart_Site();

View File

@@ -0,0 +1,79 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Archive_Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Archive_Rule extends Rule {
/**
* Returns true if the rule is valid
*
* NOTE: for now the archive rules is only supported for Authors
*
* @return bool
*/
public function is_valid() {
$valid = parent::is_valid();
if ( $this->field !== 'author' ) {
/**
* For now we only support Author field for Archive Rules
*/
$valid = false;
}
return $valid;
}
/**
* Should be extended in child classes
*
* @param string $query_string
* @param bool|int $paged if non-false, it will return limited results
* @param int $per_page number of results per page. ignored if $paged = false
*
* @return array
*/
public function get_items( $query_string = '', $paged = false, $per_page = 15 ) {
$this->paged = $paged;
$this->per_page = $per_page;
$this->query_string = $query_string;
$items = array();
if ( $this->field === 'author' ) {
$items = parent::search_users();
}
return $items;
}
/**
* Test if a rule matches the given params
*
* @param \WP_User $user
*
* @return bool
*/
public function matches( $user ) {
if ( is_author() ) {
return $this->match_value( $user->ID, $user );
}
return false;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Blog_Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Blog_Rule extends Rule {
/**
* @return bool
*/
public function is_valid() {
return true;
}
/**
* Returns true if the active query is for the blog homepage.
*
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function matches( $post_or_term ) {
return is_home();
}
}

View File

@@ -0,0 +1,305 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Post_Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Post_Rule extends Rule {
/**
* @var string[]
*/
private $tax_fields
= array(
self::FIELD_CATEGORY => 'category',
self::FIELD_TAG => 'post_tag',
self::FIELD_AUTHOR => 'author',
);
/**
* Should be extended in child classes
*
* @param string $query_string
* @param bool|int $paged if non-false, it will return limited results
* @param int $per_page number of results per page. ignored if $paged = false
*
* @return array
*/
public function get_items( $query_string = '', $paged = false, $per_page = 15 ) {
/**
* Needed for the filter
*/
$this->query_string = $query_string;
$this->paged = $paged;
$this->per_page = $per_page;
return $this->should_search_terms() ? $this->search_terms() : $this->search_posts();
}
/**
* posts_where hook callback
* Searches particular post by title
*
* @param string $where
* @param \WP_Query $wp_query
*
* @return string
*/
public function title_filter( $where, $wp_query ) {
if ( $this->field === self::FIELD_TITLE ) {
global $wpdb;
$operation = $this->operator === self::OPERATOR_IS ? 'LIKE' : 'NOT LIKE';
$where .= ' AND ' . $wpdb->posts . '.post_title ' . $operation . ' \'%' . esc_sql( $wpdb->esc_like( $this->query_string ) ) . '%\'';
}
return $where;
}
/**
* terms_clauses hook callback
* Searches for a particular term by name
*
* @param $pieces
* @param $taxonomies
* @param $args
*
* @return array
*/
public function name_filter( $pieces, $taxonomies, $args ) {
if ( $this->should_search_terms() ) {
global $wpdb;
$operation = 'LIKE';
$pieces['where'] .= ' AND name ' . $operation . ' ' . ' \'%' . esc_sql( $wpdb->esc_like( $this->query_string ) ) . '%\' ';
}
return $pieces;
}
/**
* Prepares the item for front-end
*
* @param int $item
* @param bool $is_term
*
* @return array
*/
public function get_frontend_item( $item ) {
if ( $this->should_search_terms() ) {
return parent::get_frontend_item( $item );
}
$post = get_post( $item );
if ( empty( $post ) ) {
return array();
}
return array(
'id' => $post->ID,
'text' => $this->alter_frontend_title( $post->post_title, $post->post_status ),
);
}
/**
* Used to get all public content types needed for the content sets
*
* @return array
*/
public static function get_content_types() {
$ignored_types = apply_filters( 'thrive_ignored_post_types', array(
'attachment',
'tcb_lightbox',
'tcb_symbol',
'tva-acc-restriction',
) );
$all = get_post_types( array( 'public' => true ) );
$post_types = array();
foreach ( $all as $key => $post_type ) {
if ( in_array( $key, $ignored_types, true ) ) {
continue;
}
$post_types[ $key ] = tvd_get_post_type_label( $key );
}
/**
* Allow other functionality to be injected here
* Used to inject the Protected File post_type in the list so it can be protected by an apprentice product
*
* @param array $post_types
*/
return apply_filters( 'tvd_content_sets_get_content_types', $post_types );
}
/**
* Returns true if the UI needs to perform a terms search
*
* @return bool
*/
private function should_search_terms() {
return array_key_exists( $this->field, $this->tax_fields );
}
/**
* Returns the terms (category|post_tags) needed for the UI
* called from get_items method
*
* @return array
*/
private function search_terms() {
$response = array();
$taxonomy = $this->tax_fields[ $this->field ];
if ( $this->field === self::FIELD_AUTHOR ) {
$response = parent::search_users();
} else {
add_filter( 'terms_clauses', array( $this, 'name_filter' ), 10, 3 );
$query = new \WP_Term_Query();
$args = array(
'taxonomy' => $taxonomy,
'hide_empty' => false,
);
if ( $this->paged !== false ) {
$args['number'] = $this->per_page;
$args['offset'] = ( $this->paged - 1 ) * $this->per_page;
}
$terms = $query->query( $args );
remove_filter( 'terms_clauses', array( $this, 'name_filter' ), 10 );
foreach ( $terms as $term ) {
$response[] = array(
'id' => $term->term_id,
'text' => $term->name,
);
}
}
return $response;
}
/**
* Returns the posts needed for the UI
* called from get_items method
*
* @return array
*/
private function search_posts() {
$response = array();
add_filter( 'posts_where', array( $this, 'title_filter' ), 10, 2 );
$query = new \WP_Query;
$args = array(
'post_type' => $this->content,
'post_status' => array( 'draft', 'publish' ),
'posts_per_page' => $this->paged !== false ? $this->per_page : - 1,
);
if ( $this->paged !== false ) {
$args['paged'] = $this->paged;
}
$posts = $query->query( $args );
remove_filter( 'posts_where', array( $this, 'title_filter' ), 10 );
foreach ( $posts as $post ) {
/**
* Allow other plugins to hook here and remove the post from the content set dropdown
*
* @param boolean return value
* @param \WP_Post $post
*/
if ( ! apply_filters( 'tvd_content_sets_allow_select_post', true, $post ) ) {
continue;
}
$response[] = array(
'id' => $post->ID,
'text' => $this->alter_frontend_title( $post->post_title, $post->post_status ),
);
}
return $response;
}
/**
* Test if a rule matches the given params
*
* @param int|string $value
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function match_value( $value, $post_or_term ) {
if ( $this->field === self::FIELD_PUBLISHED_DATE ) {
$post_published_date = get_the_date( 'Y-m-d', $post_or_term );
switch ( $this->operator ) {
case self::OPERATOR_LOWER_EQUAL:
return strtotime( $post_published_date ) <= strtotime( $this->value );
case self::OPERATOR_GRATER_EQUAL:
return strtotime( $post_published_date ) >= strtotime( $this->value );
case self::OPERATOR_WITHIN_LAST:
return strtotime( $post_published_date ) >= strtotime( '-' . $this->value );
default:
break;
}
return false;
}
if ( $this->field === self::FIELD_TAG ) {
$common = count( array_intersect( $this->value, wp_get_post_tags( $post_or_term->ID, array( 'fields' => 'ids' ) ) ) );
if ( $this->operator === self::OPERATOR_IS ) {
return $common > 0;
}
return $common === 0;
}
if ( $this->field === self::FIELD_CATEGORY ) {
$common = count( array_intersect( $this->value, wp_get_post_categories( $post_or_term->ID, array( 'fields' => 'ids' ) ) ) );
if ( $this->operator === self::OPERATOR_IS ) {
return $common > 0;
}
return $common === 0;
}
if ( $this->field === self::FIELD_AUTHOR ) {
return in_array( (int) $post_or_term->post_author, $this->value, true );
}
return parent::match_value( $value, $post_or_term );
}
}

View File

@@ -0,0 +1,445 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Rule implements \JsonSerializable {
const HIDDEN_POST_TYPE_SEARCH_RESULTS = 'tvd_search_result_page';
const HIDDEN_POST_TYPE_BLOG = 'tvd_blog_page';
const OPERATOR_IS = '===';
const OPERATOR_NOT_IS = '!==';
const OPERATOR_GRATER_EQUAL = '>=';
const OPERATOR_LOWER_EQUAL = '<=';
const OPERATOR_WITHIN_LAST = 'within_last';
const FIELD_ALL = '-1';
const FIELD_TITLE = 'title';
const FIELD_TAG = 'tag';
const FIELD_CATEGORY = 'category';
const FIELD_PUBLISHED_DATE = 'published_date';
const FIELD_TOPIC = 'topic';
const FIELD_DIFFICULTY = 'difficulty';
const FIELD_LABEL = 'label';
const FIELD_AUTHOR = 'author';
/**
* @var string Query String needed to for post search
*/
protected $query_string = '';
protected $paged = false;
protected $per_page = 15;
protected $user_fields = array(
self::FIELD_AUTHOR,
);
/**
* @var mixed
*/
public $type;
/**
* @var mixed
*/
public $value;
/**
* @var mixed
*/
public $content;
/**
* @var mixed
*/
public $field;
/**
* @var mixed
*/
public $operator;
/**
* Rule constructor.
*
* @param array $data
*/
public function __construct( $data ) {
$this->type = $data['content_type'];
$this->content = $data['content'];
$this->field = $data['field'];
$this->operator = $data['operator'];
$this->value = $data['value'];
}
/**
* Factory for Rule Classes
*
* @param array $data
*
* @return Term_Rule|Post_Rule
*/
public static function factory( $data ) {
switch ( $data['content_type'] ) {
case 'term':
$class_name = 'Term_Rule';
break;
case self::HIDDEN_POST_TYPE_SEARCH_RESULTS:
$class_name = 'Search_Result_Rule';
break;
case self::HIDDEN_POST_TYPE_BLOG:
$class_name = 'Blog_Rule';
break;
case 'archive':
$class_name = 'Archive_Rule';
break;
default:
$class_name = 'Post_Rule';
break;
}
$class_name = __NAMESPACE__ . '\\' . $class_name;
return new $class_name( $data );
}
/**
* Returns true if the rule is valid
*
* @return bool
*/
public function is_valid() {
return ! empty( $this->type ) && in_array( $this->type, array( 'post', 'term', 'archive' ) ) &&
! empty( $this->content ) &&
! empty( $this->field ) &&
( (int) $this->field === - 1 || ( ! empty( $this->operator ) && ( is_array( $this->value ) || ! empty( $this->value ) || is_numeric( $this->value ) ) ) );
}
/**
* Return true if the active rule is equal to the rule provided as parameter
*
* @param Rule $rule
*
* @return boolean
*/
public function is_equal_to( $rule ) {
return serialize( $rule->jsonSerialize( false ) ) === serialize( $this->jsonSerialize( false ) );
}
/**
* Returns true if rule has value equal to parameter or value in array of values
*
* @param scalar $value
*
* @return boolean
*/
public function has_value( $value ) {
if ( $this->value === $value || ( is_array( $this->value ) && in_array( $value, $this->value, true ) ) ) {
return true;
}
return false;
}
/**
* Should be extended in child classes
*
* @param string $query_string
* @param bool|int $paged if non-false, it will return limited results
* @param int $per_page number of results per page. ignored if $paged = false
*
* @return array
*/
public function get_items( $query_string = '', $paged = false, $per_page = 15 ) {
return array();
}
/**
* Test if a rule matches the given params
*
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function matches( $post_or_term ) {
list( $content, $value ) = Utils::get_post_or_term_parts( $post_or_term );
if ( ! $this->match_content( $content ) ) {
return false;
}
if ( (int) $this->field === - 1 ) {
return true;
}
return $this->match_value( $value, $post_or_term );
}
/**
* @param \WP_Post|\WP_Term $post_or_term
*
* @used in Thrive Apprentice - edit-post view
* @return bool
*/
public function matches_static_value( $post_or_term ) {
list( $content, $value ) = Utils::get_post_or_term_parts( $post_or_term );
if ( ! $this->match_content( $content ) ) {
return false;
}
if ( $this->field !== self::FIELD_TITLE ) {
return false;
}
return $this->match_value( $value, $post_or_term );
}
/**
* @param string $content
*
* @return bool
*/
public function match_content( $content ) {
return $this->content === $content;
}
/**
* @param string|array $value
* @param \WP_Post|\WP_Term|\WP_User $post_or_term
*
* @return bool
*/
public function match_value( $value, $post_or_term ) {
if ( is_array( $this->value ) ) {
if ( $this->operator === self::OPERATOR_IS ) {
return in_array( $value, $this->value );
}
if ( $this->operator === self::OPERATOR_NOT_IS ) {
return ! in_array( $value, $this->value );
}
}
return $value === $this->value;
}
/**
* Constructs the item needed for front-end
* Needs to be extended in child classes
*
* @param int $item
*
* @return array
*/
public function get_frontend_item( $item ) {
if ( $this->should_search_users() ) {
return $this->get_frontend_user( $item );
}
return $this->get_frontend_term( $item );
}
/**
* Constructs the item from a term, needed for front-end
*
* @param int $item
*
* @return array
*/
public function get_frontend_user( $item ) {
$user = get_user_by( 'id', $item );
if ( empty( $user ) || ! $user instanceof \WP_User ) {
return array();
}
return array(
'id' => (int) $item,
'text' => $user->display_name,
);
}
/**
* Constructs the item from a term, needed for front-end
*
* @param int $item
*
* @return array
*/
public function get_frontend_term( $item ) {
$term = get_term( $item );
if ( empty( $term ) ) {
return array();
}
return array(
'id' => $term->term_id,
'text' => $term->name,
);
}
/**
* @return string
*/
public function get_content() {
return $this->content;
}
/**
* @return array
*/
public function get_value() {
return $this->value;
}
/**
* @return array
*/
#[\ReturnTypeWillChange]
public function jsonSerialize( $frontend = true ) {
$value = $this->value;
if ( is_array( $this->value ) ) {
if ( $frontend ) {
$value = array_map( function ( $item ) {
if ( isset( $item ) && is_int( $item ) && $front_item = $this->get_frontend_item( $item ) ) {
return $front_item;
}
}, $this->value );
//Remove empty values for UI
//Solves the case when we have a content set that contains a course or a post and that post is no longer available
//Also reset the indexes of the array
$value = array_values( array_filter( $value ) );
} else {
$value = $this->value = array_map( static function ( $item ) {
if ( is_numeric( $item ) ) {
return (int) $item;
}
if ( ! empty( $item['id'] ) || is_numeric( $item['id'] ) ) {
return (int) $item['id'];
}
}, $this->value );
}
}
$return = array(
'content_type' => $this->type,
'content' => $this->content,
'field' => $this->field,
'operator' => $this->operator,
'value' => $value,
);
if ( $frontend ) {
$return['content_label'] = array(
'singular' => 'Post',
'plural' => 'Posts',
);
if ( $return['content_type'] === 'term' ) {
/**
* @var \WP_Taxonomy
*/
$taxonomy = get_taxonomy( $return['content'] );
if ( $taxonomy instanceof \WP_Taxonomy ) {
$return['content_label']['singular'] = $taxonomy->labels->singular_name;
$return['content_label']['plural'] = $taxonomy->labels->name;
}
} elseif ( $return['content_type'] === 'post' ) {
/**
* @var \WP_Post_Type
*/
$postType = get_post_type_object( $return['content'] );
if ( $postType instanceof \WP_Post_Type ) {
$return['content_label']['singular'] = $postType->labels->singular_name;
$return['content_label']['plural'] = $postType->labels->name;
}
} elseif ( $return['content_type'] === self::HIDDEN_POST_TYPE_SEARCH_RESULTS ) {
$return['content_label']['plural'] = $return['content_label']['singular'] = 'Search result page';
} elseif ( $return['content_type'] === self::HIDDEN_POST_TYPE_BLOG ) {
$return['content_label']['plural'] = $return['content_label']['singular'] = 'Blog page';
} elseif ( $return['content_type'] === 'archive' ) {
$return['content_label']['plural'] = $return['content_label']['singular'] = 'Archive page';
}
}
return $return;
}
/**
* Alter the title that is shown in the UI depending on the status
*
* @param string $title
* @param string $status
*
* @return string
*/
protected function alter_frontend_title( $title, $status = 'publish' ) {
if ( $status !== 'publish' ) {
$title .= ' [' . $status . ']';
}
return $title;
}
/**
* @return array
*/
public function search_users() {
$search_string = esc_attr( trim( $this->query_string ) );
$response = array();
$users = new \WP_User_Query( array(
'search' => '*' . $search_string . '*',
'search_columns' => array(
'display_name',
),
'number' => $this->paged !== false ? $this->per_page : - 1,
'offset' => $this->paged !== false ? ( $this->paged - 1 ) * $this->per_page : 0,
)
);
/**
* @var \WP_User $user
*/
foreach ( $users->get_results() as $user ) {
$response[] = array(
'id' => (int) $user->data->ID,
'text' => (string) $user->data->display_name,
);
}
return $response;
}
/**
* Returns true if the system should search in user tables for values
*
* @return bool
*/
protected function should_search_users() {
return ! empty( $this->user_fields ) && in_array( $this->field, $this->user_fields );
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Search_Result_Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Search_Result_Rule extends Rule {
/**
* @return bool
*/
public function is_valid() {
return true;
}
/**
* Returns true if the active query is for a search.
*
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function matches( $post_or_term ) {
return is_search();
}
}

View File

@@ -0,0 +1,669 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
use TVD\Cache\Cache_Exception;
use function TVD\Cache\content_set_cache;
use function TVD\Cache\meta_cache;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Set
*
* @property int ID
* @property string $post_title
* @property array $post_content
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Set implements \JsonSerializable {
/**
* Stores the identified content set
*
* @var array
*/
private static $matched;
use \TD_Magic_Methods;
/**
* @var string Post type name
*/
const POST_TYPE = 'tvd_content_set';
/**
* default properties for a TA Course
*
* @var array
*/
protected $_defaults
= array(
'ID' => 0,
'post_title' => '',
'post_content' => array(),
);
private $rules = array();
/**
* Set constructor.
*
* @param int|array $data
*/
public function __construct( $data ) {
if ( is_int( $data ) ) {
$data = get_post( (int) $data, ARRAY_A );
$data['post_content'] = thrive_safe_unserialize( $data['post_content'] );
} elseif ( is_array( $data ) && ! empty( $data['post_content'] ) ) {
$data['post_content'] = thrive_safe_unserialize( $data['post_content'] );
}
$this->_data = array_merge( $this->_defaults, (array) $data );
foreach ( $data['post_content'] as $rule_data ) {
$this->rules[] = Rule::factory( $rule_data );
}
}
/**
* Register post type
*/
public static function init() {
register_post_type( static::POST_TYPE, array(
'labels' => array(
'name' => 'Content Set',
),
'publicly_queryable' => true, //Needs to be queryable on front-end for products
'public' => false,
'query_var' => false,
'rewrite' => false,
'show_in_nav_menus' => false,
'show_in_menu' => false,
'show_ui' => false,
'exclude_from_search' => true,
'show_in_rest' => true,
'has_archive' => false,
'map_meta_cap' => true,
) );
}
/**
* @param array $args
*
* @return Set[]
*/
public static function get_items( $args = array() ) {
$posts = get_posts( array_merge( array(
'posts_per_page' => - 1,
'post_type' => static::POST_TYPE,
), $args ) );
$sets = array();
foreach ( $posts as $post ) {
$sets[] = new Set( $post->to_array() );
}
return $sets;
}
/**
* Return all content sets as key => name
* Used for adding into a select element
*
* @param array|null $sets
*
* @return array
*/
public static function get_items_for_dropdown( $sets = null ) {
$dropdown = array();
if ( ! is_array( $sets ) ) {
$sets = static::get_items();
}
foreach ( $sets as $set ) {
$dropdown[] = array( 'id' => $set->ID, 'text' => $set->post_title );
}
return $dropdown;
}
/**
* @param \WP_Post|\WP_Term $post_or_term
* @param string $return_type can be objects or ids
*
* @return array
*/
public static function get_items_that_static_match( $post_or_term, $return_type = 'objects' ) {
$return = array();
foreach ( static::get_items() as $set ) {
if ( $set->has_matching_static_rules( $post_or_term ) ) {
$return[] = $return_type === 'ids' ? $set->ID : $set;
}
}
return $return;
}
/**
* @param $post_or_term
* @param array $sets_ids_for_object
*
* @return string|void
*/
public static function toggle_object_to_set_static_rules( $post_or_term, $sets_ids_for_object = array() ) {
list( $content, $value ) = Utils::get_post_or_term_parts( $post_or_term );
if ( $post_or_term instanceof \WP_Post && ! array_key_exists( $content, Post_Rule::get_content_types() ) ) {
return;
}
$current_matches = static::get_items_that_static_match( $post_or_term, 'ids' );
sort( $current_matches );
sort( $sets_ids_for_object );
if ( $current_matches == $sets_ids_for_object ) {
//No modifications have been done to the content sets$ty
return;
}
$rule = Rule::factory( array(
'content_type' => 'post',
'content' => $content,
'field' => Rule::FIELD_TITLE,
'operator' => Rule::OPERATOR_IS,
'value' => array( $value ),
) );
//Remove rule from content set
$sets_to_remove = array_diff( $current_matches, $sets_ids_for_object );
foreach ( $sets_to_remove as $id ) {
$set = new Set( (int) $id );
$set->remove_rule( $rule )->remove_rule_value( $value )->update();
}
//Add rule to content set
$sets_to_add = array_diff( $sets_ids_for_object, $current_matches );
foreach ( $sets_to_add as $id ) {
$set = new Set( (int) $id );
$set->add_rule( $rule )->update();
}
}
/**
* @param \WP_Post|\WP_Term|\WP_User $post_or_term
*
* @return bool
*/
public function has_matching_rules( $post_or_term ) {
$return = false;
/**
* @var Rule $rule
*/
foreach ( $this->rules as $rule ) {
if ( $rule->matches( $post_or_term ) ) {
$return = true;
break;
}
}
return $return;
}
/**
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function has_matching_static_rules( $post_or_term ) {
$return = false;
/**
* @var Rule $rule
*/
foreach ( $this->rules as $rule ) {
if ( $rule->matches_static_value( $post_or_term ) ) {
$return = true;
break;
}
}
return $return;
}
/**
* @param Rule $rule
*
* @return $this
*/
public function remove_rule( $rule ) {
$index_to_remove = null;
/**
* @var Rule $r
*/
foreach ( $this->rules as $index => $r ) {
if ( $r->is_equal_to( $rule ) ) {
$index_to_remove = $index;
break;
}
}
if ( ! is_numeric( $index_to_remove ) ) {
return $this;
}
unset( $this->rules[ $index_to_remove ] );
return $this;
}
/**
* Remove value from the rules
*
* @param scalar $value
*
* @return $this
*/
public function remove_rule_value( $value ) {
if ( empty( $value ) ) {
return $this;
}
/**
* @var Rule $r
*/
foreach ( $this->rules as $index => $r ) {
if ( $r->has_value( $value ) ) {
if ( is_array( $r->value ) && ( $key = array_search( $value, $r->value ) ) !== false ) {
unset( $this->rules[ $index ]->value[ $key ] );
//Reset the indexes
$this->rules[ $index ]->value = array_values( $this->rules[ $index ]->value );
} elseif ( is_scalar( $r->value ) ) {
$this->rules[ $index ]->value = '';
}
}
}
return $this;
}
/**
* @param Rule $rule
*
* @return $this
*/
public function add_static_rule( $rule ) {
if ( ! $rule->is_valid() ) {
return $this;
}
$child_added = false;
/**
* @var Rule $r
*/
foreach ( $this->rules as $r ) {
if ( $rule->get_content() === $r->get_content() && $rule->field === Rule::FIELD_TITLE && $r->field === Rule::FIELD_TITLE ) {
if ( is_array( $rule->value ) && is_array( $r->value ) ) {
$r->value = array_merge( $rule->value, $r->value );
$child_added = true;
break;
}
}
}
if ( ! $child_added ) {
$this->rules[] = $rule;
}
return $this;
}
/**
* @param Rule $rule
*
* @return $this
*/
public function add_rule( $rule ) {
if ( ! $rule->is_valid() ) {
return $this;
}
/**
* @var Rule $r
*/
foreach ( $this->rules as $r ) {
if ( $r->is_valid() && $r->is_equal_to( $rule ) ) {
return $this;
}
}
$this->rules[] = $rule;
return $this;
}
/**
* Identify all the sets that contain the given object
*
* @param \WP_Post|\WP_Term $post_or_term
* @param string $return_type what to return - objects or IDs
*
* @return array
*/
public static function identify_from_object( $post_or_term, $return_type = 'objects' ) {
$sets = array();
if ( ! $post_or_term instanceof \WP_Post && ! $post_or_term instanceof \WP_Term ) {
return $sets;
}
/**
* @var Set $set
*/
foreach ( static::get_items() as $set ) {
if ( $set->has_matching_rules( $post_or_term ) ) {
$sets[] = $return_type === 'objects' ? $set : $set->ID;
}
}
return $sets;
}
/**
* @return int|\WP_Error
*/
public function create() {
$rules = $this->prepare_rules_for_db();
$valid = ! empty( $this->post_title ) && is_array( $rules );
if ( ! $valid ) {
return 0;
}
/**
* We need to make sure that fire_after_hooks is false because other plugins also call the create method on save_post hooks
*/
return wp_insert_post( array(
'post_title' => $this->post_title,
'post_content' => serialize( $rules ),
'post_type' => static::POST_TYPE,
'post_status' => 'publish',
), false, false );
}
/**
* @return array|false|\WP_Post|null
*/
public function delete() {
/**
* Fired before completely deleting a content set from the database.
*
* @param Set $instance the content set instance
*/
do_action( 'tvd_content_set_before_delete', $this );
return wp_delete_post( $this->ID, true );
}
/**
* @return int|\WP_Error
*/
public function update() {
/**
* Triggered before a content set is updated
*
* @param Set $instance the content set instance
*/
do_action( 'tvd_content_set_before_update', new static( $this->ID ) );
$rules = $this->prepare_rules_for_db();
$valid = ! empty( $this->post_title ) && is_array( $rules );
if ( ! $valid ) {
return 0;
}
/**
* We need to make sure that fire_after_hooks is false because other plugins also call the update method on save_post hooks
*/
$result = wp_update_post( array(
'ID' => $this->ID,
'post_title' => $this->post_title,
'post_content' => serialize( $rules ),
), false, false );
if ( ! is_wp_error( $result ) ) {
/**
* Triggered before a content set is updated
*
* @param Set $instance the content set instance
*/
do_action( 'tvd_content_set_after_update', $this );
}
return $result;
}
/**
* Returns the rules if the rules are valid or false otherwise
* Prepares the rules for database
*
* @return bool|array
*/
private function prepare_rules_for_db() {
$valid = true;
$rules = array();
/**
* @var Post_Rule|Term_Rule $rule
*/
foreach ( $this->rules as $rule ) {
if ( ! $rule->is_valid() ) {
$valid = false;
break;
}
$rules[] = $rule->jsonSerialize( false );
}
if ( $valid ) {
return $rules;
}
return false;
}
public function get_tva_courses_ids() {
$id_pairs = array();
foreach ( $this->rules as $rule ) {
if ( true === $rule instanceof Term_Rule && $rule->get_content() === 'tva_courses' ) {
if ( $rule->field === Rule::FIELD_TITLE ) {
$entries = $rule->get_value();
} else {
//Dynamic stuff
//Fetching courses based on dynamic properties -> such as difficulty, label, topic or author
$course_terms = get_terms( [
'taxonomy' => 'tva_courses',
'hide_empty' => false,
'meta_query' => [
'tva_status' => [
'key' => 'tva_status',
'value' => 'private',
'compare' => '!=',
],
],
] );
$entries = [];
foreach ( $course_terms as $course_term ) {
if ( $rule->matches( $course_term ) ) {
$entries[] = [ 'id' => $course_term->term_id ];
}
}
}
if ( ! empty( $entries ) && is_array( $entries ) && is_array( $entries[0] ) && array_key_exists( 'id', $entries[0] ) ) {
$entries = array_column( $entries, 'id' );
}
$id_pairs [] = empty( $entries ) || ! is_array( $entries ) ? [] : $entries;
}
}
if ( empty( $id_pairs ) ) {
return array();
}
return array_merge( ...$id_pairs );
}
/**
* @return array
*/
#[\ReturnTypeWillChange]
public function jsonSerialize() {
return array(
'ID' => $this->ID,
'post_title' => $this->post_title,
'post_content' => $this->rules,
);
}
/**
* Identify the content sets that have rules matching the current request and store it in a local cache for further calls during this request
*
* @return int[]
*/
public static function get_for_request() {
if ( ! did_action( 'parse_query' ) && ! wp_doing_ajax() ) {
trigger_error( 'Content Sets: get_for_request() called incorrectly. It must be called after the `parse_query` hook', E_USER_WARNING );
return [];
}
if ( wp_doing_ajax() ) {
// TODO find a way to reliably match the main request
}
/* search in local cache first */
if ( static::$matched !== null ) {
return static::$matched;
}
/* nothing in the local cache, compute it */
static::$matched = [];
/* currently, only supports taxonomy terms and posts */
if ( Utils::is_context_supported() ) {
$queried_object = get_queried_object();
static::$matched = empty( $queried_object ) ? static::get_for_non_object() : static::get_for_object( $queried_object );
}
return static::$matched;
}
/**
* @param \WP_Post|\WP_Term $object
* @param int|null $id
*/
public static function get_for_object( $object, $id = null ) {
if ( $id === null ) {
$id = get_queried_object_id();
}
try {
$cache = content_set_cache( $object );
} catch ( Cache_Exception $e ) {
/* when receiving invalid data, make sure the execution continues */
return [];
}
$matched = $cache->get_or_store( $id, static function () use ( $object ) {
return static::identify_from_object( $object, 'ids' );
} );
if ( $cache->hit() ) {
/**
* This generates unused queries
* The purpose of this functionality is to fetch content sets with dynamic data - such as pusblied_date
* This functionality was removed from the initial content sets release
*/
// /**
// * We need to find all sets that are matched and the rule contains the time related rules
// */
// $sets = static::get_items( array( 'post__in' => $matched, 's' => Rule::FIELD_PUBLISHED_DATE ) );
//
// /**
// * For time related rules we apply again the match logic
// *
// * @var $set Set
// */
// foreach ( $sets as $set ) {
// if ( ! $set->has_matching_rules( $object ) ) {
// /**
// * If the rules has no matches, we exclude it from the matched list
// */
// $index = array_search( $set->ID, $matched );
// unset( $matched[ $index ] );
// }
// }
$matched = array_values( $matched ); //We need this to reset the indexes
}
return $matched;
}
/**
* Returns sets with dynamic contexts
* Ex: sets that have Search Result Page or Blog Page as rules.
*
* @return array
*/
public static function get_for_non_object() {
$sets = array();
/**
* @var null|\WP_User $maybe_user
*/
$maybe_user = null;
if ( is_author() ) {
$maybe_user = get_user_by( 'slug', get_query_var( 'author_name' ) );
}
foreach ( static::get_items() as $set ) {
if ( $set->has_matching_rules( $maybe_user ) ) {
$sets[] = $set->ID;
}
}
return $sets;
}
}

View File

@@ -0,0 +1,227 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Term_Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Term_Rule extends Rule {
private $option_fields
= array(
'tva_courses' => array(
self::FIELD_DIFFICULTY,
self::FIELD_TOPIC,
self::FIELD_LABEL,
),
);
/**
* Should be extended in child classes
*
* @param string $query_string
* @param bool|int $paged if non-false, it will return limited results
* @param int $per_page number of results per page. ignored if $paged = false
*
* @return array
*/
public function get_items( $query_string = '', $paged = false, $per_page = 15 ) {
/**
* Needed for the filter
*/
$this->query_string = $query_string;
$response = array();
if ( $this->should_search_options() ) {
$response = $this->search_option_fields();
} elseif ( $this->should_search_users() ) {
$response = parent::search_users();
} else {
add_filter( 'terms_clauses', array( $this, 'filter_terms_clauses' ), 10, 3 );
$query = new \WP_Term_Query();
$args = array(
'taxonomy' => $this->content,
'hide_empty' => 0,
);
if ( $paged !== false ) {
$args['number'] = $per_page;
$args['offset'] = ( $paged - 1 ) * $per_page;
}
$terms = $query->query( $args );
remove_filter( 'terms_clauses', array( $this, 'filter_terms_clauses' ), 10 );
foreach ( $terms as $term ) {
/**
* Allow other plugins to hook here and remove the term from the content set dropdown
*
* @param boolean return value
* @param \WP_Term $term
*/
if ( ! apply_filters( 'tvd_content_sets_allow_select_term', true, $term ) ) {
continue;
}
$response[] = array(
'id' => $term->term_id,
'text' => $this->alter_frontend_title( $term->name, $this->get_term_status( $term ) ),
);
}
}
return $response;
}
/**
* @param \WP_Term $term
*
* @return string
*/
private function get_term_status( $term ) {
/**
* Returns the status for the term
*
* @param string $status
* @param \WP_Term $term
*/
return apply_filters( 'tvd_content_sets_get_term_status', 'publish', $term );
}
/**
* @param $pieces
* @param $taxonomies
* @param $args
*
* @return mixed
*/
public function filter_terms_clauses( $pieces, $taxonomies, $args ) {
global $wpdb;
if ( $this->field === self::FIELD_TITLE ) {
$operation = $this->operator === self::OPERATOR_IS ? 'LIKE' : 'NOT LIKE';
$pieces['where'] .= ' AND lower(name) ' . $operation . ' ' . '\'%' . esc_sql( $wpdb->esc_like( strtolower( $this->query_string ) ) ) . '%\' ';
}
return $pieces;
}
/**
* Prepares the item for front-end
*
* @param int $item
*
* @return array
*/
public function get_frontend_item( $item ) {
if ( $this->should_search_options() ) {
$items = $this->get_option_fields();
if ( empty( $items ) || empty( $items[ $item ] ) ) {
return array();
}
return array(
'id' => (string) $item,
'text' => $items[ $item ],
);
}
if ( $this->should_search_users() ) {
return parent::get_frontend_user( $item );
}
return $this->get_frontend_term( $item );
}
/**
* Constructs the item from a term, needed for front-end
*
* @param int $item
*
* @return array
*/
public function get_frontend_term( $item ) {
$term = get_term( $item );
if ( empty( $term ) || is_wp_error( $term ) ) {
return array();
}
return array(
'id' => $term->term_id,
'text' => $this->alter_frontend_title( $term->name, $this->get_term_status( $term ) ),
);
}
/**
* @return array
*/
private function search_option_fields() {
$items = $this->get_option_fields();
$response = array();
foreach ( $items as $ID => $title ) {
if ( stripos( $title, $this->query_string ) !== false ) {
$response[] = array(
'id' => (string) $ID, //can be also 0 from the DB
'text' => $title,
);
}
}
return $response;
}
/**
* @return array
*/
private function get_option_fields() {
return apply_filters( 'tvd_content_sets_get_option_fields', [], $this );
}
/**
* Returns true if the system should search the option table for values
*
* @return bool
*/
private function should_search_options() {
return ! empty( $this->option_fields[ $this->content ] ) && in_array( $this->field, $this->option_fields[ $this->content ] );
}
/**
* Test if a rule matches the given params
*
* @param int|string $value
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function match_value( $value, $post_or_term ) {
if ( $this->should_search_options() || $this->should_search_users() ) {
$field_value = apply_filters( 'tvd_content_sets_field_value', '', $this, $post_or_term );
if ( $this->operator === self::OPERATOR_IS ) {
return in_array( $field_value, $this->value );
}
return ! in_array( $field_value, $this->value );
}
return parent::match_value( $value, $post_or_term );
}
}

View File

@@ -0,0 +1,52 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Set_Utils
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Utils {
/**
* Currently, only supports taxonomy terms and posts
*
* @return bool
*/
public static function is_context_supported() {
return is_singular() || is_tax() || is_category() || is_home() || is_search() || is_author();
}
/**
* @param \WP_Post| \WP_Term $post_or_term
*
* @return array
*/
public static function get_post_or_term_parts( $post_or_term ) {
$type = '';
$id = 0;
if ( $post_or_term instanceof \WP_Post ) {
$type = $post_or_term->post_type;
$id = $post_or_term->ID;
} else if ( $post_or_term instanceof \WP_Term ) {
$type = $post_or_term->taxonomy;
$id = $post_or_term->term_id;
}
return array(
$type,
$id,
);
}
}

View File

@@ -0,0 +1,249 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TVD_Content_Sets_Controller
*
* @project: thrive-dashboard
*/
class TVD_Content_Sets_Controller extends WP_REST_Controller {
/**
* @var string Base name
*/
public $rest_base = 'content-sets';
protected $version = 1;
protected $namespace = 'tss/v';
/**
* Register routes function
*/
public function register_routes() {
register_rest_route( $this->namespace . $this->version, $this->rest_base . '/normalize-rule', array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'normalize_rule' ),
'permission_callback' => array( $this, 'permission_check' ),
'args' => array(
'content_type' => array(
'type' => 'string',
'required' => true,
'enum' => array(
'post',
'term',
'archive',
\TVD\Content_Sets\Rule::HIDDEN_POST_TYPE_SEARCH_RESULTS,
\TVD\Content_Sets\Rule::HIDDEN_POST_TYPE_BLOG,
),
),
'content' => array(
'type' => 'string',
'required' => true,
),
'field' => array(
'type' => 'string',
'required' => true,
),
'operator' => array(
'type' => 'string',
'required' => true,
),
'value' => array(
'type' => array( 'string', 'array' ),
'default' => '',
),
),
),
) );
register_rest_route( $this->namespace . $this->version, $this->rest_base, array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'create_item' ),
'permission_callback' => array( $this, 'permission_check' ),
'args' => array(
'post_title' => array(
'type' => 'string',
'required' => true,
),
'post_content' => array(
'type' => 'array',
'required' => true,
),
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'search_items' ),
'permission_callback' => array( $this, 'permission_check' ),
'args' => array(
'content_type' => array(
'type' => 'string',
'required' => true,
'enum' => array( 'post', 'term', 'archive' ),
),
'content' => array(
'type' => 'string',
'required' => true,
),
'field' => array(
'type' => 'string',
'required' => true,
),
'operator' => array(
'type' => 'string',
'required' => true,
),
'value' => array(
'type' => array( 'string', 'array' ),
'default' => '',
),
'query_string' => array(
'type' => 'string',
'required' => true,
),
),
),
) );
register_rest_route( $this->namespace . $this->version, $this->rest_base . '/(?P<ID>.+)', array(
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_item' ),
'permission_callback' => array( $this, 'permission_check' ),
'args' => array(
'ID' => array(
'type' => 'integer',
'required' => true,
),
),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'update_item' ),
'permission_callback' => array( $this, 'permission_check' ),
'args' => array(
'ID' => array(
'type' => 'integer',
'required' => true,
),
'post_title' => array(
'type' => 'string',
'required' => true,
),
'post_content' => array(
'type' => 'array',
'required' => true,
),
),
),
) );
}
/**
* rename this into search posts/ search items
*
* @param WP_REST_Request $request
*
* @return WP_REST_Response
*/
public function search_items( $request ) {
$rule = \TVD\Content_Sets\Rule::factory( $request->get_params() );
$paged = false;
if ( $request->get_param( 'page' ) ) {
$paged = (int) $request->get_param( 'page' );
$per_page = (int) $request->get_param( 'per_page' ) ?: 150;
}
return new WP_REST_Response( $rule->get_items( $request->get_param( 'query_string' ), $paged, $per_page ), 200 );
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function create_item( $request ) {
$set = new \TVD\Content_Sets\Set( $request->get_params() );
$new_set_id = $set->create();
$cache_plugin = tve_dash_detect_cache_plugin();
if ( $cache_plugin ) {
tve_dash_cache_plugin_clear( $cache_plugin );
}
if ( ! empty( $new_set_id ) ) {
return new WP_REST_Response( \TVD\Content_Sets\Set::get_items(), 200 );
}
return new WP_Error( 'cant-create', __( 'The request contains invalid rules', 'thrive-dash' ), array( 'status' => 422 ) );
}
/**
* @param WP_REST_Request $request
*
* @return WP_REST_Response
*/
public function delete_item( $request ) {
$set = new \TVD\Content_Sets\Set( $request->get_param( 'ID' ) );
$response = $set->delete();
return new WP_REST_Response( $response, 200 );
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function update_item( $request ) {
$set = new \TVD\Content_Sets\Set( $request->get_params() );
$set_id = $set->update();
$cache_plugin = tve_dash_detect_cache_plugin();
if ( $cache_plugin ) {
tve_dash_cache_plugin_clear( $cache_plugin );
}
if ( ! empty( $set_id ) ) {
return new WP_REST_Response( ! empty( $request->get_param( 'return_one' ) ) ? $set->jsonSerialize() : \TVD\Content_Sets\Set::get_items(), 200 );
}
return new WP_Error( 'cant-update', __( 'The request contains invalid rules', 'thrive-dash' ), array( 'status' => 422 ) );
}
/**
* Normalizing a rule so that the rule contains all information
*
* Mainly this is for content_label rule key.
*
* @param WP_REST_Request $request
*
* @return WP_REST_Response
*/
public function normalize_rule( $request ) {
$rule = \TVD\Content_Sets\Rule::factory( $request->get_params() );
return new WP_REST_Response( $rule, 200 );
}
/**
* If the user has access to the admin pages, then he is allowed to perform any operation on the scripts.
*
* @return bool
*/
public function permission_check() {
return current_user_can( TVE_DASH_CAPABILITY );
}
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
class TVD_Fields_Controller extends TVD_REST_Controller {
/**
* @var string Base name
*/
public $base = 'fields';
/**
* Register Routes
*/
public function register_routes() {
register_rest_route( static::$namespace . static::$version, '/' . $this->base, array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'add_field' ),
'permission_callback' => array( $this, 'fields_permissions_check' ),
'args' => array(),
),
) );
register_rest_route( static::$namespace . static::$version, '/' . $this->base . '/(?P<id>[\d]+)', array(
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_field' ),
'permission_callback' => array( $this, 'fields_permissions_check' ),
'args' => array(),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'edit_field' ),
'permission_callback' => array( $this, 'fields_permissions_check' ),
'args' => array(),
),
) );
register_rest_route( static::$namespace . static::$version, '/' . $this->base . '/save_fields/', array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'save_fields' ),
'permission_callback' => array( $this, 'fields_permissions_check' ),
'args' => array(),
),
) );
}
/**
* Add multiple fields at once
*
* @param $request WP_REST_Request
*
* @return WP_Error|WP_REST_Response
*/
public function save_fields( $request ) {
$models = $request->get_param( 'models' );
$response = array();
foreach ( $models as $model ) {
$data = TVD_Smart_DB::save_field( $model, empty( $model['id'] )? 'insert': 'update' );
if ( $data ) {
$response[] = $data;
} else {
return new WP_Error( 'error', __( 'Something went wrong while saving the fields, please refresh and try again. If the problem persists please contact our support team.', 'thrive-dash' ) );
}
}
return new WP_REST_Response( $response, 200 );
}
/**
* @param $request WP_REST_Request
*
* @return WP_Error|WP_REST_Response
*/
public function add_field( $request ) {
$model = $request->get_params();
$model = TVD_Smart_DB::save_field( $model, 'insert' );
if ( $model ) {
return new WP_REST_Response( $model, 200 );
}
return new WP_Error( 'no-results', __( 'The group was not added, please try again !', 'thrive-dash' ) );
}
/**
* Delete a group and all it's fields
*
* @param $request
*
* @return WP_Error|WP_REST_Response
*/
public function delete_field( $request ) {
$id = $request->get_param( 'id' );
$result = TVD_Smart_DB::delete_field( $id );
if ( $result ) {
return new WP_REST_Response( true, 200 );
}
return new WP_Error( 'no-results', __( 'No field was deleted!', 'thrive-dash' ) );
}
/**
* Edit a group
*
* @param $request WP_REST_Request
*
* @return WP_Error|WP_REST_Response
*/
public function edit_field( $request ) {
$model = $request->get_params();
$model = TVD_Smart_DB::save_field( $model, 'update' );
if ( $model ) {
return new WP_REST_Response( $model, 200 );
}
return new WP_Error( 'no-results', __( 'No group was updated!', 'thrive-dash' ) );
}
/**
* Permissions check
*
* @param $request
*
* @return bool
*/
public function fields_permissions_check( $request ) {
return current_user_can( TVE_DASH_CAPABILITY );
}
}

View File

@@ -0,0 +1,116 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
class TVD_Groups_Controller extends TVD_REST_Controller {
/**
* @var string Base name
*/
public $base = 'groups';
/**
* Register Routes
*/
public function register_routes() {
register_rest_route( static::$namespace . static::$version, '/' . $this->base, array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'add_group' ),
'permission_callback' => array( $this, 'groups_permissions_check' ),
'args' => array(),
),
) );
register_rest_route( static::$namespace . static::$version, '/' . $this->base . '/(?P<id>[\d]+)', array(
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_group' ),
'permission_callback' => array( $this, 'groups_permissions_check' ),
'args' => array(),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'edit_group' ),
'permission_callback' => array( $this, 'groups_permissions_check' ),
'args' => array(),
),
) );
}
/**
* Add a group
*
* @param $request WP_REST_Request
*
* @return WP_Error|WP_REST_Response
*/
public function add_group( $request ) {
$model = $request->get_params();
$model = TVD_Smart_DB::insert_group( $model );
if ( $model ) {
return new WP_REST_Response( $model, 200 );
}
return new WP_Error( 'no-results', __( 'The group was not added, please try again !', 'thrive-dash' ) );
}
/**
* Delete a group and all it's fields
*
* @param $request
*
* @return WP_Error|WP_REST_Response
*/
public function delete_group( $request ) {
$id = $request->get_param( 'id' );
$result = TVD_Smart_DB::delete_group( $id );
if ( $result ) {
return new WP_REST_Response( true, 200 );
}
return new WP_Error( 'no-results', __( 'No group was deleted!', 'thrive-dash' ) );
}
/**
* Edit a group
*
* @param $request WP_REST_Request
*
* @return WP_Error|WP_REST_Response
*/
public function edit_group( $request ) {
$model = $request->get_params();
$model = TVD_Smart_DB::update_group( $model );
if ( $model ) {
return new WP_REST_Response( $model, 200 );
}
return new WP_Error( 'no-results', __( 'No group was updated!', 'thrive-dash' ) );
}
/**
* Permissions check
*
* @param $request
*
* @return bool
*/
public function groups_permissions_check( $request ) {
return current_user_can( TVE_DASH_CAPABILITY );
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div id="tvd-contents-sets">
<?php wp_nonce_field( TVD_Content_Sets::META_BOX_NONCE, 'tvd_content_sets_meta_box' ); ?>
<p>
<?php echo sprintf( __( 'If this post has been added to a content set by matching a dynamic rule, the content set will not be displayed here. View your content set rules from %s.', 'thrive-dash' ),
'<strong>' . __( 'Thrive Dashboard > Smart Site', 'thrive-dash' ) . '</strong>' ); ?>
</p>
<p>
<?php echo esc_html__( 'This post has been added directly to the following content sets:', 'thrive-dash' ); ?>
</p>
<div>
<input placeholder="<?php echo esc_html__( 'Search content sets', 'thrive-dash' ) ?>" id="tvd-content-sets-autocomplete"/>
</div>
<div id="tvd-matched-content-sets"></div>
</div>

View File

@@ -0,0 +1,34 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
return array(
'Thrive_Dashboard' => __( 'Thrive Dashboard', 'thrive-dash' ),
'SmartSite' => __( 'Smart Site', 'thrive-dash' ),
'InvalidName' => __( 'The name should not be empty', 'thrive-dash' ),
'InvalidInput' => __( 'This field should not be empty', 'thrive-dash' ),
'InvalidText' => __( 'The text value should not be empty', 'thrive-dash' ),
'InvalidAddress' => __( 'The address should not be empty', 'thrive-dash' ),
'InvalidCity' => __( 'The city should not be empty', 'thrive-dash' ),
'InvalidCountry' => __( 'The country should not be empty', 'thrive-dash' ),
'InvalidZip' => __( 'The zip should not be empty', 'thrive-dash' ),
'InvalidPhone' => __( 'The phone should not be empty', 'thrive-dash' ),
'NoEmail' => __( 'The email address should not be empty', 'thrive-dash' ),
'InvalidEmail' => __( 'The email address is not valid', 'thrive-dash' ),
'InvalidURL' => __( 'The URL is not valid', 'thrive-dash' ),
'NoURL' => __( 'The URL should not be empty', 'thrive-dash' ),
'GroupSaved' => __( 'The group was saved successfully', 'thrive-dash' ),
'FieldSaved' => __( 'The field was saved successfully', 'thrive-dash' ),
'FieldsSaved' => __( 'The fields were saved successfully', 'thrive-dash' ),
'ItemDeleted' => __( 'was deleted successfully', 'thrive-dash' ),
'InvalidLocation' => __( 'The location value should not be empty', 'thrive-dash' ),
'NoFields' => __( 'No fields in this group. Click the Add Field button to add a new field', 'thrive-dash' ),
);

View File

@@ -0,0 +1,14 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div>
<button class="click" data-fn="addNewRule">+&nbsp;<?php _e( 'Add more content', 'thrive-dash' ); ?></button>
</div>

View File

@@ -0,0 +1,30 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div>
<h3><?php _e( 'Add new content set', 'thrive-dash' ); ?></h3>
</div>
<div class="tvd-content-set-inner">
<div>
<input class="mb-30" id="tvd-content-set-name" placeholder="<?php echo __( 'Content set name', 'thrive-dash' ) ?>" type="text"
value="<#= model.get('post_title') #>">
</div>
<div>
<h4><?php _e( 'What content should be part of this set?', 'thrive-dash' ); ?></h4>
</div>
<div id="tvd-content-set-rules"></div>
<div id="tvd-content-set-add-rule"></div>
</div>
<div class="tvd-content-set-save-footer">
<button class="click" data-fn="saveContentSet">
<?php echo __( 'Save content set', 'thrive-dash' ); ?>
</button>
</div>

View File

@@ -0,0 +1,20 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div class="tvd-item-delete">
<div>
<?php echo esc_html__( 'Are you sure you want to delete this content set? This cannot be undone.', 'thrive-dash' ); ?>
</div>
<div>
<a href="javascript:void(0);" class="click" data-fn="cancel"><?php echo esc_html__( 'No, cancel', 'thrive-dash' ); ?></a>
<a href="javascript:void(0);" class="click" data-fn="confirm"><?php echo esc_html__( 'Yes, delete', 'thrive-dash' ); ?></a>
</div>
</div>

View File

@@ -0,0 +1,16 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div data-id="<#= model.get('ID') #>">
<#= model.get('post_title') #>
<button class="tvd-content-set-edit click" data-fn="edit"><?php echo dashboard_icon( 'edit' ) ?></button>
<button class="tvd-content-set-delete click" data-fn="delete"><?php echo dashboard_icon( 'delete' ) ?></button>
</div>

View File

@@ -0,0 +1,32 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div class="tvd-content-sets-header">
<h3>
<?php _e( 'Content Sets', 'thrive-dash' ); ?>
<span class="tvd-tooltip-parent">
<?php echo dashboard_icon( 'question-circle-full' ) ?>
<span class="tvd-ss-tooltip">
<?php _e( 'Define content sets that will be used site wide to control what content your customers have access to.', 'thrive-dash' ); ?><br>
<?php echo dashboard_icon( 'question-circle-full' ) ?>
<a href="#" target="_blank">
<?php _e('Show me how Content Sets work', 'thrive-dash') ?>
</a>
</span>
</span>
</h3>
<a href="javascript:void(0)" class="click tvd-tss-add-new-field" data-fn="addSet"><span><?php _e( 'Add Set', 'thrive-dash' ); ?></span></a>
</div>
<input type="text" class="tva-filter-sets input" data-fn="filterSets" placeholder="<?php esc_html_e( 'Filter content sets', 'thrive-dash' ); ?>"/>
<div class="tvd-content-sets-list"></div>
<div class="tvd-content-sets-list-pagination"></div>

View File

@@ -0,0 +1,12 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div class="tvd-content-set-form"></div>

View File

@@ -0,0 +1,18 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div class="tvd-rule-delete">
<div><?php echo esc_html__( 'Are you sure you want to delete this rule? This cannot be undone.', 'thrive-dash' ); ?></div>
<div>
<a href="javascript:void(0);" class="click" data-fn="cancel"><?php echo esc_html__( 'No, cancel', 'thrive-dash' ); ?></a>
<a href="javascript:void(0);" class="click" data-fn="confirm"><?php echo esc_html__( 'Yes, delete', 'thrive-dash' ); ?></a>
</div>
</div>

View File

@@ -0,0 +1,18 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div>
<div class="tvd-content-set-rule-wrapper tvd-rule-holder"></div>
<div class="tvd-content-set-delete-rule-wrapper">
<a href="javascript:void(0);" class="click" data-fn="deleteRule"><i class="tvd-icon-close2"></i></a>
</div>
</div>

View File

@@ -0,0 +1,23 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div class="tvd-cs-empty">
<?php echo dashboard_icon( 'content-sets' ) ?>
<p>
<b><?php echo esc_html__( 'No content sets added', 'thrive-dash' ); ?></b>
<br>
<?php
echo sprintf(
esc_html__( "Click %s button to create your first set", 'thrive-dash' ),
'<a href="javascript:void(0);" class="tva-add-button click" data-fn="addSet">+ ' . esc_html__( 'Add Set', 'thrive-dash' ) . '</a>'
); ?>
</p>
</div>

View File

@@ -0,0 +1,18 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div class="tvd-cs-empty">
<?php echo dashboard_icon( 'content-sets' ) ?>
<p>
<b><?php echo esc_html__( 'No content sets found based on your search', 'thrive-dash' ); ?></b>
<br>
</p>
</div>

View File

@@ -0,0 +1,24 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<# if( prev ) { #>
<a class="tva-prev click" data-fn="previousPage" href="javascript:void(0);"></a>
<# } else { #>
<span class="tva-prev"></span>
<# } #>
&nbsp;&nbsp;<#= currentPage #> <?php echo esc_html__( 'out of', 'thrive-dash' ) ?> <#= totalPages #>&nbsp;&nbsp;
<# if( next ) { #>
<a class="tva-next click" data-fn="nextPage" href="javascript:void(0);"></a>
<# } else { #>
<span class="tva-next"></span>
<# } #>

View File

@@ -0,0 +1,12 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<input placeholder="<?php echo esc_html__( 'Search for a date', 'thrive-dash' ) ?>" class="change tvd-date-picker" data-fn="change" type="text"/>

View File

@@ -0,0 +1,12 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<select class="tvd-browser-default"></select>

View File

@@ -0,0 +1,12 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<select class="change tvd-browser-default" data-fn="change"></select>

View File

@@ -0,0 +1,24 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div class="tvd-content-set-rule-step-widthin-the-last">
<div>
<input type="number" class="change input" data-fn="change" min="0"/>
</div>
<div>
<select class="tvd-browser-default change" data-fn="change">
<option value="days">Days</option>
<option value="weeks">Weeks</option>
<option value="month">Months</option>
<option value="years">Years</option>
</select>
</div>
</div>

View File

@@ -0,0 +1,21 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<ul class="tvd-ss-breadcrumbs">
<# links.each( function( item, index ) { item.has_link = index < links.size() - 1 #>
<li class="tvd-breadcrumb <#= ( item.has_link ? '' : ' tva-no-link' ) #>">
<# if ( item.has_link ) { #><a href="<#= item.get_url() #>"><# } #>
<#= _.escape ( item.get ( 'label' ) ) #>
<# if ( item.has_link ) { #></a><# } #>
</li>
<# } ) #>
</ul>

View File

@@ -0,0 +1,21 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<?php include_once TVE_DASH_PATH . '/css/font/dashboard-icons.svg' ?>
<div id="tvd-tss-wrap-all">
<div id="tvd-tss-header"></div>
<div class="tvd-tss-breadcrumbs-wrapper" id="tvd-tss-breadcrumbs-wrapper"></div>
<div id="tvd-tss-wrapper">
<div class="tvd-tss-menu"></div>
<div class="tvd-tss-content"></div>
</div>
</div>

View File

@@ -0,0 +1,64 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-name"><?php echo __( 'Field Name', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-name" autocomplete="address" class="input-change <#= model.get( 'default_field' )? 'tvd-disabled tvd-no-focus' : '' #>" type="text" value="<#= model.get('name') #>"
data-field="name"
placeholder="<?php echo __( 'Company Address', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-address1"><?php echo __( 'Address line 1', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-address1" autocomplete="address1-input" class="input-change" type="text" value="<#= model.get('data').address1 ? model.get('data').address1 : '' #>" data-field="data_address1" placeholder="e.g. Stevenage Rd">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-address2" id="tvd-tss-not-required"><?php echo __( 'Address line 2', 'thrive-dash' ) ?></label>
<div class="tvd-input-field">
<input id="tvd-tss-field-address2" autocomplete="address2-input" class="input-change" type="text" value="<#= model.get('data').address2 ? model.get('data').address2 : '' #>" data-field="data_address2" placeholder="e.g. Fulham">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-city"><?php echo __( 'City', 'thrive-dash' ) ?></label>
<div class="tvd-input-field">
<input id="tvd-tss-field-city" autocomplete="city-input" class="input-change" type="text" value="<#= model.get('data').city ? model.get('data').city : '' #>" data-field="data_city" placeholder="e.g. London">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-state"><?php echo __( 'County / State', 'thrive-dash' ) ?></label>
<div class="tvd-input-field">
<input id="tvd-tss-field-state" autocomplete="state-input" class="input-change" type="text" value="<#= model.get('data').state ? model.get('data').state : '' #>" data-field="data_state" placeholder="e.g. SW6">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-zip"><?php echo __( 'Zip / Postal Code', 'thrive-dash' ) ?></label>
<div class="tvd-input-field">
<input id="tvd-tss-field-zip" autocomplete="zip-input" class="input-change" type="text" value="<#= model.get('data').zip ? model.get('data').zip : '' #>" data-field="data_zip" placeholder="e.g. 6HH">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-country"><?php echo __( 'Country', 'thrive-dash' ) ?></label>
<div class="tvd-input-field">
<input id="tvd-tss-field-country" autocomplete="country-input" class="input-change" type="text" value="<#= model.get('data').country ? model.get('data').country : '' #>" data-field="data_country" placeholder="e.g. UK">
<label></label>
</div>
</div>

View File

@@ -0,0 +1,29 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-name"><?php echo __( 'Field Name', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-name" autocomplete="email" class="input-change <#= model.get( 'default_field' )? 'tvd-disabled tvd-no-focus' : '' #>" type="text" value="<#= model.get('name') #>"
data-field="name"
placeholder="<?php echo __( 'Email address', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-email"><?php echo __( 'Email Address', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-email" autocomplete="email-input" class="input-change" type="text" value="<#= model.get('data').email ? model.get('data').email : '' #>" data-field="data_email" placeholder="<?php echo __( 'email@someexample.com', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>

View File

@@ -0,0 +1,18 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-tss-list-item <#= model.get( 'just_added' ) ? 'tve-tss-just-added':'' #>">
<i class="tvd-tss-icon"><#= model.get('icon') #></i>
<span class="tvd-tss-label"><#= model.get('name') #></span>
<span class="tvd-tss-value"><#= Object.entries(model.get('formated_data')).length === 0 ? '<span class="unset">Not Set</span>' : model.get('formated_data') #></span>
<a class="tvd-tss-edit-field tvd-tss-icon" href="javascript:void(0)"><?php echo dashboard_icon( 'edit-new' ) ?></a>
<a class="tvd-tss-delete-field tvd-tss-icon <#= model.get('default_field')? 'tvd-disabled':'' #>" href="javascript:void(0)"><?php echo dashboard_icon( 'trash-new' ) ?></a>
</div>

View File

@@ -0,0 +1,25 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-collapsible-header tvd-active">
<span><#= model.get('name') #></span>
<span class="tvd-tss-edit-group tvd-tss-group-action"><?php echo dashboard_icon( 'edit-new' ) ?></span>
<span class="tvd-tss-delete-group tvd-tss-group-action"><?php echo dashboard_icon( 'trash-new' ) ?></span>
<span class="tvd-tss-hl"></span>
<a class="tvd-tss-expand" href="javascript:void(0)"><i class="tvd-icon-expanded"></i></a>
</div>
<div class="tvd-collapsible-body">
<div class="tvd-collapsible-body-content">
<ul class="tvd-tss-fields-wrapper"></ul>
</div>
</div>

View File

@@ -0,0 +1,37 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-name"><?php echo __( 'Field Name', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-name" autocomplete="link" class="input-change <#= model.get( 'default_field' )? 'tvd-disabled tvd-no-focus' : '' #>" type="text" value="<#= model.get('name') #>"
data-field="name"
placeholder="<?php echo __( 'Company website', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-text"><?php echo __( 'Link Text', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-text" autocomplete="link-input" class="input-change" type="text" value="<#= model.get('data').text ? model.get('data').text : '' #>" data-field="data_text" placeholder="<?php echo __( 'e.g Thrive Themes', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-url"><?php echo __( 'Link URL', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-url" autocomplete="url" class="input-change" type="text" value="<#= model.get('data').url ? model.get('data').url : '' #>" data-field="data_url" placeholder="<?php echo __( 'e.g thrivethemes.com', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>

View File

@@ -0,0 +1,34 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-tss-field-data">
<span><?php echo __( 'Field Name', 'thrive-dash' ) ?></span>
<div class="tvd-input-field">
<input id="tvd-tss-field-name" autocomplete="location" class="input-change <#= model.get( 'default_field' )? 'tvd-disabled tvd-no-focus' : '' #>" type="text" value="<#= model.get('name') #>"
data-field="name"
placeholder="<?php echo __( 'Company Location', 'thrive-dash' ) ?>">
<label for="tvd-tss-field-name"><?php echo __( 'Field Name', 'thrive-dash' ) ?></label>
</div>
</div>
<div class="tvd-tss-field-data">
<span><?php echo __( 'Field Value', 'thrive-dash' ) ?></span>
<div class="tvd-input-field">
<input id="tvd-tss-field-location" class="input-change" type="text" value="<#= model.get('data').location ? model.get('data').location : '' #>"
data-field="data_location" placeholder="<?php echo __( 'e.g New York', 'thrive-dash' ) ?>">
<label for="tvd-tss-field-text"><?php echo __( 'Location', 'thrive-dash' ) ?></label>
</div>
<div id="tvd-tss-google-map">
</div>
</div>

View File

@@ -0,0 +1,14 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-group-option tvd-pointer" data-id="<#= model.get( 'id' ) #>">
<span><#= model.get( 'name' ) #></span>
</div>

View File

@@ -0,0 +1,15 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-field-type-option tvd-pointer" data-id="<#= model.get( 'key' ) #>" data-name="<#= model.get( 'name' ) #>">
<i><#= model.get( 'icon' ) #></i>
<span><#= model.get( 'name' ) #></span>
</div>

View File

@@ -0,0 +1,33 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-modal-content">
<h3 class="tvd-modal-title"><?php echo __( 'Delete', 'thrive-dash' ) ?> <strong><#= model.get('name') #></strong> - <#= model.get('type') ? 'Field' : 'Group' #></h3>
<# if( model.get('type') ) {#>
<p><?php echo __( 'Are you sure you want to delete this field? This will affect any content on the site that contains this particular field.', 'thrive-dash' ) ?></p>
<# } else {#>
<p><?php echo __( 'Are you sure you want to delete this field group? Any fields that are part of this field group will also be deleted.', 'thrive-dash' ) ?></p>
<# } #>
</div>
<div class="tvd-modal-footer">
<div class="tvd-tss-flex">
<a href="javascript:void(0)"
class="tvd-btn-flat tvd-btn-flat-secondary tvd-btn-flat-dark tvd-waves-effect tvd-modal-close">
<?php echo __( 'Cancel', 'thrive-dash' ) ?>
</a>
<a href="javascript:void(0)"
class="tvd-btn tvd-btn-red tvd-waves-light tvd-right tvd-delete-item">
<?php echo __( 'Yes', 'thrive-dash' ) ?>
</a>
</div>
</div>

View File

@@ -0,0 +1,40 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-tss-modal-header">
<h3 class="tvd-modal-title"><?php echo __( 'Edit Group', 'thrive-dash' ) ?></h3>
</div>
<div class="tvd-modal-content tvd-tss-add">
<div class="tvd-tss-field-data-wrapper">
<div>
<label for="tvd-tss-group-name">
<?php echo __( 'Group name', 'thrive-dash' ) ?> *
</label>
<div class="tvd-input-field">
<input id="tvd-tss-group-name" type="text" value="<#= model.get('name') #>" data-field="name" placeholder="<?php echo __( 'Group name', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>
</div>
</div>
<div class="tvd-tss-modal-footer">
<div class="tvd-tss-flex">
<button class="tvd-btn-flat tvd-btn-flat-secondary tvd-btn-flat-dark tvd-waves-effect tvd-modal-close">
<?php echo __( 'Cancel', 'thrive-dash' ) ?>
</button>
<button class="tvd-btn tvd-btn-green tvd-waves-light tvd-right tvd-modal-submit">
<?php echo __( 'Save', 'thrive-dash' ) ?>
</button>
</div>
</div>

View File

@@ -0,0 +1,89 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-tss-modal-header">
<h3 class="tvd-modal-title">
<# if( model.get('type') ){ #>
<?php echo __( 'Edit Field', 'thrive-dash' ) ?>
<# } else{ #>
<?php echo __( 'Add Field', 'thrive-dash' ) ?>
<# } #>
</h3>
</div>
<div class="tvd-modal-content tvd-tss-add">
<div class="tvd-tss-group-wrapper">
<p><?php _e( 'Which Group should this Global Field be added to?', 'thrive-dash' ) ?> *</p>
<div class="tvd-input-field">
<div id="tvd-tss-group-name" data-field="group_id" class="tvd-complex-dropdown-trigger <#= Number( model.get( 'is_default' ) ) ? 'tvd-disabled' : '' #>">
<# if( group.get('name') ){ #>
<#= group.get('name') #>
<# } else{ #>
<?php echo __( 'Select the field group', 'thrive-dash' ) ?>
<# } #>
</div>
<label></label>
</div>
<div class="tvd-select-group-dropdown tvd-complex-dropdown-content tvd-hide">
<div class="tvd-global-field-group-list"></div>
<div id="tvd-add-new-group" class="tvd-add-new-group">
<p>
<span><?php dashboard_icon( 'plus-circle-regular' ); ?></span>
<span><?php echo __( 'Add New Group', 'thrive-dash' ) ?></span>
</p>
</div>
<div id="tvd-input-new-group" class="tvd-save-new-group tvd-hide">
<div class="tvd-input-field">
<input id="tvd-add-group" data-field="name" type="text" value="" placeholder="Complete the group name">
<label></label>
</div>
<div class="tvd-add-new-group" id="tvd-save-new-group">
<p>
<span><?php dashboard_icon( 'check-light' ); ?></span>
<span><?php echo __( 'Save Group', 'thrive-dash' ) ?></span>
</p>
</div>
</div>
</div>
</div>
<div class="tvd-tss-field-wrapper">
<p><?php _e( 'What type of field is this?', 'thrive-dash' ) ?> *</p>
<div class="tvd-input-field">
<div id="tvd-tss-field-type" data-field="type"
class=" tvd-complex-dropdown-trigger <#= model.get( 'default_field' )? 'tvd-disabled tvd-no-focus' : '' #>">
<# if( model.get('type') ){ #>
<i><#= SmartSite.data.fieldTypes[model.get('type')].icon #></i>
<span><#= SmartSite.utils.get_field_type_name( parseInt( model.get('type') ) ) #></span>
<# } else{ #>
<?php echo __( 'Select the field type', 'thrive-dash' ) ?>
<# } #>
</div>
</div>
<div class="tvd-select-type-dropdown tvd-complex-dropdown-content tvd-hide">
<div class="tvd-global-field-type-list"></div>
</div>
</div>
<div class="tvd-tss-field-data-wrapper"></div>
</div>
<div class="tvd-tss-modal-footer">
<div class="tvd-tss-flex">
<button class="tvd-btn-flat tvd-btn-flat-secondary tvd-btn-flat-dark tvd-waves-effect tvd-modal-close">
<?php echo __( 'Cancel', 'thrive-dash' ) ?>
</button>
<button class="tvd-btn tvd-btn-green tvd-waves-light tvd-right tvd-modal-submit tvd-tss-save-field" id="tvd-tss-save-field">
<?php echo __( 'Save', 'thrive-dash' ) ?>
</button>
</div>
</div>

View File

@@ -0,0 +1,29 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-name"><?php echo __( 'Field Name', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-name" autocomplete="phone" class="input-change <#= model.get( 'default_field' )? 'tvd-disabled tvd-no-focus' : '' #>" type="text" value="<#= model.get('name') #>"
data-field="name"
placeholder="<?php echo __( 'Company Phone', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-phone"><?php echo __( 'Phone Number', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-phone" autocomplete="phone-input" class="input-change" type="text" value="<#= model.get('data').phone ? model.get('data').phone : '' #>" data-field="data_phone" placeholder="<?php echo __( '555-555-555', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>

View File

@@ -0,0 +1,29 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-name" ><?php echo __( 'Field Name', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-name" autocomplete="text" class="input-change <#= model.get( 'default_field' )? 'tvd-disabled tvd-no-focus' : '' #>" type="text" value="<#= model.get('name') #>"
data-field="name"
placeholder="<?php echo __( 'Company name', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>
<div class="tvd-tss-field-data">
<label for="tvd-tss-field-text"><?php echo __( 'Text', 'thrive-dash' ) ?> *</label>
<div class="tvd-input-field">
<input id="tvd-tss-field-text" autocomplete="text-input" class="input-change" type="text" value="<#= model.get('data').text ? model.get('data').text : '' #>" data-field="data_text" placeholder="<?php echo __( 'e.g WidgetCorp', 'thrive-dash' ) ?>">
<label></label>
</div>
</div>

View File

@@ -0,0 +1,32 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<div class="tvd-tss-fields-content">
<div class="tvd-tss-fixed-top">
<h3><?php _e('Global Fields', 'thrive-dash') ?></h3>
<a href="javascript:void(0)" class="tvd-tss-add-new-field"><span><?php _e('Add Field', 'thrive-dash') ?></span></a>
</div>
<div>
<p>
<?php _e('Add global information that can be used in many places on your site. Your company address, for example', 'thrive-dash') ?>
</p>
<a href="https://thrivethemes.com/tkb_item/how-to-use-the-global-fields-feature/" target="_blank" class="tvd-tss-wizard-button">
<?php echo dashboard_icon( 'question-circle' ) ?>
<span>
<?php _e('Show me how Global Fields Work', 'thrive-dash') ?>
</span>
</a>
</div>
<ul class="tvd-tss-groups-wrapper tvd-collapsible tvd-expandable" data-collapsible="" data-easing="easeInOutQuart" data-duration="500"></ul>
</div>

View File

@@ -0,0 +1,16 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<h1 id="tvd-tss-title">
<?php echo dashboard_icon( 'ss-logo' ) ?>
<?php _e( 'Smart Site', 'thrive-dash' ); ?>
</h1>

View File

@@ -0,0 +1,34 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<ul>
<li class="<#= page === 'global-fields' ? 'active' : '' #>">
<a href="#global-fields">
<?php echo dashboard_icon( 'global-fields' ) ?>
<span>
<?php _e( 'Global Fields', 'thrive-dash' ); ?>
</span>
</a>
</li>
<?php if ( tvd_content_sets()->show_ui() ) : ?>
<li class="<#= page === 'content-sets' ? 'active' : '' #>">
<a href="#content-sets">
<?php echo dashboard_icon( 'content-sets' ) ?>
<span>
<?php _e( 'Content Sets', 'thrive-dash' ); ?>
</span>
</a>
</li>
<?php endif; ?>
</ul>
<a href="<?php echo admin_url( 'admin.php?page=tve_dash_section' ); ?>" class="tvd-waves-effect tvd-waves-light tvd-btn-small tvd-btn-gray tvd-tss-back">
<?php echo __( "Back To Dashboard", 'thrive-dash' ); ?>
</a>

View File

@@ -0,0 +1,29 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
?>
<ul>
<li class="<#= page == 'global_fields' ? 'tvd-selected' : '' #>">
<a href="#global_fields">
<i><?php echo dashboard_icon( 'format-align-justify' ) ?></i><?php _e( 'Global Fields', 'thrive-dash' ); ?>
</a>
</li>
<!-- <li class="<#= page == 'dynamic_fields' ? 'tvd-selected' : '' #>">-->
<!-- <a href="#dynamic_fields">-->
<!-- <i>--><?php //echo dashboard_icon( 'dynamic-list' ) ?><!--</i> --><?php //_e( 'Dynamic Fields', 'thrive-dash' ); ?>
<!-- </a>-->
<!-- </li>-->
<!-- <li class="<#= page == 'visitor_targeting' ? 'tvd-selected' : '' #>">-->
<!-- <a href="#visitor_targeting">-->
<!-- <i>--><?php //echo dashboard_icon( 'crosshairs-regular' ) ?><!--</i> --><?php //_e( 'Visitor Targeting', 'thrive-dash' ); ?>
<!-- </a>-->
<!-- </li>-->
</ul>