UIWindowItemProperties.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /**
  2. * Copyright (C) 2024 Puter Technologies Inc.
  3. *
  4. * This file is part of Puter.
  5. *
  6. * Puter is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published
  8. * by the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. import UIWindow from './UIWindow.js'
  20. // todo do this using uid rather than item_path, since item_path is way mroe expensive on the DB
  21. async function UIWindowItemProperties(item_name, item_path, item_uid, left, top, width, height){
  22. let h = '';
  23. h += `<div class="item-props-tabview" style="display: flex; flex-direction: column; height: 100%;">`;
  24. // tabs
  25. h += `<div class="item-props-tab">`;
  26. h += `<div class="item-props-tab-btn antialiased disable-user-select item-props-tab-selected" data-tab="general">General</div>`;
  27. h += `<div class="item-props-tab-btn antialiased disable-user-select item-props-tab-btn-versions" data-tab="versions">Versions</div>`;
  28. h += `</div>`;
  29. h+= `<div class="item-props-tab-content item-props-tab-content-selected" data-tab="general" style="border-top-left-radius:0;">`;
  30. h += `<table class="item-props-tbl">`;
  31. h += `<tr><td class="item-prop-label">Name</td><td class="item-prop-val item-prop-val-name"></td></tr>`;
  32. h += `<tr><td class="item-prop-label">Path</td><td class="item-prop-val item-prop-val-path"></td></tr>`;
  33. h += `<tr class="item-prop-original-name"><td class="item-prop-label">Original Name</td><td class="item-prop-val item-prop-val-original-name"></td></tr>`;
  34. h += `<tr class="item-prop-original-path"><td class="item-prop-label">Original Path</td><td class="item-prop-val item-prop-val-original-path"></td></tr>`;
  35. h += `<tr><td class="item-prop-label">Shortcut to</td><td class="item-prop-val item-prop-val-shortcut-to"></td></tr>`;
  36. h += `<tr><td class="item-prop-label">UID</td><td class="item-prop-val item-prop-val-uid"></td></tr>`;
  37. h += `<tr><td class="item-prop-label">Type</td><td class="item-prop-val item-prop-val-type"></td></tr>`;
  38. h += `<tr><td class="item-prop-label">Size</td><td class="item-prop-val item-prop-val-size"></td></tr>`;
  39. h += `<tr><td class="item-prop-label">Modified</td><td class="item-prop-val item-prop-val-modified"></td></tr>`;
  40. h += `<tr><td class="item-prop-label">Created</td><td class="item-prop-val item-prop-val-created"></td></tr>`;
  41. h += `<tr><td class="item-prop-label">Versions</td><td class="item-prop-val item-prop-val-versions"></td></tr>`;
  42. h += `<tr><td class="item-prop-label">Associated Websites</td><td class="item-prop-val item-prop-val-websites">`;
  43. h += `</td></tr>`;
  44. h += `<tr><td class="item-prop-label">Access Granted To</td><td class="item-prop-val item-prop-val-permissions"></td></tr>`;
  45. h += `</table>`;
  46. h += `</div>`;
  47. h += `<div class="item-props-tab-content" data-tab="versions" style="padding: 20px;">`
  48. h += `<div class="item-props-version-list">`;
  49. h += `</div>`;
  50. h += `</div>`;
  51. h += `</div>`;
  52. const el_window = await UIWindow({
  53. title: `${item_name} properties`,
  54. app: item_uid+'-account',
  55. single_instance: true,
  56. icon: null,
  57. uid: null,
  58. is_dir: false,
  59. body_content: h,
  60. draggable_body: false,
  61. has_head: true,
  62. selectable_body: false,
  63. draggable_body: false,
  64. allow_context_menu: false,
  65. is_resizable: false,
  66. is_droppable: false,
  67. init_center: true,
  68. allow_native_ctxmenu: true,
  69. allow_user_select: true,
  70. left: left,
  71. top: top,
  72. width: width,
  73. height: height,
  74. onAppend: function(el_window){
  75. },
  76. width: 450,
  77. window_class: 'window-item-properties',
  78. window_css:{
  79. // height: 'initial',
  80. },
  81. body_css: {
  82. padding: '10px',
  83. width: 'initial',
  84. height: 'calc(100% - 50px)',
  85. 'background-color': 'rgb(241 242 246)',
  86. 'backdrop-filter': 'blur(3px)',
  87. 'content-box': 'content-box',
  88. }
  89. })
  90. // item props tab click handler
  91. $(el_window).find('.item-props-tab-btn').click(function(e){
  92. // unselect all tabs
  93. $(el_window).find('.item-props-tab-btn').removeClass('item-props-tab-selected');
  94. // select this tab
  95. $(this).addClass('item-props-tab-selected');
  96. // unselect all tab contents
  97. $(el_window).find('.item-props-tab-content').removeClass('item-props-tab-content-selected');
  98. // select this tab content
  99. $(el_window).find(`.item-props-tab-content[data-tab="${$(this).attr('data-tab')}"]`).addClass('item-props-tab-content-selected');
  100. })
  101. // /stat
  102. puter.fs.stat({
  103. uid: item_uid,
  104. returnSubdomains: true,
  105. returnPermissions: true,
  106. returnVersions: true,
  107. returnSize: true,
  108. success: function (fsentry){
  109. // hide versions tab if item is a directory
  110. if(fsentry.is_dir){
  111. $(el_window).find('[data-tab="versions"]').hide();
  112. }
  113. // name
  114. $(el_window).find('.item-prop-val-name').html(fsentry.name);
  115. // path
  116. $(el_window).find('.item-prop-val-path').html(item_path);
  117. // original name & path
  118. if(fsentry.metadata){
  119. try{
  120. let metadata = JSON.parse(fsentry.metadata);
  121. if(metadata.original_name){
  122. $(el_window).find('.item-prop-val-original-name').html(metadata.original_name);
  123. $(el_window).find('.item-prop-original-name').show();
  124. }
  125. if(metadata.original_path){
  126. $(el_window).find('.item-prop-val-original-path').html(metadata.original_path);
  127. $(el_window).find('.item-prop-original-path').show();
  128. }
  129. }catch(e){}
  130. }
  131. // shortcut to
  132. if(fsentry.shortcut_to && fsentry.shortcut_to_path){
  133. $(el_window).find('.item-prop-val-shortcut-to').html(fsentry.shortcut_to_path);
  134. }
  135. // uid
  136. $(el_window).find('.item-prop-val-uid').html(fsentry.id);
  137. // type
  138. $(el_window).find('.item-prop-val-type').html(fsentry.is_dir ? 'Directory' : (fsentry.type === null ? '-' : fsentry.type));
  139. // size
  140. $(el_window).find('.item-prop-val-size').html(fsentry.size === null || fsentry.size === undefined ? '-' : byte_format(fsentry.size));
  141. // modified
  142. $(el_window).find('.item-prop-val-modified').html(fsentry.modified === 0 ? '-' : timeago.format(fsentry.modified*1000));
  143. // created
  144. $(el_window).find('.item-prop-val-created').html(fsentry.created === 0 ? '-' : timeago.format(fsentry.created*1000));
  145. // subdomains
  146. if(fsentry.subdomains && fsentry.subdomains.length > 0 ){
  147. fsentry.subdomains.forEach(subdomain => {
  148. $(el_window).find('.item-prop-val-websites').append(`<p class="item-prop-website-entry" data-uuid="${subdomain.uuid}" style="margin-bottom:5px; margin-top:5px;"><a target="_blank" href="${subdomain.address}">${subdomain.address}</a> (<span class="disassociate-website-link" data-uuid="${subdomain.uuid}" data-subdomain="${extractSubdomain(subdomain.address)}">disassociate</span>)</p>`);
  149. });
  150. }
  151. else{
  152. $(el_window).find('.item-prop-val-websites').append('-');
  153. }
  154. // versions
  155. if(fsentry.versions && fsentry.versions.length > 0 ){
  156. fsentry.versions.reverse().forEach(version => {
  157. $(el_window).find('.item-props-version-list')
  158. .append(`<div class="item-prop-version-entry">${version.user? version.user.username : ''} &bull; ${timeago.format(version.timestamp*1000)}<p style="font-size:10px;">${version.id}</p></div>`);
  159. });
  160. }
  161. else{
  162. $(el_window).find('.item-props-version-list').append('-');
  163. }
  164. // owner
  165. $(el_window).find('.item-prop-val-permissions').append(`<p class="item-prop-perm-entry" style="margin-bottom:5px; margin-top:5px;">${(fsentry.owner.email === undefined || fsentry.owner.email === null) ? fsentry.owner.username : fsentry.owner.email} (owner)</p>`);
  166. // other users with access
  167. if(fsentry.permissions && fsentry.permissions.length > 0 ){
  168. fsentry.permissions.forEach(perm => {
  169. let h = ``;
  170. // username/email
  171. h += `<p class="item-prop-perm-entry" data-perm-uid="${perm.uid}" style="margin-bottom:5px; margin-top:5px;">${perm.email ?? perm.username} `;
  172. // remove
  173. h += `(<span class="remove-permission-link" data-perm-uid="${perm.uid}">remove</span>)`;
  174. $(el_window).find('.item-prop-val-permissions').append(h);
  175. });
  176. }
  177. else{
  178. $(el_window).find('.item-prop-val-permissions').append('-');
  179. }
  180. $(el_window).find(`.disassociate-website-link`).on('click', function(e){
  181. puter.hosting.update(
  182. $(e.target).attr('data-subdomain'),
  183. null).then(()=>{
  184. $(el_window).find(`.item-prop-website-entry[data-uuid="${$(e.target).attr('data-uuid')}"]`).remove();
  185. if($(el_window).find(`.item-prop-website-entry`).length === 0){
  186. $(el_window).find(`.item-prop-val-websites`).html('-');
  187. // remove the website badge from all instances of the dir
  188. $(`.item[data-uid="${item_uid}"]`).find('.item-has-website-badge').fadeOut(200);
  189. }
  190. }
  191. )
  192. })
  193. $(el_window).find('.remove-permission-link').on('click', function(e){
  194. const el_remove_perm_link= this;
  195. const perm_uid = $(el_remove_perm_link).attr('data-perm-uid');
  196. $.ajax({
  197. url: api_origin + "/remove-perm",
  198. type: 'POST',
  199. async: true,
  200. contentType: "application/json",
  201. data: JSON.stringify({
  202. uid: perm_uid,
  203. }),
  204. headers: {
  205. "Authorization": "Bearer "+auth_token
  206. },
  207. statusCode: {
  208. 401: function () {
  209. logout();
  210. },
  211. },
  212. success: async function (res){
  213. $(el_window).find(`.item-prop-perm-entry[data-perm-uid="${perm_uid}"]`).remove();
  214. if($(el_window).find(`.item-prop-perm-entry`).length === 0){
  215. $(el_window).find(`.item-prop-val-permissions`).html('-');
  216. // todo is it better to combine the following two queriesinto one css selector?
  217. $(`.item[data-uid="${item_uid}"]`).find(`.item-is-shared`).fadeOut(200);
  218. // todo optim do this only if item is a directory
  219. // todo this has to be case-insensitive but the `i` selector doesn't work on ^=
  220. $(`.item[data-path^="${item_path}/"]`).find(`.item-is-shared`).fadeOut(200);
  221. }
  222. },
  223. complete: function(){
  224. }
  225. })
  226. })
  227. }
  228. })
  229. }
  230. export default UIWindowItemProperties