packages.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. import $ from 'jquery';
  2. import { config, translations } from 'grav-config';
  3. import request from '../utils/request';
  4. import { Instance as gpm } from '../utils/gpm';
  5. import { Promise } from 'es6-promise';
  6. class Sorter {
  7. getElements(elements, container) {
  8. this.elements = elements || $('[data-gpm-plugin], [data-gpm-theme]');
  9. this.container = container || $('.gpm-plugins > table > tbody, .gpm-themes > .themes.card-row');
  10. return this.elements;
  11. }
  12. static sort(A, B, direction = 'asc') {
  13. if (A > B) { return (direction === 'asc') ? 1 : -1; }
  14. if (A < B) { return (direction === 'asc') ? -1 : 1; }
  15. return 0;
  16. }
  17. byCommon(direction = 'asc', data = '') {
  18. let elements = this.getElements().sort((a, b) => {
  19. let A = $(a).data(data).toString().toLowerCase();
  20. let B = $(b).data(data).toString().toLowerCase();
  21. return Sorter.sort(A, B, direction);
  22. });
  23. return elements.appendTo(this.container);
  24. }
  25. byName(direction = 'asc', data = 'gpm-name') {
  26. return this.byCommon(direction, data);
  27. }
  28. byAuthor(direction = 'asc', data = 'gpm-author') {
  29. return this.byCommon(direction, data);
  30. }
  31. byOfficial(direction = 'asc', data = 'gpm-official') {
  32. return this.byCommon(direction, data);
  33. }
  34. byReleaseDate(direction = 'asc', data = 'gpm-release-date') {
  35. let elements = this.getElements().sort((a, b) => {
  36. let A = new Date($(a).data(data)).getTime();
  37. let B = new Date($(b).data(data)).getTime();
  38. return Sorter.sort(A, B, direction === 'asc' ? 'desc' : 'asc');
  39. });
  40. elements.appendTo(this.container);
  41. }
  42. byUpdatable(direction = 'asc', data = 'gpm-updatable') {
  43. return this.byCommon(direction, data);
  44. }
  45. byEnabled(direction = 'asc', data = 'gpm-enabled') {
  46. return this.byCommon(direction, data);
  47. }
  48. byTesting(direction = 'asc', data = 'gpm-testing') {
  49. return this.byCommon(direction, data);
  50. }
  51. }
  52. class Packages {
  53. constructor() {
  54. this.Sort = new Sorter();
  55. }
  56. static getBackToList(type) {
  57. global.location.href = `${config.base_url_relative}/${type}s`;
  58. }
  59. static addDependencyToList(type, dependency, slug = '') {
  60. if (['admin', 'form', 'login', 'email', 'grav'].indexOf(dependency) !== -1) { return; }
  61. let container = $('.package-dependencies-container');
  62. let text = `${dependency} <a href="#" class="button" data-dependency-slug="${dependency}" data-${type}-action="remove-dependency-package">Remove</a>`;
  63. if (slug) {
  64. text += ` (was needed by ${slug})`;
  65. }
  66. container.append(`<li>${text}</li>`);
  67. }
  68. addDependenciesToList(dependencies, slug = '') {
  69. dependencies.forEach((dependency) => {
  70. Packages.addDependencyToList('plugin', dependency.name || dependency, slug);
  71. });
  72. }
  73. static getTaskUrl(type, task) {
  74. let url = `${config.base_url_relative}`;
  75. url += `/${type}s.json`;
  76. url += `/task${config.param_sep}${task}`;
  77. return url;
  78. }
  79. static getRemovePackageUrl(type) {
  80. return `${Packages.getTaskUrl(type, 'removePackage')}`;
  81. }
  82. static getReinstallPackageUrl(type) {
  83. return `${Packages.getTaskUrl(type, 'reinstallPackage')}`;
  84. }
  85. static getGetPackagesDependenciesUrl(type) {
  86. return `${Packages.getTaskUrl(type, 'getPackagesDependencies')}`;
  87. }
  88. static getInstallDependenciesOfPackagesUrl(type) {
  89. return `${Packages.getTaskUrl(type, 'installDependenciesOfPackages')}`;
  90. }
  91. static getInstallPackageUrl(type) {
  92. return `${Packages.getTaskUrl(type, 'installPackage')}`;
  93. }
  94. removePackage(type, slug) {
  95. let url = Packages.getRemovePackageUrl(type);
  96. request(url, {
  97. method: 'post',
  98. body: {
  99. package: slug
  100. }
  101. }, (response) => {
  102. if (response.status === 'success') {
  103. $('.remove-package-confirm').addClass('hidden');
  104. if (response.dependencies && response.dependencies.length > 0) {
  105. this.addDependenciesToList(response.dependencies);
  106. $('.remove-package-dependencies').removeClass('hidden');
  107. } else {
  108. $('.remove-package-done').removeClass('hidden');
  109. }
  110. // The package was removed. When the modal closes, move to the packages list
  111. $(document).on('closing', '[data-remodal-id="remove-package"]', () => {
  112. Packages.getBackToList(type);
  113. });
  114. } else {
  115. $('.remove-package-confirm').addClass('hidden');
  116. $('.remove-package-error').removeClass('hidden');
  117. }
  118. });
  119. }
  120. reinstallPackage(type, slug, package_name, current_version) {
  121. $('.button-bar button').addClass('hidden');
  122. $('.button-bar .spinning-wheel').removeClass('hidden');
  123. let url = Packages.getReinstallPackageUrl(type);
  124. request(url, {
  125. method: 'post',
  126. body: {
  127. slug: slug,
  128. type: type,
  129. package_name: package_name,
  130. current_version: current_version
  131. }
  132. }, (response) => {
  133. if (response.status === 'success') {
  134. $('.reinstall-package-confirm').addClass('hidden');
  135. $('.reinstall-package-done').removeClass('hidden');
  136. } else {
  137. $('.reinstall-package-confirm').addClass('hidden');
  138. $('.reinstall-package-error').removeClass('hidden');
  139. }
  140. window.location.reload();
  141. });
  142. }
  143. removeDependency(type, slug, button) {
  144. let url = Packages.getRemovePackageUrl(type);
  145. request(url, {
  146. method: 'post',
  147. body: {
  148. package: slug
  149. }
  150. }, (response) => {
  151. if (response.status === 'success') {
  152. button.removeClass('button');
  153. button.replaceWith($('<span>Removed successfully</span>'));
  154. if (response.dependencies && response.dependencies.length > 0) {
  155. this.addDependenciesToList(response.dependencies, slug);
  156. }
  157. }
  158. });
  159. }
  160. static addNeededDependencyToList(action, slug) {
  161. $('.install-dependencies-package-container .type-' + action).removeClass('hidden');
  162. let list = $('.install-dependencies-package-container .type-' + action + ' ul');
  163. if (action !== 'install') {
  164. let current_version = '';
  165. let available_version = '';
  166. let name = '';
  167. let resources = gpm.payload.payload.resources;
  168. if (resources.plugins[slug]) {
  169. available_version = resources.plugins[slug].available;
  170. current_version = resources.plugins[slug].version;
  171. name = resources.plugins[slug].name;
  172. } else if (resources.themes[slug]) {
  173. available_version = resources.themes[slug].available;
  174. current_version = resources.themes[slug].version;
  175. name = resources.themes[slug].name;
  176. }
  177. list.append(`<li>${name ? name : slug}, ${translations.PLUGIN_ADMIN.FROM} v<strong>${current_version}</strong> ${translations.PLUGIN_ADMIN.TO} v<strong>${available_version}</strong></li>`);
  178. } else {
  179. list.append(`<li>${name ? name : slug}</li>`);
  180. }
  181. }
  182. getPackagesDependencies(type, slugs, finishedLoadingCallback) {
  183. let url = Packages.getGetPackagesDependenciesUrl(type);
  184. request(url, {
  185. method: 'post',
  186. body: {
  187. packages: slugs
  188. }
  189. }, (response) => {
  190. finishedLoadingCallback();
  191. if (response.status === 'success') {
  192. if (response.dependencies) {
  193. let hasDependencies = false;
  194. for (var dependency in response.dependencies) {
  195. if (response.dependencies.hasOwnProperty(dependency)) {
  196. if (dependency === 'grav') {
  197. continue;
  198. }
  199. hasDependencies = true;
  200. let dependencyName = dependency;
  201. let action = response.dependencies[dependency];
  202. Packages.addNeededDependencyToList(action, dependencyName);
  203. }
  204. }
  205. if (hasDependencies) {
  206. $('[data-packages-modal] .install-dependencies-package-container').removeClass('hidden');
  207. } else {
  208. $('[data-packages-modal] .install-package-container').removeClass('hidden');
  209. }
  210. } else {
  211. $('[data-packages-modal] .install-package-container').removeClass('hidden');
  212. }
  213. } else {
  214. $('[data-packages-modal] .install-package-error').removeClass('hidden');
  215. }
  216. });
  217. }
  218. installDependenciesOfPackages(type, slugs, callbackSuccess, callbackError) {
  219. let url = Packages.getInstallDependenciesOfPackagesUrl(type);
  220. request(url, {
  221. method: 'post',
  222. body: {
  223. packages: slugs
  224. }
  225. }, callbackSuccess);
  226. }
  227. installPackages(type, slugs, callbackSuccess) {
  228. let url = Packages.getInstallPackageUrl(type);
  229. Promise.all(slugs.map((slug) => {
  230. return request(url, {
  231. method: 'post',
  232. body: {
  233. package: slug,
  234. type: type
  235. }
  236. });
  237. })).then(callbackSuccess);
  238. }
  239. static getSlugsFromEvent(event) {
  240. let slugs = '';
  241. if ($(event.target).is('[data-packages-slugs]')) {
  242. slugs = $(event.target).attr('data-packages-slugs');
  243. } else {
  244. slugs = $(event.target).parent('[data-packages-slugs]').attr('data-packages-slugs');
  245. }
  246. if (typeof slugs === 'undefined') {
  247. return null;
  248. }
  249. slugs = slugs.split(',');
  250. return typeof slugs === 'string' ? [slugs] : slugs;
  251. }
  252. handleGettingPackageDependencies(type, event, action = 'update') {
  253. let slugs = Packages.getSlugsFromEvent(event);
  254. if (!slugs) {
  255. alert('No slug set');
  256. return;
  257. }
  258. // Cleanup
  259. $('.packages-names-list').html('');
  260. $('.install-dependencies-package-container li').remove();
  261. slugs.forEach((slug) => {
  262. if (action === 'update') {
  263. let current_version = '';
  264. let available_version = '';
  265. let name = '';
  266. let resources = gpm.payload.payload.resources;
  267. if (resources.plugins[slug]) {
  268. available_version = resources.plugins[slug].available;
  269. current_version = resources.plugins[slug].version;
  270. name = resources.plugins[slug].name;
  271. } else if (resources.themes[slug]) {
  272. available_version = resources.themes[slug].available;
  273. current_version = resources.themes[slug].version;
  274. name = resources.themes[slug].name;
  275. }
  276. $('.packages-names-list').append(`<li>${name ? name : slug}, ${translations.PLUGIN_ADMIN.FROM} v<strong>${current_version}</strong> ${translations.PLUGIN_ADMIN.TO} v<strong>${available_version}</strong></li>`);
  277. } else {
  278. $('.packages-names-list').append(`<li>${name ? name : slug}</li>`);
  279. }
  280. });
  281. event.preventDefault();
  282. event.stopPropagation();
  283. // Restore original state
  284. $('[data-packages-modal] .loading').removeClass('hidden');
  285. $('[data-packages-modal] .install-dependencies-package-container').addClass('hidden');
  286. $('[data-packages-modal] .install-package-container').addClass('hidden');
  287. $('[data-packages-modal] .installing-dependencies').addClass('hidden');
  288. $('[data-packages-modal] .installing-package').addClass('hidden');
  289. $('[data-packages-modal] .installation-complete').addClass('hidden');
  290. $('[data-packages-modal] .install-package-error').addClass('hidden');
  291. this.getPackagesDependencies(type, slugs, () => {
  292. let slugs_string = slugs.join();
  293. $(`[data-packages-modal] [data-${type}-action="install-dependencies-and-package"]`).attr('data-packages-slugs', slugs_string);
  294. $(`[data-packages-modal] [data-${type}-action="install-package"]`).attr('data-packages-slugs', slugs_string);
  295. $('[data-packages-modal] .loading').addClass('hidden');
  296. });
  297. }
  298. handleInstallingDependenciesAndPackage(type, event) {
  299. let slugs = Packages.getSlugsFromEvent(event);
  300. event.preventDefault();
  301. event.stopPropagation();
  302. $('[data-packages-modal] .install-dependencies-package-container').addClass('hidden');
  303. $('[data-packages-modal] .installing-dependencies').removeClass('hidden');
  304. this.installDependenciesOfPackages(type, slugs, (response) => {
  305. $('[data-packages-modal] .installing-dependencies').addClass('hidden');
  306. $('[data-packages-modal] .installing-package').removeClass('hidden');
  307. this.installPackages(type, slugs, () => {
  308. $('[data-packages-modal] .installing-package').addClass('hidden');
  309. $('[data-packages-modal] .installation-complete').removeClass('hidden');
  310. if (response.status === 'error') {
  311. let remodal = $.remodal.lookup[$('[data-packages-modal]').data('remodal')];
  312. remodal.close();
  313. return;
  314. }
  315. setTimeout(() => {
  316. if (slugs.length === 1) {
  317. global.location.href = `${config.base_url_relative}/${type}s/${slugs[0]}`;
  318. } else {
  319. global.location.href = `${config.base_url_relative}/${type}s`;
  320. }
  321. }, 1000);
  322. });
  323. });
  324. }
  325. handleInstallingPackage(type, event) {
  326. let slugs = Packages.getSlugsFromEvent(event);
  327. event.preventDefault();
  328. event.stopPropagation();
  329. $('[data-packages-modal] .install-package-container').addClass('hidden');
  330. $('[data-packages-modal] .installing-package').removeClass('hidden');
  331. this.installPackages(type, slugs, (response) => {
  332. $('[data-packages-modal] .installing-package').addClass('hidden');
  333. $('[data-packages-modal] .installation-complete').removeClass('hidden');
  334. if (response.status === 'error') {
  335. let remodal = $.remodal.lookup[$('[data-packages-modal]').data('remodal')];
  336. remodal.close();
  337. return;
  338. }
  339. if (slugs.length === 1) {
  340. global.location.href = `${config.base_url_relative}/${type}s/${slugs[0]}`;
  341. } else {
  342. global.location.href = `${config.base_url_relative}/${type}s`;
  343. }
  344. });
  345. }
  346. handleRemovingPackage(type, event) {
  347. let slug = $(event.target).attr('data-packages-slugs');
  348. event.preventDefault();
  349. event.stopPropagation();
  350. this.removePackage(type, slug);
  351. }
  352. handleReinstallPackage(type, event) {
  353. let target = $(event.target);
  354. let slug = target.attr('data-package-slug');
  355. let package_name = target.attr('data-package-name');
  356. let current_version = target.attr('data-package-current-version');
  357. event.preventDefault();
  358. event.stopPropagation();
  359. this.reinstallPackage(type, slug, package_name, current_version);
  360. }
  361. handleRemovingDependency(type, event) {
  362. let slug = $(event.target).attr('data-dependency-slug');
  363. let button = $(event.target);
  364. event.preventDefault();
  365. event.stopPropagation();
  366. this.removeDependency(type, slug, button);
  367. }
  368. }
  369. export default new Packages();