commit
2fcac212ae
619
assets/3rd/jquery-blockui/jquery.blockUI.js
Normal file
619
assets/3rd/jquery-blockui/jquery.blockUI.js
Normal file
@ -0,0 +1,619 @@
|
||||
/*!
|
||||
* jQuery blockUI plugin
|
||||
* Version 2.70.0-2014.11.23
|
||||
* Requires jQuery v1.7 or later
|
||||
*
|
||||
* Examples at: http://malsup.com/jquery/block/
|
||||
* Copyright (c) 2007-2013 M. Alsup
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
* Thanks to Amir-Hossein Sobhi for some excellent contributions!
|
||||
*/
|
||||
;(function() {
|
||||
/*jshint eqeqeq:false curly:false latedef:false */
|
||||
"use strict";
|
||||
|
||||
function setup($) {
|
||||
$.fn._fadeIn = $.fn.fadeIn;
|
||||
|
||||
var noOp = $.noop || function() {};
|
||||
|
||||
// this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle
|
||||
// confusing userAgent strings on Vista)
|
||||
var msie = /MSIE/.test(navigator.userAgent);
|
||||
var ie6 = /MSIE 6.0/.test(navigator.userAgent) && ! /MSIE 8.0/.test(navigator.userAgent);
|
||||
var mode = document.documentMode || 0;
|
||||
var setExpr = $.isFunction( document.createElement('div').style.setExpression );
|
||||
|
||||
// global $ methods for blocking/unblocking the entire page
|
||||
$.blockUI = function(opts) { install(window, opts); };
|
||||
$.unblockUI = function(opts) { remove(window, opts); };
|
||||
|
||||
// convenience method for quick growl-like notifications (http://www.google.com/search?q=growl)
|
||||
$.growlUI = function(title, message, timeout, onClose) {
|
||||
var $m = $('<div class="growlUI"></div>');
|
||||
if (title) $m.append('<h1>'+title+'</h1>');
|
||||
if (message) $m.append('<h2>'+message+'</h2>');
|
||||
if (timeout === undefined) timeout = 3000;
|
||||
|
||||
// Added by konapun: Set timeout to 30 seconds if this growl is moused over, like normal toast notifications
|
||||
var callBlock = function(opts) {
|
||||
opts = opts || {};
|
||||
|
||||
$.blockUI({
|
||||
message: $m,
|
||||
fadeIn : typeof opts.fadeIn !== 'undefined' ? opts.fadeIn : 700,
|
||||
fadeOut: typeof opts.fadeOut !== 'undefined' ? opts.fadeOut : 1000,
|
||||
timeout: typeof opts.timeout !== 'undefined' ? opts.timeout : timeout,
|
||||
centerY: false,
|
||||
showOverlay: false,
|
||||
onUnblock: onClose,
|
||||
css: $.blockUI.defaults.growlCSS
|
||||
});
|
||||
};
|
||||
|
||||
callBlock();
|
||||
var nonmousedOpacity = $m.css('opacity');
|
||||
$m.mouseover(function() {
|
||||
callBlock({
|
||||
fadeIn: 0,
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
var displayBlock = $('.blockMsg');
|
||||
displayBlock.stop(); // cancel fadeout if it has started
|
||||
displayBlock.fadeTo(300, 1); // make it easier to read the message by removing transparency
|
||||
}).mouseout(function() {
|
||||
$('.blockMsg').fadeOut(1000);
|
||||
});
|
||||
// End konapun additions
|
||||
};
|
||||
|
||||
// plugin method for blocking element content
|
||||
$.fn.block = function(opts) {
|
||||
if ( this[0] === window ) {
|
||||
$.blockUI( opts );
|
||||
return this;
|
||||
}
|
||||
var fullOpts = $.extend({}, $.blockUI.defaults, opts || {});
|
||||
this.each(function() {
|
||||
var $el = $(this);
|
||||
if (fullOpts.ignoreIfBlocked && $el.data('blockUI.isBlocked'))
|
||||
return;
|
||||
$el.unblock({ fadeOut: 0 });
|
||||
});
|
||||
|
||||
return this.each(function() {
|
||||
if ($.css(this,'position') == 'static') {
|
||||
this.style.position = 'relative';
|
||||
$(this).data('blockUI.static', true);
|
||||
}
|
||||
this.style.zoom = 1; // force 'hasLayout' in ie
|
||||
install(this, opts);
|
||||
});
|
||||
};
|
||||
|
||||
// plugin method for unblocking element content
|
||||
$.fn.unblock = function(opts) {
|
||||
if ( this[0] === window ) {
|
||||
$.unblockUI( opts );
|
||||
return this;
|
||||
}
|
||||
return this.each(function() {
|
||||
remove(this, opts);
|
||||
});
|
||||
};
|
||||
|
||||
$.blockUI.version = 2.70; // 2nd generation blocking at no extra cost!
|
||||
|
||||
// override these in your code to change the default behavior and style
|
||||
$.blockUI.defaults = {
|
||||
// message displayed when blocking (use null for no message)
|
||||
message: '<h1>Please wait...</h1>',
|
||||
|
||||
title: null, // title string; only used when theme == true
|
||||
draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded)
|
||||
|
||||
theme: false, // set to true to use with jQuery UI themes
|
||||
|
||||
// styles for the message when blocking; if you wish to disable
|
||||
// these and use an external stylesheet then do this in your code:
|
||||
// $.blockUI.defaults.css = {};
|
||||
css: {
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
width: '30%',
|
||||
top: '40%',
|
||||
left: '35%',
|
||||
textAlign: 'center',
|
||||
color: '#000',
|
||||
border: '3px solid #aaa',
|
||||
backgroundColor:'#fff',
|
||||
cursor: 'wait'
|
||||
},
|
||||
|
||||
// minimal style set used when themes are used
|
||||
themedCSS: {
|
||||
width: '30%',
|
||||
top: '40%',
|
||||
left: '35%'
|
||||
},
|
||||
|
||||
// styles for the overlay
|
||||
overlayCSS: {
|
||||
backgroundColor: '#000',
|
||||
opacity: 0.6,
|
||||
cursor: 'wait'
|
||||
},
|
||||
|
||||
// style to replace wait cursor before unblocking to correct issue
|
||||
// of lingering wait cursor
|
||||
cursorReset: 'default',
|
||||
|
||||
// styles applied when using $.growlUI
|
||||
growlCSS: {
|
||||
width: '350px',
|
||||
top: '10px',
|
||||
left: '',
|
||||
right: '10px',
|
||||
border: 'none',
|
||||
padding: '5px',
|
||||
opacity: 0.6,
|
||||
cursor: 'default',
|
||||
color: '#fff',
|
||||
backgroundColor: '#000',
|
||||
'-webkit-border-radius':'10px',
|
||||
'-moz-border-radius': '10px',
|
||||
'border-radius': '10px'
|
||||
},
|
||||
|
||||
// IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w
|
||||
// (hat tip to Jorge H. N. de Vasconcelos)
|
||||
/*jshint scripturl:true */
|
||||
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank',
|
||||
|
||||
// force usage of iframe in non-IE browsers (handy for blocking applets)
|
||||
forceIframe: false,
|
||||
|
||||
// z-index for the blocking overlay
|
||||
baseZ: 1000,
|
||||
|
||||
// set these to true to have the message automatically centered
|
||||
centerX: true, // <-- only effects element blocking (page block controlled via css above)
|
||||
centerY: true,
|
||||
|
||||
// allow body element to be stetched in ie6; this makes blocking look better
|
||||
// on "short" pages. disable if you wish to prevent changes to the body height
|
||||
allowBodyStretch: true,
|
||||
|
||||
// enable if you want key and mouse events to be disabled for content that is blocked
|
||||
bindEvents: true,
|
||||
|
||||
// be default blockUI will supress tab navigation from leaving blocking content
|
||||
// (if bindEvents is true)
|
||||
constrainTabKey: true,
|
||||
|
||||
// fadeIn time in millis; set to 0 to disable fadeIn on block
|
||||
fadeIn: 200,
|
||||
|
||||
// fadeOut time in millis; set to 0 to disable fadeOut on unblock
|
||||
fadeOut: 400,
|
||||
|
||||
// time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock
|
||||
timeout: 0,
|
||||
|
||||
// disable if you don't want to show the overlay
|
||||
showOverlay: true,
|
||||
|
||||
// if true, focus will be placed in the first available input field when
|
||||
// page blocking
|
||||
focusInput: true,
|
||||
|
||||
// elements that can receive focus
|
||||
focusableElements: ':input:enabled:visible',
|
||||
|
||||
// suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity)
|
||||
// no longer needed in 2012
|
||||
// applyPlatformOpacityRules: true,
|
||||
|
||||
// callback method invoked when fadeIn has completed and blocking message is visible
|
||||
onBlock: null,
|
||||
|
||||
// callback method invoked when unblocking has completed; the callback is
|
||||
// passed the element that has been unblocked (which is the window object for page
|
||||
// blocks) and the options that were passed to the unblock call:
|
||||
// onUnblock(element, options)
|
||||
onUnblock: null,
|
||||
|
||||
// callback method invoked when the overlay area is clicked.
|
||||
// setting this will turn the cursor to a pointer, otherwise cursor defined in overlayCss will be used.
|
||||
onOverlayClick: null,
|
||||
|
||||
// don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493
|
||||
quirksmodeOffsetHack: 4,
|
||||
|
||||
// class name of the message block
|
||||
blockMsgClass: 'blockMsg',
|
||||
|
||||
// if it is already blocked, then ignore it (don't unblock and reblock)
|
||||
ignoreIfBlocked: false
|
||||
};
|
||||
|
||||
// private data and functions follow...
|
||||
|
||||
var pageBlock = null;
|
||||
var pageBlockEls = [];
|
||||
|
||||
function install(el, opts) {
|
||||
var css, themedCSS;
|
||||
var full = (el == window);
|
||||
var msg = (opts && opts.message !== undefined ? opts.message : undefined);
|
||||
opts = $.extend({}, $.blockUI.defaults, opts || {});
|
||||
|
||||
if (opts.ignoreIfBlocked && $(el).data('blockUI.isBlocked'))
|
||||
return;
|
||||
|
||||
opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {});
|
||||
css = $.extend({}, $.blockUI.defaults.css, opts.css || {});
|
||||
if (opts.onOverlayClick)
|
||||
opts.overlayCSS.cursor = 'pointer';
|
||||
|
||||
themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {});
|
||||
msg = msg === undefined ? opts.message : msg;
|
||||
|
||||
// remove the current block (if there is one)
|
||||
if (full && pageBlock)
|
||||
remove(window, {fadeOut:0});
|
||||
|
||||
// if an existing element is being used as the blocking content then we capture
|
||||
// its current place in the DOM (and current display style) so we can restore
|
||||
// it when we unblock
|
||||
if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) {
|
||||
var node = msg.jquery ? msg[0] : msg;
|
||||
var data = {};
|
||||
$(el).data('blockUI.history', data);
|
||||
data.el = node;
|
||||
data.parent = node.parentNode;
|
||||
data.display = node.style.display;
|
||||
data.position = node.style.position;
|
||||
if (data.parent)
|
||||
data.parent.removeChild(node);
|
||||
}
|
||||
|
||||
$(el).data('blockUI.onUnblock', opts.onUnblock);
|
||||
var z = opts.baseZ;
|
||||
|
||||
// blockUI uses 3 layers for blocking, for simplicity they are all used on every platform;
|
||||
// layer1 is the iframe layer which is used to supress bleed through of underlying content
|
||||
// layer2 is the overlay layer which has opacity and a wait cursor (by default)
|
||||
// layer3 is the message content that is displayed while blocking
|
||||
var lyr1, lyr2, lyr3, s;
|
||||
if (msie || opts.forceIframe)
|
||||
lyr1 = $('<iframe class="blockUI" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+opts.iframeSrc+'"></iframe>');
|
||||
else
|
||||
lyr1 = $('<div class="blockUI" style="display:none"></div>');
|
||||
|
||||
if (opts.theme)
|
||||
lyr2 = $('<div class="blockUI blockOverlay ui-widget-overlay" style="z-index:'+ (z++) +';display:none"></div>');
|
||||
else
|
||||
lyr2 = $('<div class="blockUI blockOverlay" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
|
||||
|
||||
if (opts.theme && full) {
|
||||
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+(z+10)+';display:none;position:fixed">';
|
||||
if ( opts.title ) {
|
||||
s += '<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || ' ')+'</div>';
|
||||
}
|
||||
s += '<div class="ui-widget-content ui-dialog-content"></div>';
|
||||
s += '</div>';
|
||||
}
|
||||
else if (opts.theme) {
|
||||
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+(z+10)+';display:none;position:absolute">';
|
||||
if ( opts.title ) {
|
||||
s += '<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || ' ')+'</div>';
|
||||
}
|
||||
s += '<div class="ui-widget-content ui-dialog-content"></div>';
|
||||
s += '</div>';
|
||||
}
|
||||
else if (full) {
|
||||
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage" style="z-index:'+(z+10)+';display:none;position:fixed"></div>';
|
||||
}
|
||||
else {
|
||||
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement" style="z-index:'+(z+10)+';display:none;position:absolute"></div>';
|
||||
}
|
||||
lyr3 = $(s);
|
||||
|
||||
// if we have a message, style it
|
||||
if (msg) {
|
||||
if (opts.theme) {
|
||||
lyr3.css(themedCSS);
|
||||
lyr3.addClass('ui-widget-content');
|
||||
}
|
||||
else
|
||||
lyr3.css(css);
|
||||
}
|
||||
|
||||
// style the overlay
|
||||
if (!opts.theme /*&& (!opts.applyPlatformOpacityRules)*/)
|
||||
lyr2.css(opts.overlayCSS);
|
||||
lyr2.css('position', full ? 'fixed' : 'absolute');
|
||||
|
||||
// make iframe layer transparent in IE
|
||||
if (msie || opts.forceIframe)
|
||||
lyr1.css('opacity',0.0);
|
||||
|
||||
//$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el);
|
||||
var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el);
|
||||
$.each(layers, function() {
|
||||
this.appendTo($par);
|
||||
});
|
||||
|
||||
if (opts.theme && opts.draggable && $.fn.draggable) {
|
||||
lyr3.draggable({
|
||||
handle: '.ui-dialog-titlebar',
|
||||
cancel: 'li'
|
||||
});
|
||||
}
|
||||
|
||||
// ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
|
||||
var expr = setExpr && (!$.support.boxModel || $('object,embed', full ? null : el).length > 0);
|
||||
if (ie6 || expr) {
|
||||
// give body 100% height
|
||||
if (full && opts.allowBodyStretch && $.support.boxModel)
|
||||
$('html,body').css('height','100%');
|
||||
|
||||
// fix ie6 issue when blocked element has a border width
|
||||
if ((ie6 || !$.support.boxModel) && !full) {
|
||||
var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
|
||||
var fixT = t ? '(0 - '+t+')' : 0;
|
||||
var fixL = l ? '(0 - '+l+')' : 0;
|
||||
}
|
||||
|
||||
// simulate fixed position
|
||||
$.each(layers, function(i,o) {
|
||||
var s = o[0].style;
|
||||
s.position = 'absolute';
|
||||
if (i < 2) {
|
||||
if (full)
|
||||
s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.support.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"');
|
||||
else
|
||||
s.setExpression('height','this.parentNode.offsetHeight + "px"');
|
||||
if (full)
|
||||
s.setExpression('width','jQuery.support.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"');
|
||||
else
|
||||
s.setExpression('width','this.parentNode.offsetWidth + "px"');
|
||||
if (fixL) s.setExpression('left', fixL);
|
||||
if (fixT) s.setExpression('top', fixT);
|
||||
}
|
||||
else if (opts.centerY) {
|
||||
if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
|
||||
s.marginTop = 0;
|
||||
}
|
||||
else if (!opts.centerY && full) {
|
||||
var top = (opts.css && opts.css.top) ? parseInt(opts.css.top, 10) : 0;
|
||||
var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"';
|
||||
s.setExpression('top',expression);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// show the message
|
||||
if (msg) {
|
||||
if (opts.theme)
|
||||
lyr3.find('.ui-widget-content').append(msg);
|
||||
else
|
||||
lyr3.append(msg);
|
||||
if (msg.jquery || msg.nodeType)
|
||||
$(msg).show();
|
||||
}
|
||||
|
||||
if ((msie || opts.forceIframe) && opts.showOverlay)
|
||||
lyr1.show(); // opacity is zero
|
||||
if (opts.fadeIn) {
|
||||
var cb = opts.onBlock ? opts.onBlock : noOp;
|
||||
var cb1 = (opts.showOverlay && !msg) ? cb : noOp;
|
||||
var cb2 = msg ? cb : noOp;
|
||||
if (opts.showOverlay)
|
||||
lyr2._fadeIn(opts.fadeIn, cb1);
|
||||
if (msg)
|
||||
lyr3._fadeIn(opts.fadeIn, cb2);
|
||||
}
|
||||
else {
|
||||
if (opts.showOverlay)
|
||||
lyr2.show();
|
||||
if (msg)
|
||||
lyr3.show();
|
||||
if (opts.onBlock)
|
||||
opts.onBlock.bind(lyr3)();
|
||||
}
|
||||
|
||||
// bind key and mouse events
|
||||
bind(1, el, opts);
|
||||
|
||||
if (full) {
|
||||
pageBlock = lyr3[0];
|
||||
pageBlockEls = $(opts.focusableElements,pageBlock);
|
||||
if (opts.focusInput)
|
||||
setTimeout(focus, 20);
|
||||
}
|
||||
else
|
||||
center(lyr3[0], opts.centerX, opts.centerY);
|
||||
|
||||
if (opts.timeout) {
|
||||
// auto-unblock
|
||||
var to = setTimeout(function() {
|
||||
if (full)
|
||||
$.unblockUI(opts);
|
||||
else
|
||||
$(el).unblock(opts);
|
||||
}, opts.timeout);
|
||||
$(el).data('blockUI.timeout', to);
|
||||
}
|
||||
}
|
||||
|
||||
// remove the block
|
||||
function remove(el, opts) {
|
||||
var count;
|
||||
var full = (el == window);
|
||||
var $el = $(el);
|
||||
var data = $el.data('blockUI.history');
|
||||
var to = $el.data('blockUI.timeout');
|
||||
if (to) {
|
||||
clearTimeout(to);
|
||||
$el.removeData('blockUI.timeout');
|
||||
}
|
||||
opts = $.extend({}, $.blockUI.defaults, opts || {});
|
||||
bind(0, el, opts); // unbind events
|
||||
|
||||
if (opts.onUnblock === null) {
|
||||
opts.onUnblock = $el.data('blockUI.onUnblock');
|
||||
$el.removeData('blockUI.onUnblock');
|
||||
}
|
||||
|
||||
var els;
|
||||
if (full) // crazy selector to handle odd field errors in ie6/7
|
||||
els = $(document.body).children().filter('.blockUI').add('body > .blockUI');
|
||||
else
|
||||
els = $el.find('>.blockUI');
|
||||
|
||||
// fix cursor issue
|
||||
if ( opts.cursorReset ) {
|
||||
if ( els.length > 1 )
|
||||
els[1].style.cursor = opts.cursorReset;
|
||||
if ( els.length > 2 )
|
||||
els[2].style.cursor = opts.cursorReset;
|
||||
}
|
||||
|
||||
if (full)
|
||||
pageBlock = pageBlockEls = null;
|
||||
|
||||
if (opts.fadeOut) {
|
||||
count = els.length;
|
||||
els.stop().fadeOut(opts.fadeOut, function() {
|
||||
if ( --count === 0)
|
||||
reset(els,data,opts,el);
|
||||
});
|
||||
}
|
||||
else
|
||||
reset(els, data, opts, el);
|
||||
}
|
||||
|
||||
// move blocking element back into the DOM where it started
|
||||
function reset(els,data,opts,el) {
|
||||
var $el = $(el);
|
||||
if ( $el.data('blockUI.isBlocked') )
|
||||
return;
|
||||
|
||||
els.each(function(i,o) {
|
||||
// remove via DOM calls so we don't lose event handlers
|
||||
if (this.parentNode)
|
||||
this.parentNode.removeChild(this);
|
||||
});
|
||||
|
||||
if (data && data.el) {
|
||||
data.el.style.display = data.display;
|
||||
data.el.style.position = data.position;
|
||||
data.el.style.cursor = 'default'; // #59
|
||||
if (data.parent)
|
||||
data.parent.appendChild(data.el);
|
||||
$el.removeData('blockUI.history');
|
||||
}
|
||||
|
||||
if ($el.data('blockUI.static')) {
|
||||
$el.css('position', 'static'); // #22
|
||||
}
|
||||
|
||||
if (typeof opts.onUnblock == 'function')
|
||||
opts.onUnblock(el,opts);
|
||||
|
||||
// fix issue in Safari 6 where block artifacts remain until reflow
|
||||
var body = $(document.body), w = body.width(), cssW = body[0].style.width;
|
||||
body.width(w-1).width(w);
|
||||
body[0].style.width = cssW;
|
||||
}
|
||||
|
||||
// bind/unbind the handler
|
||||
function bind(b, el, opts) {
|
||||
var full = el == window, $el = $(el);
|
||||
|
||||
// don't bother unbinding if there is nothing to unbind
|
||||
if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked')))
|
||||
return;
|
||||
|
||||
$el.data('blockUI.isBlocked', b);
|
||||
|
||||
// don't bind events when overlay is not in use or if bindEvents is false
|
||||
if (!full || !opts.bindEvents || (b && !opts.showOverlay))
|
||||
return;
|
||||
|
||||
// bind anchors and inputs for mouse and key events
|
||||
var events = 'mousedown mouseup keydown keypress keyup touchstart touchend touchmove';
|
||||
if (b)
|
||||
$(document).bind(events, opts, handler);
|
||||
else
|
||||
$(document).unbind(events, handler);
|
||||
|
||||
// former impl...
|
||||
// var $e = $('a,:input');
|
||||
// b ? $e.bind(events, opts, handler) : $e.unbind(events, handler);
|
||||
}
|
||||
|
||||
// event handler to suppress keyboard/mouse events when blocking
|
||||
function handler(e) {
|
||||
// allow tab navigation (conditionally)
|
||||
if (e.type === 'keydown' && e.keyCode && e.keyCode == 9) {
|
||||
if (pageBlock && e.data.constrainTabKey) {
|
||||
var els = pageBlockEls;
|
||||
var fwd = !e.shiftKey && e.target === els[els.length-1];
|
||||
var back = e.shiftKey && e.target === els[0];
|
||||
if (fwd || back) {
|
||||
setTimeout(function(){focus(back);},10);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
var opts = e.data;
|
||||
var target = $(e.target);
|
||||
if (target.hasClass('blockOverlay') && opts.onOverlayClick)
|
||||
opts.onOverlayClick(e);
|
||||
|
||||
// allow events within the message content
|
||||
if (target.parents('div.' + opts.blockMsgClass).length > 0)
|
||||
return true;
|
||||
|
||||
// allow events for content that is not being blocked
|
||||
return target.parents().children().filter('div.blockUI').length === 0;
|
||||
}
|
||||
|
||||
function focus(back) {
|
||||
if (!pageBlockEls)
|
||||
return;
|
||||
var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
|
||||
if (e)
|
||||
e.focus();
|
||||
}
|
||||
|
||||
function center(el, x, y) {
|
||||
var p = el.parentNode, s = el.style;
|
||||
var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth');
|
||||
var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth');
|
||||
if (x) s.left = l > 0 ? (l+'px') : '0';
|
||||
if (y) s.top = t > 0 ? (t+'px') : '0';
|
||||
}
|
||||
|
||||
function sz(el, p) {
|
||||
return parseInt($.css(el,p),10)||0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*global define:true */
|
||||
if (typeof define === 'function' && define.amd && define.amd.jQuery) {
|
||||
define(['jquery'], setup);
|
||||
} else {
|
||||
setup(jQuery);
|
||||
}
|
||||
|
||||
})();
|
14
assets/3rd/jquery-blockui/jquery.blockUI.min.js
vendored
Normal file
14
assets/3rd/jquery-blockui/jquery.blockUI.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
36
assets/3rd/opal-clipboard.js
Normal file
36
assets/3rd/opal-clipboard.js
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Simple text copy functions using native browser clipboard capabilities.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set the user's clipboard contents.
|
||||
*
|
||||
* @param string data: Text to copy to clipboard.
|
||||
* @param object $el: jQuery element to trigger copy events on. (Default: document)
|
||||
*/
|
||||
function opalSetClipboard( data, $el ) {
|
||||
if ( 'undefined' === typeof $el ) {
|
||||
$el = jQuery( document );
|
||||
}
|
||||
var $temp_input = jQuery( '<textarea style="opacity:0">' );
|
||||
jQuery( 'body' ).append( $temp_input );
|
||||
$temp_input.val( data ).select();
|
||||
|
||||
$el.trigger( 'beforecopy' );
|
||||
try {
|
||||
document.execCommand( 'copy' );
|
||||
$el.trigger( 'aftercopy' );
|
||||
} catch ( err ) {
|
||||
$el.trigger( 'aftercopyfailure' );
|
||||
}
|
||||
|
||||
$temp_input.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the user's clipboard.
|
||||
*/
|
||||
function opalClearClipboard() {
|
||||
opalSetClipboard( '' );
|
||||
}
|
@ -40,6 +40,7 @@ jQuery(document).ready(function($){
|
||||
load_select2_member( '#opalestate_ppt_agent', 'opalestate_search_agents' );
|
||||
load_select2_member( '#opalestate_ppt_agency', 'opalestate_search_agencies' );
|
||||
load_select2_member( '#p-assignment #post_author_override', 'opalestate_search_property_users' );
|
||||
load_select2_member( '.opalestate-customer-search', 'opalestate_search_property_users' );
|
||||
|
||||
function formatRepo (repo) {
|
||||
if ( repo.loading ) {
|
||||
|
141
assets/js/api-keys.js
Normal file
141
assets/js/api-keys.js
Normal file
@ -0,0 +1,141 @@
|
||||
/*global jQuery, Backbone, _, opalestate_admin_api_keys, opalSetClipboard, opalClearClipboard */
|
||||
(function( $ ) {
|
||||
|
||||
var APIView = Backbone.View.extend({
|
||||
/**
|
||||
* Element
|
||||
*
|
||||
* @param {Object} '#key-fields'
|
||||
*/
|
||||
el: $( '#key-fields' ),
|
||||
|
||||
/**
|
||||
* Events
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
events: {
|
||||
'click input#update_api_key': 'saveKey'
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize actions
|
||||
*/
|
||||
initialize: function(){
|
||||
_.bindAll( this, 'saveKey' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Init jQuery.BlockUI
|
||||
*/
|
||||
block: function() {
|
||||
$( this.el ).block({
|
||||
message: null,
|
||||
overlayCSS: {
|
||||
background: '#fff',
|
||||
opacity: 0.6
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove jQuery.BlockUI
|
||||
*/
|
||||
unblock: function() {
|
||||
$( this.el ).unblock();
|
||||
},
|
||||
|
||||
/**
|
||||
* Init TipTip
|
||||
*/
|
||||
initTipTip: function( css_class ) {
|
||||
$( document.body )
|
||||
.on( 'click', css_class, function( evt ) {
|
||||
evt.preventDefault();
|
||||
if ( ! document.queryCommandSupported( 'copy' ) ) {
|
||||
$( css_class ).parent().find( 'input' ).focus().select();
|
||||
$( '#copy-error' ).text( opalestate_admin_api_keys.clipboard_failed );
|
||||
} else {
|
||||
$( '#copy-error' ).text( '' );
|
||||
opalClearClipboard();
|
||||
opalSetClipboard( $.trim( $( this ).prev( 'input' ).val() ), $( css_class ) );
|
||||
}
|
||||
} )
|
||||
.on( 'aftercopy', css_class, function() {
|
||||
$( '#copy-error' ).text( '' );
|
||||
$( css_class ).tipTip( {
|
||||
'attribute': 'data-tip',
|
||||
'activation': 'focus',
|
||||
'fadeIn': 50,
|
||||
'fadeOut': 50,
|
||||
'delay': 0
|
||||
} ).focus();
|
||||
} )
|
||||
.on( 'aftercopyerror', css_class, function() {
|
||||
$( css_class ).parent().find( 'input' ).focus().select();
|
||||
$( '#copy-error' ).text( opalestate_admin_api_keys.clipboard_failed );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Save API Key using ajax
|
||||
*
|
||||
* @param {Object} e
|
||||
*/
|
||||
saveKey: function( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
var self = this;
|
||||
|
||||
self.block();
|
||||
|
||||
Backbone.ajax({
|
||||
method: 'POST',
|
||||
dataType: 'json',
|
||||
url: opalestate_admin_api_keys.ajax_url,
|
||||
data: {
|
||||
action: 'opalestate_update_api_key',
|
||||
security: opalestate_admin_api_keys.update_api_nonce,
|
||||
key_id: $( '#key_id', self.el ).val(),
|
||||
description: $( '#key_description', self.el ).val(),
|
||||
user: $( '#key_user', self.el ).val(),
|
||||
permissions: $( '#key_permissions', self.el ).val()
|
||||
},
|
||||
success: function( response ) {
|
||||
$( '.wc-api-message', self.el ).remove();
|
||||
|
||||
if ( response.success ) {
|
||||
var data = response.data;
|
||||
|
||||
$( 'h2, h3', self.el ).first().append( '<div class="wc-api-message updated"><p>' + data.message + '</p></div>' );
|
||||
|
||||
if ( 0 < data.consumer_key.length && 0 < data.consumer_secret.length ) {
|
||||
$( '#api-keys-options', self.el ).remove();
|
||||
$( 'p.submit', self.el ).empty().append( data.revoke_url );
|
||||
|
||||
var template = wp.template( 'api-keys-template' );
|
||||
|
||||
$( 'p.submit', self.el ).before( template({
|
||||
consumer_key: data.consumer_key,
|
||||
consumer_secret: data.consumer_secret
|
||||
}) );
|
||||
self.initTipTip( '.copy-key' );
|
||||
self.initTipTip( '.copy-secret' );
|
||||
} else {
|
||||
$( '#key_description', self.el ).val( data.description );
|
||||
$( '#key_user', self.el ).val( data.user_id );
|
||||
$( '#key_permissions', self.el ).val( data.permissions );
|
||||
}
|
||||
} else {
|
||||
$( 'h2, h3', self.el ).first().append( '<div class="wc-api-message error"><p>' + response.data.message + '</p></div>' );
|
||||
}
|
||||
|
||||
self.unblock();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
new APIView();
|
||||
|
||||
})( jQuery );
|
@ -1,16 +1,14 @@
|
||||
jQuery( document ).ready( function ( $ ) {
|
||||
'use strict';
|
||||
|
||||
|
||||
var toggleSubmit = function ( _this ) {
|
||||
if( $( _this ).attr('disabled') == "disabled" ){
|
||||
if ( $( _this ).attr( 'disabled' ) == 'disabled' ) {
|
||||
$( _this ).removeAttr( 'disabled' );
|
||||
$( _this ).find( 'i' ).remove();
|
||||
} else {
|
||||
$( _this ).attr( 'disabled', 'disabled' );
|
||||
$( _this ).append( '<i class="fa fa-spinner fa-spin"></i>' );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$( '.opalestate-submission-tab' ).each( function () {
|
||||
@ -44,17 +42,14 @@ jQuery( document ).ready( function ( $ ) {
|
||||
});
|
||||
*/
|
||||
var submitFormFiles = function ( name, files ) {
|
||||
|
||||
var formData = new FormData();
|
||||
|
||||
formData.append( 'section', 'general' );
|
||||
// formData.append('action', 'opalestate_submitted_property');
|
||||
|
||||
|
||||
$(".cmb2-uploader-files").each( function(){
|
||||
$( '.cmb2-uploader-files' ).each( function () {
|
||||
var file_btn = $( 'input.select-file', this );
|
||||
|
||||
var files = $(".uploader-item-preview", this );
|
||||
var files = $( '.uploader-item-preview', this );
|
||||
|
||||
var name = $( this ).data( 'name' );
|
||||
var issingle = $( this ).data( 'single' );
|
||||
@ -64,14 +59,12 @@ jQuery( document ).ready( function ( $ ) {
|
||||
if ( issingle ) {
|
||||
formData.append( name, file );
|
||||
} else {
|
||||
formData.append( name+"["+i+"]", file );
|
||||
formData.append( name + '[' + i + ']', file );
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
// console.log( formData );
|
||||
|
||||
var dataSubmit = $submit_btn.parents( 'form' ).serializeArray();
|
||||
|
||||
$.each( dataSubmit, function ( key, input ) {
|
||||
@ -86,7 +79,7 @@ jQuery( document ).ready( function ( $ ) {
|
||||
type: 'POST',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
dataType: "json",
|
||||
dataType: 'json',
|
||||
success: function ( response ) {
|
||||
if ( response.status == true ) {
|
||||
if ( response.redirect ) {
|
||||
@ -114,14 +107,13 @@ jQuery( document ).ready( function ( $ ) {
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
$submit_btn.parents( 'form' ).on( 'submit', function () {
|
||||
submitFormFiles();
|
||||
return false;
|
||||
} );
|
||||
|
||||
|
||||
// Clicking Next button
|
||||
$next_btn.on( 'click', function ( e ) {
|
||||
e.preventDefault();
|
||||
@ -184,7 +176,8 @@ jQuery( document ).ready( function ( $ ) {
|
||||
e.preventDefault();
|
||||
var $el = $( this );
|
||||
var $prev_tab_item = $el.prev();
|
||||
if ( $el.hasClass( 'validated' ) || ($prev_tab_item.length != 0 && $prev_tab_item.hasClass( 'validated' ) &&
|
||||
if ( $el.hasClass( 'validated' ) ||
|
||||
( $prev_tab_item.length != 0 && $prev_tab_item.hasClass( 'validated' ) &&
|
||||
$prev_tab_item.hasClass( 'passed' ) ) ) {
|
||||
$submission_tab.find( '.opalestate-tab-content' ).removeClass( 'active' );
|
||||
$submission_tab.find( '.tab-item.active' ).removeClass( 'active' );
|
||||
|
@ -474,10 +474,11 @@ jQuery( document ).ready( function ( $ ) {
|
||||
var imax = $( '.slide-ranger-max-input', this ).val();
|
||||
var slider = $( '.slide-ranger-bar', this ).get( 0 );
|
||||
var unit_pos = $( this ).data( 'unitpos' );
|
||||
var unit_thousand = $( this ).data( 'thousand' );
|
||||
|
||||
var config_format = {
|
||||
decimals: decimals,
|
||||
thousand: ',',
|
||||
thousand: unit_thousand,
|
||||
};
|
||||
|
||||
if ( unit_pos == 'prefix' ) {
|
||||
|
@ -33,14 +33,35 @@ class Opalestate_Admin {
|
||||
* enqueue editor.js for edit mode
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
$screen = get_current_screen();
|
||||
$screen_id = $screen ? $screen->id : '';
|
||||
|
||||
wp_enqueue_style( 'opalestate-admin', OPALESTATE_PLUGIN_URL . 'assets/admin.css', [], '3.0.3' );
|
||||
|
||||
$suffix = '';
|
||||
wp_enqueue_style( 'select2', OPALESTATE_PLUGIN_URL . 'assets/3rd/select2/css/select2.min.css', null, '1.3' );
|
||||
wp_enqueue_script( 'select2', OPALESTATE_PLUGIN_URL . 'assets/3rd/select2/js/select2.min.js', null, '1.3', true );
|
||||
|
||||
wp_enqueue_script( 'opalestate-country-select', OPALESTATE_PLUGIN_URL . 'assets/js/country-select.js', [ 'jquery' ], null, true );
|
||||
wp_enqueue_script( 'opalestate-admin', OPALESTATE_PLUGIN_URL . 'assets/js/admin' . $suffix . '.js', [ 'jquery' ], null, true );
|
||||
wp_enqueue_script( 'opalestate-country-select', OPALESTATE_PLUGIN_URL . 'assets/js/country-select.js', [ 'jquery' ], OPALESTATE_VERSION, true );
|
||||
wp_enqueue_script( 'opalestate-admin', OPALESTATE_PLUGIN_URL . 'assets/js/admin' . $suffix . '.js', [ 'jquery' ], OPALESTATE_VERSION, true );
|
||||
wp_register_script( 'jquery-blockui', OPALESTATE_PLUGIN_URL . 'assets/3rd/jquery-blockui/jquery.blockUI' . $suffix . '.js', array( 'jquery' ), '2.70', true );
|
||||
wp_register_script( 'opal-clipboard', OPALESTATE_PLUGIN_URL . 'assets/3rd/opal-clipboard.js', array( 'jquery' ), OPALESTATE_VERSION, true );
|
||||
|
||||
// API settings.
|
||||
if ( 'opalestate_property_page_opalestate-settings' === $screen_id && isset( $_GET['tab'] ) && 'api_keys' == $_GET['tab'] ) {
|
||||
wp_register_script( 'opalestate-api-keys', OPALESTATE_PLUGIN_URL . 'assets/js/api-keys' . $suffix . '.js', array( 'jquery', 'opalestate-admin', 'underscore', 'backbone', 'wp-util', 'jquery-blockui', 'opal-clipboard' ),
|
||||
OPALESTATE_VERSION, true );
|
||||
wp_enqueue_script( 'opalestate-api-keys' );
|
||||
wp_localize_script(
|
||||
'opalestate-api-keys',
|
||||
'opalestate_admin_api_keys',
|
||||
array(
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'update_api_nonce' => wp_create_nonce( 'update-api-key' ),
|
||||
'clipboard_failed' => esc_html__( 'Copying to clipboard failed. Please press Ctrl/Cmd+C to copy.', 'opalestate-pro' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,11 +88,8 @@ class Opalestate_Admin {
|
||||
'settings/property.php',
|
||||
] );
|
||||
|
||||
//
|
||||
|
||||
// Get it started
|
||||
$Opalestate_Settings = new Opalestate_Plugin_Settings();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,329 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* API Key Table Class
|
||||
*
|
||||
* @package Opalestate
|
||||
* @subpackage Admin/Tools/APIKeys
|
||||
* @copyright Copyright (c) 2019, WordImpress
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 1.1
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Load WP_List_Table if not loaded
|
||||
if ( ! class_exists( 'WP_List_Table' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Opalestate_API_Keys_Table Class
|
||||
*
|
||||
* Renders the API Keys table
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
class Opalestate_API_Keys_Table extends WP_List_Table {
|
||||
|
||||
/**
|
||||
* @var int Number of items per page
|
||||
* @since 1.1
|
||||
*/
|
||||
public $per_page = 30;
|
||||
|
||||
/**
|
||||
* @var object Query results
|
||||
* @since 1.1
|
||||
*/
|
||||
private $keys;
|
||||
|
||||
/**
|
||||
* Get things started
|
||||
*
|
||||
* @since 1.1
|
||||
* @see WP_List_Table::__construct()
|
||||
*/
|
||||
public function __construct() {
|
||||
global $status, $page;
|
||||
|
||||
// Set parent defaults
|
||||
parent::__construct( [
|
||||
'singular' => esc_html__( 'API Key', 'opalestate-pro' ), // Singular name of the listed records
|
||||
'plural' => esc_html__( 'API Keys', 'opalestate-pro' ), // Plural name of the listed records
|
||||
'ajax' => false // Does this table support ajax?
|
||||
] );
|
||||
|
||||
$this->query();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function renders most of the columns in the list table.
|
||||
*
|
||||
* @access public
|
||||
* @param array $item Contains all the data of the keys
|
||||
* @param string $column_name The name of the column
|
||||
*
|
||||
* @return string Column Name
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function column_default( $item, $column_name ) {
|
||||
return $item[ $column_name ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the public key rows
|
||||
*
|
||||
* @access public
|
||||
* @param array $item Contains all the data of the keys
|
||||
* @param string $column_name The name of the column
|
||||
*
|
||||
* @return string Column Name
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function column_key( $item ) {
|
||||
return '<input onClick="this.setSelectionRange(0, this.value.length)" readonly="readonly" type="text" class="large-text" value="' . esc_attr( $item['key'] ) . '"/>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the token rows
|
||||
*
|
||||
* @access public
|
||||
* @param array $item Contains all the data of the keys
|
||||
* @param string $column_name The name of the column
|
||||
*
|
||||
* @return string Column Name
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function column_token( $item ) {
|
||||
return '<input onClick="this.setSelectionRange(0, this.value.length)" readonly="readonly" type="text" class="large-text" value="' . esc_attr( $item['token'] ) . '"/>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the secret key rows
|
||||
*
|
||||
* @access public
|
||||
* @param array $item Contains all the data of the keys
|
||||
* @param string $column_name The name of the column
|
||||
*
|
||||
* @return string Column Name
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function column_secret( $item ) {
|
||||
return '<input onClick="this.setSelectionRange(0, this.value.length)" readonly="readonly" type="text" class="large-text" value="' . esc_attr( $item['secret'] ) . '"/>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the column for the user field
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
* @since 1.1
|
||||
*/
|
||||
public function column_user( $item ) {
|
||||
|
||||
$actions = [];
|
||||
|
||||
if ( apply_filters( 'opalestate_api_log_requests', true ) ) {
|
||||
$actions['view'] = sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
esc_url( add_query_arg( [
|
||||
'view' => 'api_requests',
|
||||
'post_type' => 'opalestate_forms',
|
||||
'page' => 'opalestate-reports',
|
||||
'tab' => 'logs',
|
||||
's' => $item['email'],
|
||||
], 'edit.php' ) ),
|
||||
esc_html__( 'View API Log', 'opalestate-pro' )
|
||||
);
|
||||
}
|
||||
|
||||
$actions['reissue'] = sprintf(
|
||||
'<a href="%s" class="opalestate-regenerate-api-key">%s</a>',
|
||||
esc_url( wp_nonce_url( add_query_arg( [
|
||||
'user_id' => $item['id'],
|
||||
'opalestate_action' => 'process_api_key',
|
||||
'opalestate_api_process' => 'regenerate',
|
||||
] ), 'opalestate-api-nonce' ) ),
|
||||
esc_html__( 'Reissue', 'opalestate-pro' )
|
||||
);
|
||||
$actions['revoke'] = sprintf(
|
||||
'<a href="%s" class="opalestate-revoke-api-key opalestate-delete">%s</a>',
|
||||
esc_url( wp_nonce_url( add_query_arg( [
|
||||
'user_id' => $item['id'],
|
||||
'opalestate_action' => 'process_api_key',
|
||||
'opalestate_api_process' => 'revoke',
|
||||
] ), 'opalestate-api-nonce' ) ),
|
||||
esc_html__( 'Revoke', 'opalestate-pro' )
|
||||
);
|
||||
|
||||
$actions = apply_filters( 'opalestate_api_row_actions', array_filter( $actions ) );
|
||||
|
||||
return sprintf( '%1$s %2$s', $item['user'], $this->row_actions( $actions ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the table columns
|
||||
*
|
||||
* @access public
|
||||
* @return array $columns Array of all the list table columns
|
||||
* @since 1.1
|
||||
*/
|
||||
public function get_columns() {
|
||||
$columns = [
|
||||
'user' => esc_html__( 'Username', 'opalestate-pro' ),
|
||||
'key' => esc_html__( 'Public Key', 'opalestate-pro' ),
|
||||
'token' => esc_html__( 'Token', 'opalestate-pro' ),
|
||||
'secret' => esc_html__( 'Secret Key', 'opalestate-pro' ),
|
||||
];
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the table navigation above or below the table
|
||||
*
|
||||
* @param string $which
|
||||
* @since 3.1.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function display_tablenav( $which ) {
|
||||
if ( 'top' === $which ) {
|
||||
wp_nonce_field( 'bulk-' . $this->_args['plural'] );
|
||||
}
|
||||
?>
|
||||
<div class="tablenav <?php echo esc_attr( $which ); ?>">
|
||||
|
||||
<div class="alignleft actions bulkactions">
|
||||
<?php $this->bulk_actions( $which ); ?>
|
||||
</div>
|
||||
<?php
|
||||
$this->extra_tablenav( $which );
|
||||
$this->pagination( $which );
|
||||
?>
|
||||
|
||||
<br class="clear"/>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the key generation form
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
* @since 1.1
|
||||
*/
|
||||
public function bulk_actions( $which = '' ) {
|
||||
// These aren't really bulk actions but this outputs the markup in the right place
|
||||
static $opalestate_api_is_bottom;
|
||||
|
||||
if ( $opalestate_api_is_bottom ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<input type="hidden" name="opalestate_action" value="process_api_key"/>
|
||||
<input type="hidden" name="opalestate_api_process" value="generate"/>
|
||||
<?php wp_nonce_field( 'opalestate-api-nonce' ); ?>
|
||||
<?php echo OpalEstate()->html->ajax_user_search( [
|
||||
'name' => esc_html__( 'User', 'opalestate-pro' ),
|
||||
] ); ?>
|
||||
<?php submit_button( esc_html__( 'Generate New API Keys', 'opalestate-pro' ), 'secondary', 'submit', false ); ?>
|
||||
<?php
|
||||
$opalestate_api_is_bottom = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current page number
|
||||
*
|
||||
* @access public
|
||||
* @return int Current page number
|
||||
* @since 1.1
|
||||
*/
|
||||
public function get_paged() {
|
||||
return isset( $_GET['paged'] ) ? absint( $_GET['paged'] ) : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the key query
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
* @since 1.1
|
||||
*/
|
||||
public function query() {
|
||||
$users = get_users( [
|
||||
'meta_value' => 'opalestate_user_secret_key',
|
||||
'number' => $this->per_page,
|
||||
'offset' => $this->per_page * ( $this->get_paged() - 1 ),
|
||||
] );
|
||||
|
||||
$keys = [];
|
||||
|
||||
foreach ( $users as $user ) {
|
||||
$keys[ $user->ID ]['id'] = $user->ID;
|
||||
$keys[ $user->ID ]['email'] = $user->user_email;
|
||||
$keys[ $user->ID ]['user'] = '<a href="' . add_query_arg( 'user_id', $user->ID, 'user-edit.php' ) . '"><strong>' . $user->user_login . '</strong></a>';
|
||||
$keys[ $user->ID ]['key'] = OpalEstate()->api_admin->get_user_public_key( $user->ID );
|
||||
$keys[ $user->ID ]['secret'] = OpalEstate()->api_admin->get_user_secret_key( $user->ID );
|
||||
$keys[ $user->ID ]['token'] = OpalEstate()->api_admin->get_token( $user->ID );
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve count of total users with keys
|
||||
*
|
||||
* @access public
|
||||
* @return int
|
||||
* @since 1.1
|
||||
*/
|
||||
public function total_items() {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! get_transient( 'opalestate_total_api_keys' ) ) {
|
||||
$total_items = $wpdb->get_var( "SELECT count(user_id) FROM $wpdb->usermeta WHERE meta_value='opalestate_user_secret_key'" );
|
||||
|
||||
set_transient( 'opalestate_total_api_keys', $total_items, 60 * 60 );
|
||||
}
|
||||
|
||||
return get_transient( 'opalestate_total_api_keys' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the final data for the table
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
* @since 1.1
|
||||
*/
|
||||
public function prepare_items() {
|
||||
$columns = $this->get_columns();
|
||||
|
||||
$hidden = []; // No hidden columns
|
||||
$sortable = []; // Not sortable... for now
|
||||
|
||||
$this->_column_headers = [ $columns, $hidden, $sortable, 'id' ];
|
||||
|
||||
$data = $this->query();
|
||||
|
||||
$total_items = $this->total_items();
|
||||
|
||||
$this->items = $data;
|
||||
|
||||
$this->set_pagination_args( [
|
||||
'total_items' => $total_items,
|
||||
'per_page' => $this->per_page,
|
||||
'total_pages' => ceil( $total_items / $this->per_page ),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
@ -338,56 +338,6 @@ if ( ! function_exists( 'opalestate_license_key_callback' ) ) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the API Keys
|
||||
*
|
||||
* @return void
|
||||
* @since 2.0
|
||||
*/
|
||||
function opalestate_api_keys_callback() {
|
||||
if ( ! current_user_can( 'manage_opalestate_settings' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
do_action( 'opalestate_tools_api_keys_keys_before' );
|
||||
|
||||
require_once OPALESTATE_PLUGIN_DIR . 'inc/admin/class-api-keys-table.php';
|
||||
|
||||
$keys_table_list = new Opalestate_API_Keys_Table();
|
||||
$keys_table_list->prepare_items();
|
||||
|
||||
echo '<input type="hidden" name="page" value="wc-settings" />';
|
||||
echo '<input type="hidden" name="tab" value="api" />';
|
||||
echo '<input type="hidden" name="section" value="keys" />';
|
||||
|
||||
$keys_table_list->views();
|
||||
$keys_table_list->search_box( esc_html__( 'Search Key', 'opalestate-pro' ), 'key' );
|
||||
$keys_table_list->display();
|
||||
?>
|
||||
|
||||
|
||||
<p>
|
||||
<?php printf(
|
||||
__( 'API keys allow users to use the <a href="%s">Opalestate REST API</a> to retrieve donation data in JSON or XML for external applications or devices, such as <a href="%s">Zapi_keyser</a>.',
|
||||
'opalestate-pro' ),
|
||||
'https://wpopal.com/opalestate/documentation/opalestate-api_keys-reference/',
|
||||
'https://wpopal.com/addons/opalestate/'
|
||||
); ?>
|
||||
</p>
|
||||
|
||||
<style>
|
||||
.opalestate_properties_page_opalestate-settings .opalestate-submit-wrap {
|
||||
display: none; /* Hide Save settings button on System Info Tab (not needed) */
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
|
||||
do_action( 'opalestate_tools_api_keys_keys_after' );
|
||||
}
|
||||
|
||||
add_action( 'opalestate_settings_tab_api_keys_keys', 'opalestate_api_keys_callback' );
|
||||
|
||||
/**
|
||||
* Hook Callback
|
||||
*
|
||||
|
@ -46,9 +46,10 @@ class Opalestate_Plugin_Settings {
|
||||
*/
|
||||
protected $options_page = '';
|
||||
|
||||
protected $subtabs = array();
|
||||
protected $subtabs = [];
|
||||
|
||||
protected $setting_object = [];
|
||||
|
||||
protected $setting_object = array();
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -67,23 +68,32 @@ class Opalestate_Plugin_Settings {
|
||||
// add_action( 'cmb2_render_license_key', 'opalestate_license_key_callback', 10, 5 );
|
||||
add_action( "cmb2_save_options-page_fields", [ $this, 'settings_notices' ], 10, 3 );
|
||||
|
||||
|
||||
add_action( 'cmb2_render_api_keys', 'opalestate_api_keys_callback', 10, 5 );
|
||||
|
||||
// Include CMB CSS in the head to avoid FOUC
|
||||
add_action( "admin_print_styles-opalestate_properties_page_opalestate-settings", [ 'CMB2_hookup', 'enqueue_cmb_css' ] );
|
||||
}
|
||||
|
||||
public function admin_menu() {
|
||||
// Settings
|
||||
$opalestate_settings_page = add_submenu_page( 'edit.php?post_type=opalestate_property', esc_html__( 'Settings', 'opalestate-pro' ), esc_html__( 'Settings', 'opalestate-pro' ), 'manage_opalestate_settings',
|
||||
$opalestate_settings_page = add_submenu_page(
|
||||
'edit.php?post_type=opalestate_property',
|
||||
esc_html__( 'Opalestate Settings', 'opalestate-pro' ),
|
||||
esc_html__( 'Settings', 'opalestate-pro' ),
|
||||
'manage_opalestate_settings',
|
||||
'opalestate-settings',
|
||||
[ $this, 'admin_page_display' ] );
|
||||
|
||||
// addons setting
|
||||
$opalestate_settings_page = add_submenu_page( 'edit.php?post_type=opalestate_property', esc_html__( 'Addons', 'opalestate-pro' ), esc_html__( 'Addons', 'opalestate-pro' ), 'manage_options', 'opalestate-addons',
|
||||
do_action( 'load-' . $opalestate_settings_page, $this );
|
||||
|
||||
// Addons
|
||||
$opalestate_addons_page = add_submenu_page(
|
||||
'edit.php?post_type=opalestate_property',
|
||||
esc_html__( 'Opalestate Addons', 'opalestate-pro' ),
|
||||
esc_html__( 'Addons', 'opalestate-pro' ),
|
||||
'manage_options',
|
||||
'opalestate-addons',
|
||||
[ $this, 'admin_addons_page_display' ] );
|
||||
|
||||
do_action( 'load-' . $opalestate_addons_page, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,7 +103,6 @@ class Opalestate_Plugin_Settings {
|
||||
*/
|
||||
public function init() {
|
||||
register_setting( $this->key, $this->key );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,11 +142,12 @@ class Opalestate_Plugin_Settings {
|
||||
$tab_url = esc_url( add_query_arg( [
|
||||
'settings-updated' => false,
|
||||
'tab' => $tab_id,
|
||||
'subtab' => $stab_id
|
||||
'subtab' => $stab_id,
|
||||
] ) );
|
||||
|
||||
return $tab_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin page markup. Mostly handled by CMB2
|
||||
*
|
||||
@ -150,7 +160,7 @@ class Opalestate_Plugin_Settings {
|
||||
$sub_active_tab = isset( $_GET['subtab'] ) ? sanitize_text_field( $_GET['subtab'] ) : '';
|
||||
|
||||
$tabs_fields = $this->opalestate_settings( $active_tab );
|
||||
$sub_tabs_fields = array();
|
||||
$sub_tabs_fields = [];
|
||||
|
||||
if ( empty( $sub_active_tab ) && $this->subtabs ) {
|
||||
$first = array_flip( $this->subtabs );
|
||||
@ -166,12 +176,11 @@ class Opalestate_Plugin_Settings {
|
||||
<h2 class="nav-tab-wrapper">
|
||||
<?php
|
||||
foreach ( $this->opalestate_get_settings_tabs() as $tab_id => $tab_name ) {
|
||||
|
||||
$tab_url = esc_url( add_query_arg( [
|
||||
'settings-updated' => false,
|
||||
'tab' => $tab_id,
|
||||
'subtab' => false
|
||||
] ) );
|
||||
'subtab' => false,
|
||||
], admin_url( 'edit.php?post_type=opalestate_property&page=opalestate-settings' ) ) );
|
||||
|
||||
$active = $active_tab == $tab_id ? ' nav-tab-active' : '';
|
||||
|
||||
@ -230,8 +239,8 @@ class Opalestate_Plugin_Settings {
|
||||
'numberposts' => -1,
|
||||
] );
|
||||
|
||||
$general = array();
|
||||
$opalestate_settings = array();
|
||||
$general = [];
|
||||
$opalestate_settings = [];
|
||||
|
||||
//Return all settings array if necessary
|
||||
|
||||
@ -251,8 +260,10 @@ class Opalestate_Plugin_Settings {
|
||||
|
||||
return $tab->get_tab_content( $this->key );
|
||||
}
|
||||
return array( $active_tab => array() );
|
||||
|
||||
return [ $active_tab => [] ];
|
||||
}
|
||||
|
||||
// Add other tabs and settings fields as needed
|
||||
return $output;
|
||||
|
||||
|
@ -299,3 +299,105 @@ function opalestate_get_agent_property() {
|
||||
wp_reset_postdata();
|
||||
exit;
|
||||
}
|
||||
|
||||
function opalestate_update_api_key() {
|
||||
ob_start();
|
||||
|
||||
global $wpdb;
|
||||
|
||||
check_ajax_referer( 'update-api-key', 'security' );
|
||||
|
||||
if ( ! current_user_can( 'manage_opalestate_settings' ) ) {
|
||||
wp_die( -1 );
|
||||
}
|
||||
|
||||
$response = [];
|
||||
|
||||
try {
|
||||
if ( empty( $_POST['description'] ) ) {
|
||||
throw new Exception( __( 'Description is missing.', 'opalestate-pro' ) );
|
||||
}
|
||||
if ( empty( $_POST['user'] ) ) {
|
||||
throw new Exception( __( 'User is missing.', 'opalestate-pro' ) );
|
||||
}
|
||||
if ( empty( $_POST['permissions'] ) ) {
|
||||
throw new Exception( __( 'Permissions is missing.', 'opalestate-pro' ) );
|
||||
}
|
||||
|
||||
$key_id = isset( $_POST['key_id'] ) ? absint( $_POST['key_id'] ) : 0;
|
||||
$description = sanitize_text_field( wp_unslash( $_POST['description'] ) );
|
||||
$permissions = ( in_array( wp_unslash( $_POST['permissions'] ), [ 'read', 'write', 'read_write' ], true ) ) ? sanitize_text_field( wp_unslash( $_POST['permissions'] ) ) : 'read';
|
||||
$user_id = absint( $_POST['user'] );
|
||||
|
||||
// Check if current user can edit other users.
|
||||
if ( $user_id && ! current_user_can( 'edit_user', $user_id ) ) {
|
||||
if ( get_current_user_id() !== $user_id ) {
|
||||
throw new Exception( __( 'You do not have permission to assign API Keys to the selected user.', 'opalestate-pro' ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( 0 < $key_id ) {
|
||||
$data = [
|
||||
'user_id' => $user_id,
|
||||
'description' => $description,
|
||||
'permissions' => $permissions,
|
||||
];
|
||||
|
||||
$wpdb->update(
|
||||
$wpdb->prefix . 'opalestate_api_keys',
|
||||
$data,
|
||||
[ 'key_id' => $key_id ],
|
||||
[
|
||||
'%d',
|
||||
'%s',
|
||||
'%s',
|
||||
],
|
||||
[ '%d' ]
|
||||
);
|
||||
|
||||
$response = $data;
|
||||
$response['consumer_key'] = '';
|
||||
$response['consumer_secret'] = '';
|
||||
$response['message'] = __( 'API Key updated successfully.', 'opalestate-pro' );
|
||||
} else {
|
||||
$consumer_key = 'ck_' . opalestate_rand_hash();
|
||||
$consumer_secret = 'cs_' . opalestate_rand_hash();
|
||||
|
||||
$data = [
|
||||
'user_id' => $user_id,
|
||||
'description' => $description,
|
||||
'permissions' => $permissions,
|
||||
'consumer_key' => opalestate_api_hash( $consumer_key ),
|
||||
'consumer_secret' => $consumer_secret,
|
||||
'truncated_key' => substr( $consumer_key, -7 ),
|
||||
];
|
||||
|
||||
$wpdb->insert(
|
||||
$wpdb->prefix . 'opalestate_api_keys',
|
||||
$data,
|
||||
[
|
||||
'%d',
|
||||
'%s',
|
||||
'%s',
|
||||
'%s',
|
||||
'%s',
|
||||
'%s',
|
||||
]
|
||||
);
|
||||
|
||||
$key_id = $wpdb->insert_id;
|
||||
$response = $data;
|
||||
$response['consumer_key'] = $consumer_key;
|
||||
$response['consumer_secret'] = $consumer_secret;
|
||||
$response['message'] = __( 'API Key generated successfully. Make sure to copy your new keys now as the secret key will be hidden once you leave this page.', 'opalestate-pro' );
|
||||
$response['revoke_url'] = '<a style="color: #a00; text-decoration: none;" href="' . esc_url( wp_nonce_url( add_query_arg( [ 'revoke-key' => $key_id ],
|
||||
admin_url( 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys' ) ), 'revoke' ) ) . '">' . __( 'Revoke key', 'opalestate-pro' ) . '</a>';
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
wp_send_json_error( [ 'message' => $e->getMessage() ] );
|
||||
}
|
||||
|
||||
// wp_send_json_success must be outside the try block not to break phpunit tests.
|
||||
wp_send_json_success( $response );
|
||||
}
|
||||
add_action( 'wp_ajax_opalestate_update_api_key', 'opalestate_update_api_key' );
|
||||
|
272
inc/api/class-opalestate-admin-api-keys-table-list.php
Normal file
272
inc/api/class-opalestate-admin-api-keys-table-list.php
Normal file
@ -0,0 +1,272 @@
|
||||
<?php
|
||||
/**
|
||||
* Opalestate API Keys Table List
|
||||
*
|
||||
* @package Opalestate\Admin
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! class_exists( 'WP_List_Table' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* API Keys table list class.
|
||||
*/
|
||||
class Opalestate_Admin_API_Keys_Table_List extends WP_List_Table {
|
||||
|
||||
/**
|
||||
* Initialize the API key table list.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
[
|
||||
'singular' => 'key',
|
||||
'plural' => 'keys',
|
||||
'ajax' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* No items found text.
|
||||
*/
|
||||
public function no_items() {
|
||||
esc_html_e( 'No keys found.', 'opalestate-pro' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list columns.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_columns() {
|
||||
return [
|
||||
'cb' => '<input type="checkbox" />',
|
||||
'title' => __( 'Description', 'opalestate-pro' ),
|
||||
'truncated_key' => __( 'Consumer key ending in', 'opalestate-pro' ),
|
||||
'user' => __( 'User', 'opalestate-pro' ),
|
||||
'permissions' => __( 'Permissions', 'opalestate-pro' ),
|
||||
'last_access' => __( 'Last access', 'opalestate-pro' ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Column cb.
|
||||
*
|
||||
* @param array $key Key data.
|
||||
* @return string
|
||||
*/
|
||||
public function column_cb( $key ) {
|
||||
return sprintf( '<input type="checkbox" name="key[]" value="%1$s" />', $key['key_id'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return title column.
|
||||
*
|
||||
* @param array $key Key data.
|
||||
* @return string
|
||||
*/
|
||||
public function column_title( $key ) {
|
||||
$url = admin_url( 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys&edit-key=' . $key['key_id'] );
|
||||
$user_id = absint( $key['user_id'] );
|
||||
|
||||
// Check if current user can edit other users or if it's the same user.
|
||||
$can_edit = current_user_can( 'edit_user', $user_id ) || get_current_user_id() === $user_id;
|
||||
|
||||
$output = '<strong>';
|
||||
if ( $can_edit ) {
|
||||
$output .= '<a href="' . esc_url( $url ) . '" class="row-title">';
|
||||
}
|
||||
if ( empty( $key['description'] ) ) {
|
||||
$output .= esc_html__( 'API key', 'opalestate-pro' );
|
||||
} else {
|
||||
$output .= esc_html( $key['description'] );
|
||||
}
|
||||
if ( $can_edit ) {
|
||||
$output .= '</a>';
|
||||
}
|
||||
$output .= '</strong>';
|
||||
|
||||
// Get actions.
|
||||
$actions = [
|
||||
/* translators: %s: API key ID. */
|
||||
'id' => sprintf( __( 'ID: %d', 'opalestate-pro' ), $key['key_id'] ),
|
||||
];
|
||||
|
||||
if ( $can_edit ) {
|
||||
$actions['edit'] = '<a href="' . esc_url( $url ) . '">' . __( 'View/Edit', 'opalestate-pro' ) . '</a>';
|
||||
$actions['trash'] = '<a class="submitdelete" aria-label="' . esc_attr__( 'Revoke API key', 'opalestate-pro' ) . '" href="' . esc_url(
|
||||
wp_nonce_url(
|
||||
add_query_arg(
|
||||
[
|
||||
'revoke-key' => $key['key_id'],
|
||||
], admin_url( 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys' )
|
||||
), 'revoke'
|
||||
)
|
||||
) . '">' . esc_html__( 'Revoke', 'opalestate-pro' ) . '</a>';
|
||||
}
|
||||
|
||||
$row_actions = [];
|
||||
|
||||
foreach ( $actions as $action => $link ) {
|
||||
$row_actions[] = '<span class="' . esc_attr( $action ) . '">' . $link . '</span>';
|
||||
}
|
||||
|
||||
$output .= '<div class="row-actions">' . implode( ' | ', $row_actions ) . '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return truncated consumer key column.
|
||||
*
|
||||
* @param array $key Key data.
|
||||
* @return string
|
||||
*/
|
||||
public function column_truncated_key( $key ) {
|
||||
return '<code>…' . esc_html( $key['truncated_key'] ) . '</code>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user column.
|
||||
*
|
||||
* @param array $key Key data.
|
||||
* @return string
|
||||
*/
|
||||
public function column_user( $key ) {
|
||||
$user = get_user_by( 'id', $key['user_id'] );
|
||||
|
||||
if ( ! $user ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( current_user_can( 'edit_user', $user->ID ) ) {
|
||||
return '<a href="' . esc_url( add_query_arg( [ 'user_id' => $user->ID ], admin_url( 'user-edit.php' ) ) ) . '">' . esc_html( $user->display_name ) . '</a>';
|
||||
}
|
||||
|
||||
return esc_html( $user->display_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return permissions column.
|
||||
*
|
||||
* @param array $key Key data.
|
||||
* @return string
|
||||
*/
|
||||
public function column_permissions( $key ) {
|
||||
$permission_key = $key['permissions'];
|
||||
$permissions = [
|
||||
'read' => __( 'Read', 'opalestate-pro' ),
|
||||
'write' => __( 'Write', 'opalestate-pro' ),
|
||||
'read_write' => __( 'Read/Write', 'opalestate-pro' ),
|
||||
];
|
||||
|
||||
if ( isset( $permissions[ $permission_key ] ) ) {
|
||||
return esc_html( $permissions[ $permission_key ] );
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return last access column.
|
||||
*
|
||||
* @param array $key Key data.
|
||||
* @return string
|
||||
*/
|
||||
public function column_last_access( $key ) {
|
||||
if ( ! empty( $key['last_access'] ) ) {
|
||||
/* translators: 1: last access date 2: last access time */
|
||||
$date = sprintf( __( '%1$s at %2$s', 'opalestate-pro' ), date_i18n( wc_date_format(), strtotime( $key['last_access'] ) ),
|
||||
date_i18n( get_option( 'time_format' ), strtotime( $key['last_access'] ) ) );
|
||||
|
||||
return apply_filters( 'opalestate_api_key_last_access_datetime', $date, $key['last_access'] );
|
||||
}
|
||||
|
||||
return __( 'Unknown', 'opalestate-pro' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bulk actions.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_bulk_actions() {
|
||||
if ( ! current_user_can( 'remove_users' ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'revoke' => __( 'Revoke', 'opalestate-pro' ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Search box.
|
||||
*
|
||||
* @param string $text Button text.
|
||||
* @param string $input_id Input ID.
|
||||
*/
|
||||
public function search_box( $text, $input_id ) {
|
||||
if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) { // WPCS: input var okay, CSRF ok.
|
||||
return;
|
||||
}
|
||||
|
||||
$input_id = $input_id . '-search-input';
|
||||
$search_query = isset( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : ''; // WPCS: input var okay, CSRF ok.
|
||||
|
||||
echo '<p class="search-box">';
|
||||
echo '<label class="screen-reader-text" for="' . esc_attr( $input_id ) . '">' . esc_html( $text ) . ':</label>';
|
||||
echo '<input type="search" id="' . esc_attr( $input_id ) . '" name="s" value="' . esc_attr( $search_query ) . '" />';
|
||||
submit_button(
|
||||
$text, '', '', false,
|
||||
[
|
||||
'id' => 'search-submit',
|
||||
]
|
||||
);
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare table list items.
|
||||
*/
|
||||
public function prepare_items() {
|
||||
global $wpdb;
|
||||
|
||||
$per_page = $this->get_items_per_page( 'opalestate_keys_per_page' );
|
||||
$current_page = $this->get_pagenum();
|
||||
|
||||
if ( 1 < $current_page ) {
|
||||
$offset = $per_page * ( $current_page - 1 );
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
$search = '';
|
||||
|
||||
if ( ! empty( $_REQUEST['s'] ) ) { // WPCS: input var okay, CSRF ok.
|
||||
$search = "AND description LIKE '%" . esc_sql( $wpdb->esc_like( opalestate_clean( wp_unslash( $_REQUEST['s'] ) ) ) ) . "%' "; // WPCS: input var okay, CSRF ok.
|
||||
}
|
||||
|
||||
// Get the API keys.
|
||||
$keys = $wpdb->get_results(
|
||||
"SELECT key_id, user_id, description, permissions, truncated_key, last_access FROM {$wpdb->prefix}opalestate_api_keys WHERE 1 = 1 {$search}" .
|
||||
$wpdb->prepare( 'ORDER BY key_id DESC LIMIT %d OFFSET %d;', $per_page, $offset ), ARRAY_A
|
||||
); // WPCS: unprepared SQL ok.
|
||||
|
||||
$count = $wpdb->get_var( "SELECT COUNT(key_id) FROM {$wpdb->prefix}opalestate_api_keys WHERE 1 = 1 {$search};" ); // WPCS: unprepared SQL ok.
|
||||
|
||||
$this->items = $keys;
|
||||
|
||||
// Set the pagination.
|
||||
$this->set_pagination_args(
|
||||
[
|
||||
'total_items' => $count,
|
||||
'per_page' => $per_page,
|
||||
'total_pages' => ceil( $count / $per_page ),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
290
inc/api/class-opalestate-admin-api-keys.php
Normal file
290
inc/api/class-opalestate-admin-api-keys.php
Normal file
@ -0,0 +1,290 @@
|
||||
<?php
|
||||
/**
|
||||
* Opalestate Admin API Keys Class
|
||||
*
|
||||
* @package Opalestate\API
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Opalestate_Admin_API_Keys.
|
||||
*/
|
||||
class Opalestate_Admin_API_Keys {
|
||||
|
||||
/**
|
||||
* Initialize the API Keys admin actions.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'admin_init', [ $this, 'actions' ] );
|
||||
add_action( 'load-opalestate_property_page_opalestate-settings', [ $this, 'screen_option' ] );
|
||||
add_action( 'cmb2_render_api_keys', [ $this, 'page_output' ] );
|
||||
add_filter( 'opalestate_save_settings_advanced_keys', [ $this, 'allow_save_settings' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if should allow save settings.
|
||||
* This prevents "Your settings have been saved." notices on the table list.
|
||||
*
|
||||
* @param bool $allow If allow save settings.
|
||||
* @return bool
|
||||
*/
|
||||
public function allow_save_settings( $allow ) {
|
||||
if ( ! isset( $_GET['create-key'], $_GET['edit-key'] ) ) { // WPCS: input var okay, CSRF ok.
|
||||
return false;
|
||||
}
|
||||
|
||||
return $allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is API Keys settings page.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_api_keys_settings_page() {
|
||||
return isset( $_GET['page'], $_GET['tab'] ) && 'opalestate-settings' === $_GET['page'] && 'api_keys' === $_GET['tab']; // WPCS: input var okay, CSRF ok.
|
||||
}
|
||||
|
||||
/**
|
||||
* Page output.
|
||||
*/
|
||||
public static function page_output() {
|
||||
if ( isset( $_GET['create-key'] ) || isset( $_GET['edit-key'] ) ) {
|
||||
$key_id = isset( $_GET['edit-key'] ) ? absint( $_GET['edit-key'] ) : 0; // WPCS: input var okay, CSRF ok.
|
||||
$key_data = static::get_key_data( $key_id );
|
||||
$user_id = absint( $key_data['user_id'] );
|
||||
|
||||
if ( $key_id && $user_id && ! current_user_can( 'edit_user', $user_id ) ) {
|
||||
if ( get_current_user_id() !== $user_id ) {
|
||||
wp_die( esc_html__( 'You do not have permission to edit this API Key', 'opalestate-pro' ) );
|
||||
}
|
||||
}
|
||||
|
||||
include dirname( __FILE__ ) . '/html-keys-edit.php';
|
||||
} else {
|
||||
static::table_list_output();
|
||||
}
|
||||
?>
|
||||
<style>
|
||||
input[name="submit-cmb"] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Add screen option.
|
||||
*/
|
||||
public function screen_option() {
|
||||
global $keys_table_list;
|
||||
|
||||
if ( ! isset( $_GET['create-key'] ) && ! isset( $_GET['edit-key'] ) && $this->is_api_keys_settings_page() ) { // WPCS: input var okay, CSRF ok.
|
||||
$keys_table_list = new Opalestate_Admin_API_Keys_Table_List();
|
||||
|
||||
// Add screen option.
|
||||
add_screen_option(
|
||||
'per_page', [
|
||||
'default' => 10,
|
||||
'option' => 'opalestate_keys_per_page',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Table list output.
|
||||
*/
|
||||
private static function table_list_output() {
|
||||
global $wpdb, $keys_table_list;
|
||||
|
||||
echo '<h2>' . esc_html__( 'REST API',
|
||||
'opalestate-pro' ) . ' <a href="' . esc_url( admin_url( 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys&create-key=1' ) ) . '" class="add-new-h2">' .
|
||||
esc_html__( 'Add key', 'opalestate-pro' ) . '</a></h2>';
|
||||
|
||||
// Get the API keys count.
|
||||
$count = $wpdb->get_var( "SELECT COUNT(key_id) FROM {$wpdb->prefix}opalestate_api_keys WHERE 1 = 1;" );
|
||||
|
||||
if ( absint( $count ) && $count > 0 ) {
|
||||
$keys_table_list->prepare_items();
|
||||
|
||||
echo '<input type="hidden" name="page" value="wc-settings" />';
|
||||
echo '<input type="hidden" name="tab" value="advanced" />';
|
||||
echo '<input type="hidden" name="section" value="keys" />';
|
||||
|
||||
$keys_table_list->views();
|
||||
$keys_table_list->search_box( __( 'Search key', 'opalestate-pro' ), 'key' );
|
||||
$keys_table_list->display();
|
||||
} else {
|
||||
echo '<div class="opalestate-admin-api">';
|
||||
?>
|
||||
<h2 class="opalestate-admin-api-message">
|
||||
<?php printf(
|
||||
__( 'API keys allow users to use the <a href="%s">Opalestate REST API</a> to retrieve donation data in JSON or XML for external applications or devices, such as <a href="%s">Zapi_keyser</a>.',
|
||||
'opalestate-pro' ),
|
||||
'https://wpopal.com/opalestate/documentation/opalestate-api_keys-reference/',
|
||||
'https://wpopal.com/addons/opalestate/'
|
||||
); ?>
|
||||
</h2>
|
||||
|
||||
<a class="button-primary button" href="<?php echo esc_url( admin_url( 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys&create-key=1' ) ); ?>">
|
||||
<?php esc_html_e( 'Create an API key', 'opalestate-pro' ); ?>
|
||||
</a>
|
||||
<style type="text/css">#posts-filter .wp-list-table, #posts-filter .tablenav.top, .tablenav.bottom .actions {
|
||||
display: none;
|
||||
}</style>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key data.
|
||||
*
|
||||
* @param int $key_id API Key ID.
|
||||
* @return array
|
||||
*/
|
||||
private static function get_key_data( $key_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$empty = [
|
||||
'key_id' => 0,
|
||||
'user_id' => '',
|
||||
'description' => '',
|
||||
'permissions' => '',
|
||||
'truncated_key' => '',
|
||||
'last_access' => '',
|
||||
];
|
||||
|
||||
if ( 0 === $key_id ) {
|
||||
return $empty;
|
||||
}
|
||||
|
||||
$key = $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"SELECT key_id, user_id, description, permissions, truncated_key, last_access
|
||||
FROM {$wpdb->prefix}opalestate_api_keys
|
||||
WHERE key_id = %d",
|
||||
$key_id
|
||||
), ARRAY_A
|
||||
);
|
||||
|
||||
if ( is_null( $key ) ) {
|
||||
return $empty;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* API Keys admin actions.
|
||||
*/
|
||||
public function actions() {
|
||||
if ( $this->is_api_keys_settings_page() ) {
|
||||
// Revoke key.
|
||||
if ( isset( $_REQUEST['revoke-key'] ) ) { // WPCS: input var okay, CSRF ok.
|
||||
$this->revoke_key();
|
||||
}
|
||||
|
||||
// Bulk actions.
|
||||
if ( isset( $_REQUEST['action'] ) && isset( $_REQUEST['key'] ) ) { // WPCS: input var okay, CSRF ok.
|
||||
$this->bulk_actions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notices.
|
||||
*/
|
||||
public static function notices() {
|
||||
if ( isset( $_GET['revoked'] ) ) { // WPCS: input var okay, CSRF ok.
|
||||
$revoked = absint( $_GET['revoked'] ); // WPCS: input var okay, CSRF ok.
|
||||
|
||||
/* translators: %d: count */
|
||||
// Opalestate_Admin_Settings::add_message( sprintf( _n( '%d API key permanently revoked.', '%d API keys permanently revoked.', $revoked, 'opalestate-pro' ), $revoked ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke key.
|
||||
*/
|
||||
private function revoke_key() {
|
||||
global $wpdb;
|
||||
|
||||
check_admin_referer( 'revoke' );
|
||||
|
||||
if ( isset( $_REQUEST['revoke-key'] ) ) { // WPCS: input var okay, CSRF ok.
|
||||
$key_id = absint( $_REQUEST['revoke-key'] ); // WPCS: input var okay, CSRF ok.
|
||||
$user_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM {$wpdb->prefix}opalestate_api_keys WHERE key_id = %d", $key_id ) );
|
||||
|
||||
if ( $key_id && $user_id && ( current_user_can( 'edit_user', $user_id ) || get_current_user_id() === $user_id ) ) {
|
||||
$this->remove_key( $key_id );
|
||||
} else {
|
||||
wp_die( esc_html__( 'You do not have permission to revoke this API Key', 'opalestate-pro' ) );
|
||||
}
|
||||
}
|
||||
|
||||
wp_safe_redirect( esc_url_raw( add_query_arg( [ 'revoked' => 1 ], admin_url( 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys' ) ) ) );
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk actions.
|
||||
*/
|
||||
private function bulk_actions() {
|
||||
check_admin_referer( 'opalestate-settings' );
|
||||
|
||||
if ( ! current_user_can( 'manage_opalestate' ) ) {
|
||||
wp_die( esc_html__( 'You do not have permission to edit API Keys', 'opalestate-pro' ) );
|
||||
}
|
||||
|
||||
if ( isset( $_REQUEST['action'] ) ) { // WPCS: input var okay, CSRF ok.
|
||||
$action = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); // WPCS: input var okay, CSRF ok.
|
||||
$keys = isset( $_REQUEST['key'] ) ? array_map( 'absint', (array) $_REQUEST['key'] ) : []; // WPCS: input var okay, CSRF ok.
|
||||
|
||||
if ( 'revoke' === $action ) {
|
||||
$this->bulk_revoke_key( $keys );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk revoke key.
|
||||
*
|
||||
* @param array $keys API Keys.
|
||||
*/
|
||||
private function bulk_revoke_key( $keys ) {
|
||||
if ( ! current_user_can( 'remove_users' ) ) {
|
||||
wp_die( esc_html__( 'You do not have permission to revoke API Keys', 'opalestate-pro' ) );
|
||||
}
|
||||
|
||||
$qty = 0;
|
||||
foreach ( $keys as $key_id ) {
|
||||
$result = $this->remove_key( $key_id );
|
||||
|
||||
if ( $result ) {
|
||||
$qty++;
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect to webhooks page.
|
||||
wp_safe_redirect( esc_url_raw( add_query_arg( [ 'revoked' => $qty ], admin_url( 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys' ) ) ) );
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove key.
|
||||
*
|
||||
* @param int $key_id API Key ID.
|
||||
* @return bool
|
||||
*/
|
||||
private function remove_key( $key_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$delete = $wpdb->delete( $wpdb->prefix . 'opalestate_api_keys', [ 'key_id' => $key_id ], [ '%d' ] );
|
||||
|
||||
return $delete;
|
||||
}
|
||||
}
|
||||
|
||||
new Opalestate_Admin_API_Keys();
|
@ -1,547 +0,0 @@
|
||||
<?php
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opaljob_API Class
|
||||
*
|
||||
* Renders API returns as a JSON/XML array
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
class Opalestate_API_Admin {
|
||||
/**
|
||||
* Latest API Version
|
||||
*/
|
||||
const VERSION = 1;
|
||||
/**
|
||||
* Pretty Print?
|
||||
*
|
||||
* @var bool
|
||||
* @access private
|
||||
* @since 1.1
|
||||
*/
|
||||
private $pretty_print = false;
|
||||
/**
|
||||
* Log API requests?
|
||||
*
|
||||
* @var bool
|
||||
* @access public
|
||||
* @since 1.1
|
||||
*/
|
||||
public $log_requests = true;
|
||||
/**
|
||||
* Is this a valid request?
|
||||
*
|
||||
* @var bool
|
||||
* @access private
|
||||
* @since 1.1
|
||||
*/
|
||||
private $is_valid_request = false;
|
||||
/**
|
||||
* User ID Perpropertying the API Request
|
||||
*
|
||||
* @var int
|
||||
* @access public
|
||||
* @since 1.1
|
||||
*/
|
||||
public $user_id = 0;
|
||||
/**
|
||||
* Instance of Opalestate Stats class
|
||||
*
|
||||
* @var object
|
||||
* @access private
|
||||
* @since 1.1
|
||||
*/
|
||||
private $stats;
|
||||
/**
|
||||
* Response data to return
|
||||
*
|
||||
* @var array
|
||||
* @access private
|
||||
* @since 1.1
|
||||
*/
|
||||
private $data = [];
|
||||
/**
|
||||
*
|
||||
* @var bool
|
||||
* @access public
|
||||
* @since 1.1
|
||||
*/
|
||||
public $override = true;
|
||||
|
||||
/**
|
||||
* Render Sidebar
|
||||
*
|
||||
* Display Sidebar on left side and next is main content
|
||||
*
|
||||
* @return string
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
public static function get_instance() {
|
||||
|
||||
static $_instance;
|
||||
if ( ! $_instance ) {
|
||||
$_instance = new Opalestate_API_Admin();
|
||||
}
|
||||
|
||||
return $_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the Opalestate API
|
||||
*
|
||||
* @since 1.1
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( is_admin() ) {
|
||||
$this->register_actions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers query vars for API access
|
||||
*
|
||||
* @access public
|
||||
* @param array $vars Query vars
|
||||
*
|
||||
* @return string[] $vars New query vars
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function register_actions() {
|
||||
add_action( 'admin_init', [ $this, 'process_action' ] );
|
||||
add_action( 'show_user_profile', [ $this, 'user_key_field' ] );
|
||||
add_action( 'edit_user_profile', [ $this, 'user_key_field' ] );
|
||||
add_action( 'personal_options_update', [ $this, 'update_key' ] );
|
||||
add_action( 'edit_user_profile_update', [ $this, 'update_key' ] );
|
||||
add_action( 'opalestate_action', [ $this, 'process_api_key' ] );
|
||||
|
||||
// Setup a backwards compatibility check for user API Keys
|
||||
add_filter( 'get_user_metadata', [ $this, 'api_key_backwards_compat' ], 10, 4 );
|
||||
// Determine if JSON_PRETTY_PRINT is available
|
||||
$this->pretty_print = defined( 'JSON_PRETTY_PRINT' ) ? JSON_PRETTY_PRINT : null;
|
||||
// Allow API request logging to be turned off
|
||||
$this->log_requests = apply_filters( 'opalestate_api_log_requests', $this->log_requests );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers query vars for API access
|
||||
*
|
||||
* @access public
|
||||
* @param array $vars Query vars
|
||||
*
|
||||
* @return string[] $vars New query vars
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function process_action() {
|
||||
if ( isset( $_REQUEST['opalestate_action'] ) ) {
|
||||
$args = [
|
||||
'user_id' => isset( $_REQUEST['user_id'] ) ? sanitize_text_field( $_REQUEST['user_id'] ) : 0,
|
||||
'key_permissions' => isset( $_REQUEST['key_permissions'] ) ? sanitize_text_field( $_REQUEST['key_permissions'] ) : 'read',
|
||||
'description' => isset( $_REQUEST['description'] ) ? sanitize_text_field( $_REQUEST['description'] ) : '',
|
||||
'opalestate_api_process' => isset( $_REQUEST['opalestate_api_process'] ) ? sanitize_text_field( $_REQUEST['opalestate_api_process'] ) : '',
|
||||
];
|
||||
|
||||
do_action( 'opalestate_action', $args );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the user ID based on the public key provided
|
||||
*
|
||||
* @access public
|
||||
* @param string $key Public Key
|
||||
*
|
||||
* @return bool if user ID is found, false otherwise
|
||||
* @since 1.1
|
||||
* @global WPDB $wpdb Used to query the database using the WordPress
|
||||
* Database API
|
||||
*
|
||||
*/
|
||||
public function get_user( $key = '' ) {
|
||||
global $wpdb, $wp_query;
|
||||
|
||||
if ( empty( $key ) ) {
|
||||
$key = urldecode( $wp_query->query_vars['key'] );
|
||||
}
|
||||
|
||||
if ( empty( $key ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = get_transient( md5( 'opalestate_api_user_' . $key ) );
|
||||
|
||||
if ( false === $user ) {
|
||||
$user = $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s LIMIT 1", $key ) );
|
||||
set_transient( md5( 'opalestate_api_user_' . $key ), $user, DAY_IN_SECONDS );
|
||||
}
|
||||
|
||||
if ( $user != null ) {
|
||||
$this->user_id = $user;
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get_user_public_key( $user_id = 0 ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( empty( $user_id ) ) {
|
||||
return '';
|
||||
}
|
||||
$cache_key = md5( 'opalestate_api_user_public_key' . $user_id );
|
||||
$user_public_key = get_transient( $cache_key );
|
||||
|
||||
if ( empty( $user_public_key ) ) {
|
||||
$user_public_key = $wpdb->get_var( $wpdb->prepare( "SELECT meta_key FROM $wpdb->usermeta WHERE meta_value = 'opalestate_user_public_key' AND user_id = %d", $user_id ) );
|
||||
set_transient( $cache_key, $user_public_key, HOUR_IN_SECONDS );
|
||||
}
|
||||
|
||||
return $user_public_key;
|
||||
}
|
||||
|
||||
public function get_user_secret_key( $user_id = 0 ) {
|
||||
global $wpdb;
|
||||
if ( empty( $user_id ) ) {
|
||||
return '';
|
||||
}
|
||||
$cache_key = md5( 'opalestate_api_user_secret_key' . $user_id );
|
||||
$user_secret_key = get_transient( $cache_key );
|
||||
if ( empty( $user_secret_key ) ) {
|
||||
$user_secret_key = $wpdb->get_var( $wpdb->prepare( "SELECT meta_key FROM $wpdb->usermeta WHERE meta_value = 'opalestate_user_secret_key' AND user_id = %d", $user_id ) );
|
||||
set_transient( $cache_key, $user_secret_key, HOUR_IN_SECONDS );
|
||||
}
|
||||
|
||||
return $user_secret_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify User Profile
|
||||
*
|
||||
* Modifies the output of profile.php to add key generation/revocation
|
||||
*
|
||||
* @access public
|
||||
* @param object $user Current user info
|
||||
*
|
||||
* @return void
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function user_key_field( $user ) {
|
||||
if ( ( opalestate_get_option( 'api_allow_user_keys', false ) || current_user_can( 'manage_opalestate_settings' ) ) && current_user_can( 'edit_user', $user->ID ) ) {
|
||||
$user = get_userdata( $user->ID );
|
||||
?>
|
||||
<hr class="clearfix clear">
|
||||
<table class="property-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<?php esc_html_e( 'Opalestate API Keys', 'opalestate-pro' ); ?>
|
||||
</th>
|
||||
<td>
|
||||
<?php
|
||||
$public_key = $this->get_user_public_key( $user->ID );
|
||||
$secret_key = $this->get_user_secret_key( $user->ID );
|
||||
?>
|
||||
<?php if ( empty( $user->opalestate_user_public_key ) ) { ?>
|
||||
<input name="opalestate_set_api_key" type="checkbox" id="opalestate_set_api_key" value="0"/>
|
||||
<span class="description"><?php esc_html_e( 'Generate API Key', 'opalestate-pro' ); ?></span>
|
||||
<?php } else { ?>
|
||||
<strong style="display:inline-block; width: 125px;"><?php esc_html_e( 'Public key:', 'opalestate-pro' ); ?> </strong>
|
||||
<input type="text" disabled="disabled" class="regular-text" id="publickey" value="<?php echo esc_attr( $public_key ); ?>"/>
|
||||
<br/>
|
||||
<strong style="display:inline-block; width: 125px;"><?php esc_html_e( 'Secret key:', 'opalestate-pro' ); ?> </strong>
|
||||
<input type="text" disabled="disabled" class="regular-text" id="privatekey" value="<?php echo esc_attr( $secret_key ); ?>"/>
|
||||
<br/>
|
||||
<strong style="display:inline-block; width: 125px;"><?php esc_html_e( 'Token:', 'opalestate-pro' ); ?> </strong>
|
||||
<input type="text" disabled="disabled" class="regular-text" id="token" value="<?php echo esc_attr( $this->get_token( $user->ID ) ); ?>"/>
|
||||
<br/>
|
||||
<input name="opalestate_set_api_key" type="checkbox" id="opalestate_set_api_key" value="0"/>
|
||||
<span class="description"><label for="opalestate_set_api_key"><?php esc_html_e( 'Revoke API Keys', 'opalestate-pro' ); ?></label></span>
|
||||
<?php } ?>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php }
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an API key generation/revocation
|
||||
*
|
||||
* @access public
|
||||
* @param array $args
|
||||
*
|
||||
* @return void
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function process_api_key( $args ) {
|
||||
if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'opalestate-api-nonce' ) ) {
|
||||
wp_die( esc_html__( 'Nonce verification failed.', 'opalestate-pro' ), esc_html__( 'Error', 'opalestate-pro' ), [ 'response' => 403 ] );
|
||||
}
|
||||
if ( empty( $args['user_id'] ) ) {
|
||||
wp_die( esc_html__( 'User ID Required.', 'opalestate-pro' ), esc_html__( 'Error', 'opalestate-pro' ), [ 'response' => 401 ] );
|
||||
}
|
||||
|
||||
if ( is_numeric( $args['user_id'] ) ) {
|
||||
$user_id = isset( $args['user_id'] ) ? absint( $args['user_id'] ) : get_current_user_id();
|
||||
} else {
|
||||
$userdata = get_user_by( 'login', $args['user_id'] );
|
||||
$user_id = $userdata->ID;
|
||||
}
|
||||
$process = isset( $args['opalestate_api_process'] ) ? strtolower( $args['opalestate_api_process'] ) : false;
|
||||
|
||||
if ( $user_id == get_current_user_id() && ! opalestate_options( 'allow_user_api_keys' ) && ! current_user_can( 'manage_opalestate_settings' ) ) {
|
||||
wp_die(
|
||||
sprintf(
|
||||
/* translators: %s: process */
|
||||
esc_html__( 'You do not have permission to %s API keys for this user.', 'opalestate-pro' ),
|
||||
$process
|
||||
),
|
||||
esc_html__( 'Error', 'opalestate-pro' ),
|
||||
[ 'response' => 403 ]
|
||||
);
|
||||
} elseif ( ! current_user_can( 'manage_opalestate_settings' ) ) {
|
||||
wp_die(
|
||||
sprintf(
|
||||
/* translators: %s: process */
|
||||
esc_html__( 'You do not have permission to %s API keys for this user.', 'opalestate-pro' ),
|
||||
$process
|
||||
),
|
||||
esc_html__( 'Error', 'opalestate-pro' ),
|
||||
[ 'response' => 403 ]
|
||||
);
|
||||
}
|
||||
|
||||
switch ( $process ) {
|
||||
case 'generate':
|
||||
if ( $this->generate_api_key( $user_id ) ) {
|
||||
delete_transient( 'opalestate_total_api_keys' );
|
||||
wp_redirect( add_query_arg( 'opalestate-message', 'api-key-generated', 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys' ) );
|
||||
exit();
|
||||
} else {
|
||||
wp_redirect( add_query_arg( 'opalestate-message', 'api-key-failed', 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys' ) );
|
||||
exit();
|
||||
}
|
||||
break;
|
||||
case 'regenerate':
|
||||
$this->generate_api_key( $user_id, true );
|
||||
delete_transient( 'opalestate_total_api_keys' );
|
||||
wp_redirect( add_query_arg( 'opalestate-message', 'api-key-regenerated', 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys' ) );
|
||||
exit();
|
||||
break;
|
||||
case 'revoke':
|
||||
$this->revoke_api_key( $user_id );
|
||||
delete_transient( 'opalestate_total_api_keys' );
|
||||
wp_redirect( add_query_arg( 'opalestate-message', 'api-key-revoked', 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys' ) );
|
||||
exit();
|
||||
break;
|
||||
default;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate new API keys for a user
|
||||
*
|
||||
* @access public
|
||||
* @param int $user_id User ID the key is being generated for
|
||||
* @param boolean $regenerate Regenerate the key for the user
|
||||
*
|
||||
* @return boolean True if (re)generated succesfully, false otherwise.
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function generate_api_key( $user_id = 0, $regenerate = false ) {
|
||||
if ( empty( $user_id ) ) {
|
||||
return false;
|
||||
}
|
||||
$user = get_userdata( $user_id );
|
||||
if ( ! $user ) {
|
||||
return false;
|
||||
}
|
||||
$public_key = $this->get_user_public_key( $user_id );
|
||||
$secret_key = $this->get_user_secret_key( $user_id );
|
||||
|
||||
if ( empty( $public_key ) || $regenerate == true ) {
|
||||
$new_public_key = $this->generate_public_key( $user->user_email );
|
||||
$new_secret_key = $this->generate_private_key( $user->ID );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $regenerate == true ) {
|
||||
$this->revoke_api_key( $user->ID );
|
||||
}
|
||||
|
||||
update_user_meta( $user_id, $new_public_key, 'opalestate_user_public_key' );
|
||||
update_user_meta( $user_id, $new_secret_key, 'opalestate_user_secret_key' );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke a users API keys
|
||||
*
|
||||
* @access public
|
||||
* @param int $user_id User ID of user to revoke key for
|
||||
*
|
||||
* @return string
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function revoke_api_key( $user_id = 0 ) {
|
||||
if ( empty( $user_id ) ) {
|
||||
return false;
|
||||
}
|
||||
$user = get_userdata( $user_id );
|
||||
if ( ! $user ) {
|
||||
return false;
|
||||
}
|
||||
$public_key = $this->get_user_public_key( $user_id );
|
||||
$secret_key = $this->get_user_secret_key( $user_id );
|
||||
|
||||
if ( ! empty( $public_key ) ) {
|
||||
delete_transient( md5( 'opalestate_api_user_' . $public_key ) );
|
||||
delete_transient( md5( 'opalestate_api_user_public_key' . $user_id ) );
|
||||
delete_transient( md5( 'opalestate_api_user_secret_key' . $user_id ) );
|
||||
delete_user_meta( $user_id, $public_key );
|
||||
delete_user_meta( $user_id, $secret_key );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_version() {
|
||||
return self::VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and Save API key
|
||||
*
|
||||
* Generates the key requested by user_key_field and stores it in the database
|
||||
*
|
||||
* @access public
|
||||
* @param int $user_id
|
||||
*
|
||||
* @return void
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function update_key( $user_id ) {
|
||||
if ( current_user_can( 'edit_user', $user_id ) && isset( $_POST['opalestate_set_api_key'] ) ) {
|
||||
$user = get_userdata( $user_id );
|
||||
$public_key = $this->get_user_public_key( $user_id );
|
||||
$secret_key = $this->get_user_secret_key( $user_id );
|
||||
if ( empty( $public_key ) ) {
|
||||
$new_public_key = $this->generate_public_key( $user->user_email );
|
||||
$new_secret_key = $this->generate_private_key( $user->ID );
|
||||
update_user_meta( $user_id, $new_public_key, 'opalestate_user_public_key' );
|
||||
update_user_meta( $user_id, $new_secret_key, 'opalestate_user_secret_key' );
|
||||
} else {
|
||||
$this->revoke_api_key( $user_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the public key for a user
|
||||
*
|
||||
* @access private
|
||||
* @param string $user_email
|
||||
*
|
||||
* @return string
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
private function generate_public_key( $user_email = '' ) {
|
||||
$auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
|
||||
$public = hash( 'md5', $user_email . $auth_key . date( 'U' ) );
|
||||
|
||||
return $public;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the secret key for a user
|
||||
*
|
||||
* @access private
|
||||
* @param int $user_id
|
||||
*
|
||||
* @return string
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
private function generate_private_key( $user_id = 0 ) {
|
||||
$auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
|
||||
$secret = hash( 'md5', $user_id . $auth_key . date( 'U' ) );
|
||||
|
||||
return $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the user's token
|
||||
*
|
||||
* @access private
|
||||
* @param int $user_id
|
||||
*
|
||||
* @return string
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
public function get_token( $user_id = 0 ) {
|
||||
return hash( 'md5', $this->get_user_secret_key( $user_id ) . $this->get_user_public_key( $user_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* API Key Backwards Compatibility
|
||||
*
|
||||
* A Backwards Compatibility call for the change of meta_key/value for users API Keys
|
||||
*
|
||||
* @param string $check Whether to check the cache or not
|
||||
* @param int $object_id The User ID being passed
|
||||
* @param string $meta_key The user meta key
|
||||
* @param bool $single If it should return a single value or array
|
||||
*
|
||||
* @return string The API key/secret for the user supplied
|
||||
* @since 1.3.6
|
||||
*
|
||||
*/
|
||||
public function api_key_backwards_compat( $check, $object_id, $meta_key, $single ) {
|
||||
if ( $meta_key !== 'opalestate_user_public_key' && $meta_key !== 'opalestate_user_secret_key' ) {
|
||||
return $check;
|
||||
}
|
||||
|
||||
$return = $check;
|
||||
|
||||
switch ( $meta_key ) {
|
||||
case 'opalestate_user_public_key':
|
||||
$return = $this->get_user_public_key( $object_id );
|
||||
break;
|
||||
case 'opalestate_user_secret_key':
|
||||
$return = $this->get_user_secret_key( $object_id );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! $single ) {
|
||||
$return = [ $return ];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
@ -23,10 +23,6 @@ class Opalestate_API {
|
||||
|
||||
public function __construct() {
|
||||
$this->init();
|
||||
|
||||
if ( is_admin() ) {
|
||||
new Opalestate_API_Admin();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,7 +36,8 @@ class Opalestate_API {
|
||||
*/
|
||||
public function init() {
|
||||
$this->includes( [
|
||||
'class-opalestate-api-admin.php',
|
||||
'class-opalestate-admin-api-keys.php',
|
||||
'class-opalestate-admin-api-keys-table-list.php',
|
||||
'class-opalestate-base-api.php',
|
||||
'v1/property.php',
|
||||
'v1/agent.php',
|
||||
@ -102,4 +99,35 @@ class Opalestate_API {
|
||||
$api_class->register_routes();
|
||||
}
|
||||
}
|
||||
|
||||
public static function install() {
|
||||
try {
|
||||
if ( ! function_exists( 'dbDelta' ) ) {
|
||||
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
$sql = 'CREATE TABLE IF NOT EXISTS ' . $wpdb->prefix . 'opalestate_api_keys' . ' (
|
||||
key_id BIGINT UNSIGNED NOT NULL auto_increment,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
description varchar(200) NULL,
|
||||
permissions varchar(10) NOT NULL,
|
||||
consumer_key char(64) NOT NULL,
|
||||
consumer_secret char(43) NOT NULL,
|
||||
nonces longtext NULL,
|
||||
truncated_key char(7) NOT NULL,
|
||||
last_access datetime NULL default null,
|
||||
PRIMARY KEY (key_id),
|
||||
KEY consumer_key (consumer_key),
|
||||
KEY consumer_secret (consumer_secret)
|
||||
) ' . $charset_collate;
|
||||
dbDelta( $sql );
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,3 +95,26 @@ function opalestate_api_get_property_data( $property_info ) {
|
||||
|
||||
return apply_filters( 'opalestate_api_properties_property', $property );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a rand hash.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function opalestate_rand_hash() {
|
||||
if ( ! function_exists( 'openssl_random_pseudo_bytes' ) ) {
|
||||
return sha1( wp_rand() );
|
||||
}
|
||||
|
||||
return bin2hex( openssl_random_pseudo_bytes( 20 ) ); // @codingStandardsIgnoreLine
|
||||
}
|
||||
|
||||
/**
|
||||
* Opalestate API - Hash.
|
||||
*
|
||||
* @param string $data Message to be hashed.
|
||||
* @return string
|
||||
*/
|
||||
function opalestate_api_hash( $data ) {
|
||||
return hash_hmac( 'sha256', $data, 'opalestate-api' );
|
||||
}
|
||||
|
152
inc/api/html-keys-edit.php
Normal file
152
inc/api/html-keys-edit.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin view: Edit API keys
|
||||
*
|
||||
* @package Opalestate/API/Settings
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
?>
|
||||
|
||||
<div id="key-fields" class="settings-panel">
|
||||
<h2><?php esc_html_e( 'Key details', 'opalestate-pro' ); ?></h2>
|
||||
|
||||
<input type="hidden" id="key_id" value="<?php echo esc_attr( $key_id ); ?>" />
|
||||
|
||||
<table id="api-keys-options" class="form-table">
|
||||
<tbody>
|
||||
<tr valign="top">
|
||||
<th scope="row" class="titledesc">
|
||||
<label for="key_description">
|
||||
<?php esc_html_e( 'Description', 'opalestate-pro' ); ?>
|
||||
</label>
|
||||
</th>
|
||||
<td class="forminp">
|
||||
<input id="key_description" type="text" class="input-text regular-input" value="<?php echo esc_attr( $key_data['description'] ); ?>" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<th scope="row" class="titledesc">
|
||||
<label for="key_user">
|
||||
<?php esc_html_e( 'User', 'opalestate-pro' ); ?>
|
||||
</label>
|
||||
</th>
|
||||
<td class="forminp">
|
||||
<?php
|
||||
$curent_user_id = get_current_user_id();
|
||||
$user_id = ! empty( $key_data['user_id'] ) ? absint( $key_data['user_id'] ) : $curent_user_id;
|
||||
$user = get_user_by( 'id', $user_id );
|
||||
$user_string = sprintf(
|
||||
/* translators: 1: user display name 2: user ID 3: user email */
|
||||
esc_html__( '%1$s (#%2$s – %3$s)', 'opalestate-pro' ),
|
||||
$user->display_name,
|
||||
absint( $user->ID ),
|
||||
$user->user_email
|
||||
);
|
||||
?>
|
||||
<select class="opalestate-customer-search" id="key_user" data-placeholder="<?php esc_attr_e( 'Search for a user…', 'opalestate-pro' ); ?>" data-allow_clear="true">
|
||||
<option value="<?php echo esc_attr( $user_id ); ?>" selected="selected"><?php echo htmlspecialchars( wp_kses_post( $user_string ) ); // htmlspecialchars to prevent XSS when rendered by selectWoo. ?></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<th scope="row" class="titledesc">
|
||||
<label for="key_permissions">
|
||||
<?php esc_html_e( 'Permissions', 'opalestate-pro' ); ?>
|
||||
</label>
|
||||
</th>
|
||||
<td class="forminp">
|
||||
<select id="key_permissions" class="opalestate-enhanced-select">
|
||||
<?php
|
||||
$permissions = array(
|
||||
'read' => __( 'Read', 'opalestate-pro' ),
|
||||
'write' => __( 'Write', 'opalestate-pro' ),
|
||||
'read_write' => __( 'Read/Write', 'opalestate-pro' ),
|
||||
);
|
||||
|
||||
foreach ( $permissions as $permission_id => $permission_name ) :
|
||||
?>
|
||||
<option value="<?php echo esc_attr( $permission_id ); ?>" <?php selected( $key_data['permissions'], $permission_id, true ); ?>><?php echo esc_html( $permission_name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php if ( 0 !== $key_id ) : ?>
|
||||
<tr valign="top">
|
||||
<th scope="row" class="titledesc">
|
||||
<?php esc_html_e( 'Consumer key ending in', 'opalestate-pro' ); ?>
|
||||
</th>
|
||||
<td class="forminp">
|
||||
<code>…<?php echo esc_html( $key_data['truncated_key'] ); ?></code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<th scope="row" class="titledesc">
|
||||
<?php esc_html_e( 'Last access', 'opalestate-pro' ); ?>
|
||||
</th>
|
||||
<td class="forminp">
|
||||
<span>
|
||||
<?php
|
||||
if ( ! empty( $key_data['last_access'] ) ) {
|
||||
/* translators: 1: last access date 2: last access time */
|
||||
$date = sprintf( __( '%1$s at %2$s', 'opalestate-pro' ), date_i18n( get_option( 'date_format' ), strtotime( $key_data['last_access'] ) ), date_i18n( get_option( 'time_format' ), strtotime(
|
||||
$key_data['last_access'] ) ) );
|
||||
|
||||
echo esc_html( apply_filters( 'opalestate_api_key_last_access_datetime', $date, $key_data['last_access'] ) );
|
||||
} else {
|
||||
esc_html_e( 'Unknown', 'opalestate-pro' );
|
||||
}
|
||||
?>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php do_action( 'opalestate_admin_key_fields', $key_data ); ?>
|
||||
|
||||
<?php
|
||||
if ( 0 === intval( $key_id ) ) {
|
||||
submit_button( __( 'Generate API key', 'opalestate-pro' ), 'primary', 'update_api_key' );
|
||||
} else {
|
||||
?>
|
||||
<p class="submit">
|
||||
<?php submit_button( __( 'Save changes', 'opalestate-pro' ), 'primary', 'update_api_key', false ); ?>
|
||||
<a style="color: #a00; text-decoration: none; margin-left: 10px;" href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'revoke-key' => $key_id ), admin_url( 'edit.php?post_type=opalestate_property&page=opalestate-settings&tab=api_keys' ) ), 'revoke' ) ); ?>">
|
||||
<?php esc_html_e( 'Revoke key', 'opalestate-pro' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="tmpl-api-keys-template">
|
||||
<p id="copy-error"></p>
|
||||
<table class="form-table">
|
||||
<tbody>
|
||||
<tr valign="top">
|
||||
<th scope="row" class="titledesc">
|
||||
<?php esc_html_e( 'Consumer key', 'opalestate-pro' ); ?>
|
||||
</th>
|
||||
<td class="forminp">
|
||||
<input id="key_consumer_key" type="text" value="{{ data.consumer_key }}" size="55" readonly="readonly"> <button type="button" class="button-secondary copy-key" data-tip="<?php
|
||||
esc_attr_e( 'Copied!', 'opalestate-pro' ); ?>"><?php esc_html_e( 'Copy', 'opalestate-pro' ); ?></button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<th scope="row" class="titledesc">
|
||||
<?php esc_html_e( 'Consumer secret', 'opalestate-pro' ); ?>
|
||||
</th>
|
||||
<td class="forminp">
|
||||
<input id="key_consumer_secret" type="text" value="{{ data.consumer_secret }}" size="55" readonly="readonly">
|
||||
<button type="button" class="button-secondary copy-secret" data-tip="<?php esc_attr_e( 'Copied!', 'opalestate-pro' ); ?>">
|
||||
<?php esc_html_e( 'Copy', 'opalestate-pro' ); ?>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</script>
|
@ -357,7 +357,7 @@ class Opalestate_Property_Api extends Opalestate_Base_API {
|
||||
$fieldquery = [
|
||||
'key' => OPALESTATE_PROPERTY_PREFIX . $key,
|
||||
'value' => sanitize_text_field( trim( $value ) ),
|
||||
'compare' => '>=',
|
||||
'compare' => apply_filters( 'opalestate_info_numeric_compare', '>=' ),
|
||||
'type' => 'NUMERIC',
|
||||
];
|
||||
} else {
|
||||
|
@ -92,9 +92,8 @@ class Opalestate_Emails {
|
||||
$mail->set_pros( $post_id, $user_id );
|
||||
$return = self::send_mail_now( $mail );
|
||||
|
||||
echo json_encode( $return );
|
||||
die();
|
||||
|
||||
// echo json_encode( $return );
|
||||
// die();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,6 +57,7 @@ class Opalestate_Install {
|
||||
OpalEstate_User_Search::install();
|
||||
OpalEstate_User_Message::install();
|
||||
OpalEstate_User_Request_Viewing::install();
|
||||
Opalestate_API::install();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,6 +75,7 @@ function opalestate_property_areasize_field_template( $template = '' ) {
|
||||
'ranger_max' => opalestate_options( 'search_max_area', 1000 ),
|
||||
'input_min' => $search_min,
|
||||
'input_max' => $search_max,
|
||||
'unit_thousand' => apply_filters( 'opalestate_areasize_unit_thousand', ',' ),
|
||||
];
|
||||
|
||||
opalesate_property_slide_ranger_template( esc_html__( 'Area', 'opalestate-pro' ), $data );
|
||||
@ -82,7 +83,7 @@ function opalestate_property_areasize_field_template( $template = '' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( "opalestate_property_areasize_field_template", 'opalestate_property_areasize_field_template' );
|
||||
add_filter( 'opalestate_property_areasize_field_template', 'opalestate_property_areasize_field_template' );
|
||||
|
||||
/**
|
||||
* Render slider ranger template.
|
||||
@ -100,6 +101,7 @@ function opalesate_property_slide_ranger_template( $label, $data ) {
|
||||
'input_min' => 0,
|
||||
'input_max' => 1000,
|
||||
'unit_position' => 'postfix',
|
||||
'unit_thousand' => ',',
|
||||
'mode' => 2,
|
||||
'start' => '',
|
||||
];
|
||||
@ -109,7 +111,8 @@ function opalesate_property_slide_ranger_template( $label, $data ) {
|
||||
extract( $data );
|
||||
?>
|
||||
<label class="opalestate-label opalestate-label--<?php echo sanitize_title( $label ); ?>"><?php echo esc_html( $label ); ?></label>
|
||||
<div class="opal-slide-ranger" data-unit="<?php echo $unit; ?>" data-unitpos="<?php echo $unit_position ?>" data-decimals="<?php echo $decimals; ?>">
|
||||
<div class="opal-slide-ranger" data-unit="<?php echo $unit; ?>" data-unitpos="<?php echo $unit_position ?>" data-decimals="<?php echo $decimals; ?>" data-thousand="<?php echo esc_attr(
|
||||
$unit_thousand ); ?>">
|
||||
<label class="slide-ranger-label">
|
||||
<span class="slide-ranger-min-label"></span>
|
||||
<?php echo ( $mode == 2 ) ? '<i>-</i>' : ''; ?>
|
||||
|
@ -1232,3 +1232,18 @@ function opalestate_is_enable_price_field() {
|
||||
function opalestate_is_enable_areasize_field() {
|
||||
return 'on' == opalestate_get_option( 'opalestate_ppt_areasize_opt', 'on' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean variables using sanitize_text_field. Arrays are cleaned recursively.
|
||||
* Non-scalar values are ignored.
|
||||
*
|
||||
* @param string|array $var Data to sanitize.
|
||||
* @return string|array
|
||||
*/
|
||||
function opalestate_clean( $var ) {
|
||||
if ( is_array( $var ) ) {
|
||||
return array_map( 'opalestate_clean', $var );
|
||||
} else {
|
||||
return is_scalar( $var ) ? sanitize_text_field( $var ) : $var;
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* $Desc$
|
||||
* OpalEstate_Search
|
||||
*
|
||||
* @version $Id$
|
||||
* @package opalestate
|
||||
* @author Opal Team <info@wpopal.com >
|
||||
* @copyright Copyright (C) 2019 wpopal.com. All Rights Reserved.
|
||||
* @license GNU/GPL v2 or later http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* @website http://www.wpopal.com
|
||||
* @support http://www.wpopal.com/support/forum.html
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
@ -23,27 +16,24 @@ class OpalEstate_Search {
|
||||
public static function init() {
|
||||
add_action( 'wp_ajax_opalestate_ajx_get_properties', [ __CLASS__, 'get_search_json' ] );
|
||||
add_action( 'wp_ajax_nopriv_opalestate_ajx_get_properties', [ __CLASS__, 'get_search_json' ] );
|
||||
|
||||
add_action( 'wp_ajax_opalestate_render_get_properties', [ __CLASS__, 'render_get_properties' ] );
|
||||
add_action( 'wp_ajax_nopriv_opalestate_render_get_properties', [ __CLASS__, 'render_get_properties' ] );
|
||||
// add_filter( "pre_get_posts", array( __CLASS__, 'change_archive_query' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Query Object to display collection of property with user request which submited via search form
|
||||
* Get Query Object to display collection of property with user request which submited via search form.
|
||||
*
|
||||
* @param int $limit Limit.
|
||||
*/
|
||||
public static function get_search_results_query( $limit = 9 ) {
|
||||
// global $paged;
|
||||
global $wp_query;
|
||||
|
||||
$show_featured_first = opalestate_options( 'show_featured_first', 1 );
|
||||
$search_min_price = isset( $_GET['min_price'] ) ? sanitize_text_field( $_GET['min_price'] ) : '';
|
||||
$search_max_price = isset( $_GET['max_price'] ) ? sanitize_text_field( $_GET['max_price'] ) : '';
|
||||
|
||||
$search_min_area = isset( $_GET['min_area'] ) ? sanitize_text_field( $_GET['min_area'] ) : '';
|
||||
$search_max_area = isset( $_GET['max_area'] ) ? sanitize_text_field( $_GET['max_area'] ) : '';
|
||||
$s = isset( $_GET['search_text'] ) ? sanitize_text_field( $_GET['search_text'] ) : null;
|
||||
$location_text = isset( $_GET['location_text'] ) ? sanitize_text_field( $_GET['location_text'] ) : null;
|
||||
|
||||
$posts_per_page = apply_filters( 'opalestate_search_property_per_page', opalestate_options( 'search_property_per_page', $limit ) );
|
||||
|
||||
@ -55,8 +45,6 @@ class OpalEstate_Search {
|
||||
$paged = intval( $_GET['paged'] );
|
||||
}
|
||||
|
||||
$infos = [];
|
||||
|
||||
$args = [
|
||||
'posts_per_page' => $posts_per_page,
|
||||
'paged' => $paged,
|
||||
@ -65,7 +53,6 @@ class OpalEstate_Search {
|
||||
's' => $s,
|
||||
];
|
||||
|
||||
|
||||
$tax_query = [];
|
||||
|
||||
if ( isset( $_GET['location'] ) && $_GET['location'] != -1 ) {
|
||||
@ -139,13 +126,14 @@ class OpalEstate_Search {
|
||||
$args['meta_query'] = [ 'relation' => 'AND' ];
|
||||
if ( isset( $_GET['info'] ) && is_array( $_GET['info'] ) ) {
|
||||
$metaquery = [];
|
||||
|
||||
foreach ( $_GET['info'] as $key => $value ) {
|
||||
if ( trim( $value ) ) {
|
||||
if ( is_numeric( trim( $value ) ) ) {
|
||||
$fieldquery = [
|
||||
'key' => OPALESTATE_PROPERTY_PREFIX . $key,
|
||||
'value' => sanitize_text_field( trim( $value ) ),
|
||||
'compare' => '>=',
|
||||
'compare' => apply_filters( 'opalestate_info_numeric_compare', '>=' ),
|
||||
'type' => 'NUMERIC',
|
||||
];
|
||||
} else {
|
||||
@ -160,6 +148,7 @@ class OpalEstate_Search {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$args['meta_query'] = array_merge( $args['meta_query'], $metaquery );
|
||||
}
|
||||
|
||||
@ -229,9 +218,7 @@ class OpalEstate_Search {
|
||||
] );
|
||||
}
|
||||
|
||||
///// search by address and geo location ///
|
||||
if ( isset( $_GET['geo_long'] ) && isset( $_GET['geo_lat'] ) ) {
|
||||
|
||||
if ( $_GET['location_text'] && ( empty( $_GET['geo_long'] ) || empty( $_GET['geo_lat'] ) ) ) {
|
||||
array_push( $args['meta_query'], [
|
||||
'key' => OPALESTATE_PROPERTY_PREFIX . 'map_address',
|
||||
@ -247,7 +234,6 @@ class OpalEstate_Search {
|
||||
}
|
||||
}
|
||||
|
||||
/// ///
|
||||
$ksearchs = [];
|
||||
|
||||
if ( isset( $_REQUEST['opalsortable'] ) && ! empty( $_REQUEST['opalsortable'] ) ) {
|
||||
|
@ -128,7 +128,6 @@ if ( ! class_exists( 'OpalEstate' ) ) {
|
||||
self::$instance->roles = new Opalestate_Roles();
|
||||
self::$instance->html = new Opalestate_HTML_Elements();
|
||||
self::$instance->api = new Opalestate_API();
|
||||
self::$instance->api_admin = new Opalestate_API_Admin();
|
||||
self::$instance->session = new Opalestate_Session();
|
||||
|
||||
/**
|
||||
|
@ -25,9 +25,7 @@ The plugin will not make you disappointed with ease of use, friendly & flexible
|
||||
|
||||
1. *[ Housey - Real Estate WordPress Theme](https://bit.ly/2kHewkG "Housey - Real Estate WordPress Theme")*
|
||||
2. *[ Latehome - Real Estate WordPress Theme](https://bit.ly/2M0rmp0 "Latehome - Real Estate WordPress Theme")*
|
||||
3. *[ Latehome Free - Real Estate Free WordPress Theme](http://demo2.themelexus.com/latehome_free/ "Latehome Free - Real Estate Free WordPress Theme"). You can download free [here](https://bit
|
||||
.ly/2nLTfHT "Latehome Free
|
||||
download").*
|
||||
3. *[ Latehome Free - Real Estate Free WordPress Theme](http://demo2.themelexus.com/latehome_free/ "Latehome Free - Real Estate Free WordPress Theme"). You can download free [here](https://bit.ly/2nLTfHT "Latehome Free download").*
|
||||
|
||||
* Please keep contact us at help@wpopal.com to send us your website, we will publish it our showcase
|
||||
|
||||
|
@ -14,6 +14,7 @@ $data = [
|
||||
'ranger_max' => opalestate_options( 'search_max_price', 10000000 ),
|
||||
'input_min' => $search_min_price,
|
||||
'input_max' => $search_max_price,
|
||||
'unit_thousand' => opalestate_options( 'thousands_separator', ',' ),
|
||||
];
|
||||
|
||||
if ( opalestate_options( 'currency_position', 'before' ) == 'before' ) {
|
||||
|
Loading…
Reference in New Issue
Block a user