admin-all.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. var getState = function(){
  2. var loadValues = [],
  3. ignoreNames = ['page-filter', 'page-search'];
  4. $('input, select, textarea').each(function(index, element){
  5. var name = $(element).prop('name'),
  6. value = $(element).val();
  7. if (name && !~ignoreNames.indexOf(name)) loadValues.push(name + '|' + value);
  8. });
  9. return loadValues.toString();
  10. };
  11. var bytesToSize = function(bytes) {
  12. var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
  13. if (bytes == 0) return '0 Byte';
  14. var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
  15. return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
  16. };
  17. var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
  18. var keepAlive = function keepAlive() {
  19. $.post(GravAdmin.config.base_url_relative + '/task' + GravAdmin.config.param_sep + 'keepAlive');
  20. };
  21. $(function () {
  22. jQuery.substitute = function(str, sub) {
  23. return str.replace(/\{(.+?)\}/g, function($0, $1) {
  24. return $1 in sub ? sub[$1] : $0;
  25. });
  26. };
  27. // // selectize
  28. // $('select.fancy:not(.create)').selectize({
  29. // createOnBlur: true,
  30. // });
  31. // // selectize with create
  32. // $('select.fancy.create').selectize({
  33. // createOnBlur: true,
  34. // persist: false,
  35. // create: function (input) {
  36. // return {
  37. // value: input,
  38. // text: input
  39. // }
  40. // }
  41. // });
  42. // $('input.fancy').selectize({
  43. // delimiter: ',',
  44. // persist: false,
  45. // create: function (input) {
  46. // return {
  47. // value: input,
  48. // text: input
  49. // }
  50. // }
  51. // });
  52. // Set Toastr defaults
  53. toastr.options = {
  54. "positionClass": "toast-top-right"
  55. }
  56. // dashboard
  57. var chart = $('.updates-chart'), UpdatesChart;
  58. if (chart.length) {
  59. var data = {
  60. series: [100, 0]
  61. };
  62. var options = {
  63. donut: true,
  64. donutWidth: 10,
  65. startAngle: 0,
  66. total: 100,
  67. showLabel: false,
  68. height: 150,
  69. chartPadding: !isFirefox ? 5 : 10
  70. };
  71. UpdatesChart = Chartist.Pie('.updates-chart .ct-chart', data, options);
  72. UpdatesChart.on('draw', function(data){
  73. if (data.index) { return; }
  74. chart.find('.numeric span').text(Math.round(data.value) + '%');
  75. var text = translations.PLUGIN_ADMIN.UPDATES_AVAILABLE;
  76. if (data.value == 100) {
  77. text = translations.PLUGIN_ADMIN.FULLY_UPDATED;
  78. }
  79. $('.js__updates-available-description').html(text)
  80. $('.updates-chart .hidden').removeClass('hidden');
  81. });
  82. }
  83. // Cache Clear
  84. $('[data-clear-cache]').on('click', function(e) {
  85. $(this).attr('disabled','disabled').find('> .fa').removeClass('fa-trash').addClass('fa-refresh fa-spin');
  86. var url = $(this).data('clearCache');
  87. GravAjax({
  88. dataType: "json",
  89. url: url,
  90. toastErrors: true,
  91. success: function(result, status) {
  92. toastr.success(result.message);
  93. }
  94. }).always(function() {
  95. $('[data-clear-cache]').removeAttr('disabled').find('> .fa').removeClass('fa-refresh fa-spin').addClass('fa-trash');
  96. });
  97. });
  98. // Plugins list details sliders
  99. $('.gpm-name, .gpm-actions').on('click', function(e){
  100. var target = $(e.target);
  101. if (target.prop('tagName') == 'A' || target.parent('a').length) { return true; }
  102. var wrapper = $(this).siblings('.gpm-details').find('.table-wrapper');
  103. wrapper.slideToggle({
  104. duration: 350,
  105. complete: function(){
  106. var isVisible = wrapper.is(':visible');
  107. wrapper
  108. .closest('tr')
  109. .find('.gpm-details-expand i')
  110. .removeClass('fa-chevron-' + (isVisible ? 'down' : 'up'))
  111. .addClass('fa-chevron-' + (isVisible ? 'up' : 'down'));
  112. }
  113. });
  114. });
  115. // Update plugins/themes
  116. $(document).on('click', '[data-maintenance-update]', function(e) {
  117. $(this).attr('disabled','disabled').find('> .fa').removeClass('fa-cloud-download').addClass('fa-refresh fa-spin');
  118. var url = $(this).data('maintenanceUpdate');
  119. var task = 'task' + GravAdmin.config.param_sep;
  120. GravAjax({
  121. dataType: "json",
  122. url: url,
  123. toastErrors: true,
  124. success: function(result, status) {
  125. if (url.indexOf(task + 'updategrav') !== -1) {
  126. if (result.status == 'success') {
  127. $('[data-gpm-grav]').remove();
  128. toastr.success(result.message + window.grav_available_version);
  129. $('#footer .grav-version').html(window.grav_available_version);
  130. /*// hide the update button after successfull update and update the badges
  131. $('[data-maintenance-update]').fadeOut();
  132. $('.badges.with-updates').removeClass('with-updates').find('.badge.updates').remove();*/
  133. } else {
  134. toastr.success(result.message);
  135. }
  136. } else {
  137. toastr.success(result.message);
  138. }
  139. }
  140. }).always(function() {
  141. GPMRefresh();
  142. $('[data-maintenance-update]').removeAttr('disabled').find('> .fa').removeClass('fa-refresh fa-spin').addClass('fa-cloud-download');
  143. });
  144. });
  145. // Update plugins/themes
  146. $('[data-ajax]').on('click', function(e) {
  147. var button = $(this),
  148. icon = button.find('> .fa'),
  149. url = button.data('ajax');
  150. var iconClasses = [],
  151. helperClasses = [ 'fa-lg', 'fa-2x', 'fa-3x', 'fa-4x', 'fa-5x',
  152. 'fa-fw', 'fa-ul', 'fa-li', 'fa-border',
  153. 'fa-rotate-90', 'fa-rotate-180', 'fa-rotate-270',
  154. 'fa-flip-horizontal', 'fa-flip-vertical' ];
  155. // Disable button
  156. button.attr('disabled','disabled');
  157. // Swap fontawesome icon to loader
  158. $.each(icon.attr('class').split(/\s+/), function (i, classname) {
  159. if (classname.indexOf('fa-') === 0 && $.inArray(classname, helperClasses) === -1) {
  160. iconClasses.push(classname);
  161. icon.removeClass(classname);
  162. }
  163. });
  164. icon.addClass('fa-refresh fa-spin');
  165. GravAjax({
  166. dataType: "json",
  167. url: url,
  168. toastErrors: true,
  169. success: function(result, status) {
  170. var task = 'task' + GravAdmin.config.param_sep;
  171. var toastrBackup = {};
  172. if (result.toastr) {
  173. for (var setting in result.toastr) { if (result.toastr.hasOwnProperty(setting)) {
  174. toastrBackup[setting] = toastr.options[setting];
  175. toastr.options[setting] = result.toastr[setting];
  176. }
  177. }
  178. }
  179. toastr.success(result.message || translations.PLUGIN_ADMIN.TASK_COMPLETED);
  180. for (var setting in toastrBackup) { if (toastrBackup.hasOwnProperty(setting)) {
  181. toastr.options[setting] = toastrBackup[setting];
  182. }
  183. }
  184. if (url.indexOf(task + 'backup') !== -1) {
  185. //Reset backup days count
  186. $('.backups-chart .numeric').html("0 <em>" + translations.PLUGIN_ADMIN.DAYS + "</em>");
  187. var data = {
  188. series: [0,100]
  189. };
  190. var options = {
  191. donut: true,
  192. donutWidth: 10,
  193. startAngle: 0,
  194. total: 100,
  195. showLabel: false,
  196. height: 150
  197. };
  198. Chartist.Pie('.backups-chart .ct-chart', data, options);
  199. }
  200. }
  201. }).always(function() {
  202. // Restore button
  203. button.removeAttr('disabled');
  204. icon.removeClass('fa-refresh fa-spin').addClass(iconClasses.join(' '));
  205. });
  206. });
  207. $('[data-gpm-checkupdates]').on('click', function(){
  208. var element = $(this);
  209. element.find('i').addClass('fa-spin');
  210. GPMRefresh({
  211. flush: true,
  212. callback: function(response) {
  213. var payload = response.status == 'success' ? response.payload : false;
  214. element.find('i').removeClass('fa-spin');
  215. if (payload) {
  216. if (!payload.grav.isUpdatable && !payload.resources.total) {
  217. toastr.success(translations.PLUGIN_ADMIN.EVERYTHING_UP_TO_DATE);
  218. } else {
  219. var grav = payload.grav.isUpdatable ? 'Grav v' + payload.grav.available : '';
  220. var resources = payload.resources.total ? payload.resources.total + ' ' + translations.PLUGIN_ADMIN.UPDATES_ARE_AVAILABLE: '';
  221. if (!resources) { grav += ' ' + translations.PLUGIN_ADMIN.IS_AVAILABLE_FOR_UPDATE }
  222. toastr.info(grav + (grav && resources ? ' ' + translations.PLUGIN_ADMIN.AND + ' ' : '') + resources);
  223. }
  224. }
  225. }
  226. });
  227. });
  228. var GPMRefresh = function (options) {
  229. options = options || {};
  230. var data = {
  231. task: 'GPM',
  232. action: 'getUpdates'
  233. };
  234. if (options.flush) { data.flush = true; }
  235. GravAjax({
  236. dataType: "JSON",
  237. url: window.location.href,
  238. method: "POST",
  239. data: data,
  240. toastErrors: true,
  241. success: function (response) {
  242. var grav = response.payload.grav,
  243. installed = response.payload.installed,
  244. resources = response.payload.resources,
  245. task = 'task' + GravAdmin.config.param_sep;
  246. // grav updatable
  247. if (grav.isUpdatable) {
  248. var icon = '<i class="fa fa-bullhorn"></i> ';
  249. content = 'Grav <b>v{available}</b> ' + translations.PLUGIN_ADMIN.IS_NOW_AVAILABLE + '! <span class="less">(' + translations.PLUGIN_ADMIN.CURRENT + ': v{version})</span> ',
  250. button = '<button data-maintenance-update="' + GravAdmin.config.base_url_relative + '/update.json/' + task + 'updategrav" class="button button-small secondary" id="grav-update-button">' + translations.PLUGIN_ADMIN.UPDATE_GRAV_NOW + '</button>';
  251. if (grav.isSymlink) {
  252. button = '<span class="hint--left" style="float: right;" data-hint="' + translations.PLUGIN_ADMIN.GRAV_SYMBOLICALLY_LINKED + '"><i class="fa fa-fw fa-link"></i></span>';
  253. }
  254. content = jQuery.substitute(content, {available: grav.available, version: grav.version});
  255. $('[data-gpm-grav]').addClass('grav').html('<p>' + icon + content + button + '</p>');
  256. window.grav_available_version = grav.available;
  257. }
  258. $('#grav-update-button').on('click', function() {
  259. $(this).html(translations.PLUGIN_ADMIN.UPDATING_PLEASE_WAIT + ' ' + bytesToSize(grav.assets['grav-update'].size) + '..');
  260. });
  261. // dashboard
  262. if ($('.updates-chart').length) {
  263. var missing = (resources.total + (grav.isUpdatable ? 1 : 0)) * 100 / (installed + (grav.isUpdatable ? 1 : 0)),
  264. updated = 100 - missing;
  265. UpdatesChart.update({series: [updated, missing]});
  266. if (resources.total) {
  267. $('#updates [data-maintenance-update]').fadeIn();
  268. }
  269. }
  270. if (!resources.total) {
  271. $('#updates [data-maintenance-update]').fadeOut();
  272. $('.badges.with-updates').removeClass('with-updates').find('.badge.updates').remove();
  273. } else {
  274. var length,
  275. icon = '<i class="fa fa-bullhorn"></i>',
  276. content = '{updates} ' + translations.PLUGIN_ADMIN.OF_YOUR + ' {type} ' + translations.PLUGIN_ADMIN.HAVE_AN_UPDATE_AVAILABLE,
  277. button = '<a href="{location}/' + task + 'update" class="button button-small secondary">' + translations.PLUGIN_ADMIN.UPDATE + ' {Type}</a>',
  278. plugins = $('.grav-update.plugins'),
  279. themes = $('.grav-update.themes'),
  280. sidebar = {plugins: $('#admin-menu a[href$="/plugins"]'), themes: $('#admin-menu a[href$="/themes"]')};
  281. // sidebar
  282. if (sidebar.plugins.length || sidebar.themes.length) {
  283. var length, badges;
  284. if (sidebar.plugins.length && (length = Object.keys(resources.plugins).length)) {
  285. badges = sidebar.plugins.find('.badges');
  286. badges.addClass('with-updates');
  287. badges.find('.badge.updates').text(length);
  288. }
  289. if (sidebar.themes.length && (length = Object.keys(resources.themes).length)) {
  290. badges = sidebar.themes.find('.badges');
  291. badges.addClass('with-updates');
  292. badges.find('.badge.updates').text(length);
  293. }
  294. }
  295. // list page
  296. if (plugins[0] && (length = Object.keys(resources.plugins).length)) {
  297. content = jQuery.substitute(content, {updates: length, type: 'plugins'});
  298. button = jQuery.substitute(button, {Type: 'All Plugins', location: GravAdmin.config.base_url_relative + '/plugins'});
  299. plugins.html('<p>' + icon + content + button + '</p>');
  300. var plugin, url;
  301. $.each(resources.plugins, function (key, value) {
  302. plugin = $('[data-gpm-plugin="' + key + '"] .gpm-name');
  303. url = plugin.find('a');
  304. if (!plugin.find('.badge.update').length) {
  305. plugin.append('<a class="plugin-update-button" href="' + url.attr('href') + '"><span class="badge update">' + translations.PLUGIN_ADMIN.UPDATE_AVAILABLE + '!</span></a>');
  306. }
  307. });
  308. }
  309. if (themes[0] && (length = Object.keys(resources.themes).length)) {
  310. content = jQuery.substitute(content, {updates: length, type: 'themes'});
  311. button = jQuery.substitute(button, {Type: 'All Themes', location: GravAdmin.config.base_url_relative + '/themes'});
  312. themes.html('<p>' + icon + content + button + '</p>');
  313. var theme, url;
  314. $.each(resources.themes, function (key, value) {
  315. theme = $('[data-gpm-theme="' + key + '"]');
  316. url = theme.find('.gpm-name a');
  317. theme.append('<div class="gpm-ribbon"><a href="' + url.attr('href') + '">' + translations.PLUGIN_ADMIN.UPDATE.toUpperCase() + '</a></div>');
  318. });
  319. }
  320. // details page
  321. var type = 'plugin',
  322. details = $('.grav-update.plugin')[0];
  323. if (!details) {
  324. details = $('.grav-update.theme')[0];
  325. type = 'theme';
  326. }
  327. if (details){
  328. var slug = $('[data-gpm-' + type + ']').data('gpm-' + type),
  329. Type = type.charAt(0).toUpperCase() + type.substring(1),
  330. resource = resources[type + 's'][slug];
  331. if (resource) {
  332. content = '<strong>v{available}</strong> ' + translations.PLUGIN_ADMIN.OF_THIS + ' ' + type + ' ' + translations.PLUGIN_ADMIN.IS_NOW_AVAILABLE + '!';
  333. content = jQuery.substitute(content, { available: resource.available });
  334. button = jQuery.substitute(button, {
  335. Type: Type,
  336. location: GravAdmin.config.base_url_relative + '/' + type + 's/' + slug
  337. });
  338. $(details).html('<p>' + icon + content + button + '</p>');
  339. }
  340. }
  341. }
  342. if (options.callback && typeof options.callback == 'function') options.callback(response);
  343. }
  344. });
  345. };
  346. if (GravAdmin.config.enable_auto_updates_check === '1') {
  347. GPMRefresh();
  348. }
  349. function reIndex (collection) {
  350. var holder = collection.find('[data-collection-holder]'),
  351. addBtn = collection.find('[data-action="add"]'),
  352. prefix = holder.data('collection-holder'),
  353. index = 0;
  354. holder.find('[data-collection-item]').each(function () {
  355. var item = $(this),
  356. currentIndex = item.attr('data-collection-key');
  357. if (index != currentIndex) {
  358. var r = new RegExp('^' + prefix.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + '[\.\[]' + currentIndex);
  359. item.attr('data-collection-item', item.attr('data-collection-item').replace(r, prefix + '.' + index));
  360. item.attr('data-collection-key', index);
  361. item.find('[name]').each(function () {
  362. $(this).attr('name', $(this).attr('name').replace(r, prefix + '[' + index));
  363. });
  364. }
  365. index++;
  366. });
  367. addBtn.data('key-index', index);
  368. }
  369. // Collections
  370. $('[data-type="collection"]').each(function () {
  371. var el = $(this),
  372. holder = el.find('[data-collection-holder]'),
  373. config = el.find('[data-collection-config]'),
  374. isArray = config.data('collection-array'),
  375. template = el.find('[data-collection-template="new"]').html();
  376. // make sortable
  377. new Sortable(holder[0], {
  378. filter: '.form-input-wrapper',
  379. onUpdate: function () {
  380. if (isArray)
  381. reIndex(el);
  382. }
  383. });
  384. // hook up delete
  385. el.on('click', '[data-action="delete"]', function (e) {
  386. $(this).closest('[data-collection-item]').remove();
  387. if (isArray)
  388. reIndex(el);
  389. });
  390. // hook up add
  391. el.find('[data-action="add"]').on('click', function (e) {
  392. var button = $(this),
  393. key = button.data('key-index'),
  394. newItem = $(template);
  395. newItem.attr('data-collection-item', newItem.attr('data-collection-item').replace('*', key));
  396. newItem.attr('data-collection-key', key);
  397. newItem.find('[name]').each(function () {
  398. $(this).attr('name', $(this).attr('name').replace('*', key));
  399. });
  400. holder.append(newItem);
  401. button.data('key-index', ++key);
  402. });
  403. });
  404. // enable the toggleable checkbox when typing in the corresponding textarea/input element
  405. jQuery(document).on('input propertychange click', '.form-data textarea, .form-data input, .form-data label, .form-data .selectize-input', function() {
  406. var item = this;
  407. var checkbox = $(item).parents('.form-field').find('.toggleable input[type="checkbox"]');
  408. if (checkbox.length > 0) {
  409. checkbox.prop('checked', true);
  410. }
  411. $(this).css('opacity', 1);
  412. $(this).parents('.form-data').css('opacity', 1);
  413. checkbox.css('opacity', 1);
  414. checkbox.prop('checked', true);
  415. checkbox.prop('value', 1);
  416. checkbox.siblings('label').css('opacity', 1);
  417. checkbox.parent().siblings('label').css('opacity', 1);
  418. });
  419. // when clicking the label, click the corresponding checkbox automatically
  420. jQuery(document).on('click', 'label.toggleable', function() {
  421. var input = $(this).siblings('.checkboxes.toggleable').find('input');
  422. var on = !input.is(':checked');
  423. input.prop('checked', on);
  424. input.prop('value', on ? 1 : 0);
  425. $(this).css('opacity', on ? 1 : 0.7);
  426. input.siblings('label').css('opacity', on ? 1 : 0.7);
  427. $(this).parents('.form-label').siblings('.form-data').css('opacity', on ? 1 : 0.7);
  428. });
  429. // Themes Switcher Warning
  430. $(document).on('mousedown', '[data-remodal-target="theme-switch-warn"]', function(e){
  431. var name = $(e.target).closest('[data-gpm-theme]').find('.gpm-name a').text(),
  432. remodal = $('.remodal.theme-switcher');
  433. remodal.find('strong').text(name);
  434. remodal.find('.button.continue').attr('href', $(e.target).attr('href'));
  435. });
  436. // Setup keep-alive on pages that have at least one element with data-grav-keepalive="true" set
  437. if ($(document).find('[data-grav-keepalive="true"]').length > 0) {
  438. setInterval(function() {
  439. keepAlive();
  440. }, (GravAdmin.config.admin_timeout/2)*1000);
  441. }
  442. });