1/* global escapeHTML */ 2 3/** 4 * @namespace 5 */ 6OC.Share = _.extend(OC.Share || {}, { 7 SHARE_TYPE_USER:0, 8 SHARE_TYPE_GROUP:1, 9 SHARE_TYPE_LINK:3, 10 SHARE_TYPE_GUEST:4, 11 SHARE_TYPE_REMOTE:6, 12 13 STATE_ACCEPTED: 0, 14 STATE_PENDING: 1, 15 STATE_REJECTED: 2, 16 17 /** 18 * Regular expression for splitting parts of remote share owners: 19 * "[email protected]/path/to/owncloud" 20 * "[email protected]@example.com/path/to/owncloud 21 */ 22 _REMOTE_OWNER_REGEXP: new RegExp("^([^@]*)@(([^@]*)@)?([^/]*)([/](.*)?)?$"), 23 24 /** 25 * @deprecated use OC.Share.currentShares instead 26 */ 27 itemShares:[], 28 /** 29 * Full list of all share statuses 30 */ 31 statuses:{}, 32 /** 33 * Shares for the currently selected file. 34 * (for which the dropdown is open) 35 * 36 * Key is item type and value is an array or 37 * shares of the given item type. 38 */ 39 currentShares: {}, 40 /** 41 * Whether the share dropdown is opened. 42 */ 43 droppedDown:false, 44 /** 45 * Loads ALL share statuses from server, stores them in 46 * OC.Share.statuses then calls OC.Share.updateIcons() to update the 47 * files "Share" icon to "Shared" according to their share status and 48 * share type. 49 * 50 * If a callback is specified, the update step is skipped. 51 * 52 * @param itemType item type 53 * @param fileList file list instance, defaults to OCA.Files.App.fileList 54 * @param callback function to call after the shares were loaded 55 */ 56 57 loadIcons:function(itemType, fileList, callback) { 58 var path = fileList.dirInfo.path; 59 if (path === '/') { 60 path = ''; 61 } 62 path += '/' + fileList.dirInfo.name; 63 64 // Load all share icons 65 $.get( 66 OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares', 67 { 68 subfiles: 'true', 69 path: path, 70 format: 'json' 71 }, function(result) { 72 if (result && result.ocs.meta.statuscode === 200) { 73 OC.Share.statuses = {}; 74 $.each(result.ocs.data, function(it, share) { 75 if (!(share.item_source in OC.Share.statuses)) { 76 OC.Share.statuses[share.item_source] = {link: false}; 77 } 78 if (share.share_type === OC.Share.SHARE_TYPE_LINK) { 79 OC.Share.statuses[share.item_source] = {link: true}; 80 } 81 }); 82 if (_.isFunction(callback)) { 83 callback(OC.Share.statuses); 84 } else { 85 OC.Share.updateIcons(itemType, fileList); 86 } 87 } 88 } 89 ); 90 }, 91 /** 92 * Updates the files' "Share" icons according to the known 93 * sharing states stored in OC.Share.statuses. 94 * (not reloaded from server) 95 * 96 * @param itemType item type 97 * @param fileList file list instance 98 * defaults to OCA.Files.App.fileList 99 */ 100 updateIcons:function(itemType, fileList){ 101 var item; 102 var $fileList; 103 var currentDir; 104 if (!fileList && OCA.Files) { 105 fileList = OCA.Files.App.fileList; 106 } 107 // fileList is usually only defined in the files app 108 if (fileList) { 109 $fileList = fileList.$fileList; 110 currentDir = fileList.getCurrentDirectory(); 111 } 112 // TODO: iterating over the files might be more efficient 113 for (item in OC.Share.statuses){ 114 var iconClass = 'icon-shared'; 115 var data = OC.Share.statuses[item]; 116 var hasLink = data.link; 117 // Links override shared in terms of icon display 118 if (hasLink) { 119 iconClass = 'icon-public'; 120 } 121 if (itemType !== 'file' && itemType !== 'folder') { 122 $('a.share[data-item="'+item+'"] .icon').removeClass('icon-shared icon-public').addClass(iconClass); 123 } else { 124 // TODO: ultimately this part should be moved to files_sharing app 125 var file = $fileList.find('tr[data-id="'+item+'"]'); 126 var shareFolder = OC.imagePath('core', 'filetypes/folder-shared'); 127 var img; 128 if (file.length > 0) { 129 this.markFileAsShared(file, true, hasLink); 130 } else { 131 var dir = currentDir; 132 if (dir.length > 1) { 133 var last = ''; 134 var path = dir; 135 // Search for possible parent folders that are shared 136 while (path != last) { 137 if (path === data.path && !data.link) { 138 var actions = $fileList.find('.fileactions .action[data-action="Share"]'); 139 var files = $fileList.find('.filename'); 140 var i; 141 for (i = 0; i < actions.length; i++) { 142 // TODO: use this.markFileAsShared() 143 img = $(actions[i]).find('img'); 144 if (img.attr('src') !== OC.imagePath('core', 'actions/public')) { 145 img.attr('src', image); 146 $(actions[i]).addClass('permanent'); 147 $(actions[i]).html('<span> '+t('core', 'Shared')+'</span>').prepend(img); 148 } 149 } 150 for(i = 0; i < files.length; i++) { 151 if ($(files[i]).closest('tr').data('type') === 'dir') { 152 $(files[i]).find('.thumbnail').css('background-image', 'url('+sh
152areFolder+')'); 153 } 154 } 155 } 156 last = path; 157 path = OC.Share.dirname(path); 158 } 159 } 160 } 161 } 162 } 163 }, 164 /** 165 * Format a remote address 166 * 167 * @param {String} remoteAddress full remote share 168 * @return {String} HTML code to display 169 */ 170 _formatRemoteShare: function(remoteAddress) { 171 var parts = this._REMOTE_OWNER_REGEXP.exec(remoteAddress); 172 if (!parts) { 173 // display as is, most likely to be a simple owner name 174 return escapeHTML(remoteAddress); 175 } 176 177 var userName = parts[1]; 178 var userDomain = parts[3]; 179 var server = parts[4]; 180 var dir = parts[6]; 181 var tooltip = userName; 182 if (userDomain) { 183 tooltip += '@' + userDomain; 184 } 185 if (server) { 186 if (!userDomain) { 187 userDomain = 'â¦'; 188 } 189 tooltip += '@' + server; 190 } 191 192 var html = '<span class="remoteAddress" title="' + escapeHTML(tooltip) + '">'; 193 html += '<span class="username">' + escapeHTML(userName) + '</span>'; 194 if (userDomain) { 195 html += '<span class="userDomain">@' + escapeHTML(userDomain) + '</span>'; 196 } 197 html += '</span>'; 198 return html; 199 }, 200 /** 201 * Loop over all recipients in the list and format them using 202 * all kind of fancy magic. 203 * 204 * @param {String[]} recipients array of all the recipients 205 * @return {String[]} modified list of recipients 206 */ 207 _formatShareList: function(recipients) { 208 var _parent = this; 209 return $.map(recipients, function(recipient) { 210 recipient = _parent._formatRemoteShare(recipient); 211 return recipient; 212 }); 213 }, 214 /** 215 * Marks/unmarks a given file as shared by changing its action icon 216 * and folder icon. 217 * 218 * @param $tr file element to mark as shared 219 * @param hasShares whether shares are available 220 * @param hasLink whether link share is available 221 */ 222 markFileAsShared: function($tr, hasShares, hasLink) { 223 var action = $tr.find('.fileactions .action[data-action="Share"]'); 224 var type = $tr.data('type'); 225 var icon = action.find('.icon'); 226 var message; 227 var recipients; 228 var owner = $tr.attr('data-share-owner'); 229 var shareFolderIcon; 230 var iconClass = 'icon-shared'; 231 action.removeClass('shared-style'); 232 // update folder icon 233 if (type === 'dir' && (hasShares || hasLink || owner)) { 234 if (hasLink) { 235 shareFolderIcon = OC.MimeType.getIconUrl('dir-public'); 236 } 237 else { 238 shareFolderIcon = OC.MimeType.getIconUrl('dir-shared'); 239 } 240 $tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')'); 241 $tr.attr('data-icon', shareFolderIcon); 242 } else if (type === 'dir') { 243 var mountType = $tr.attr('data-mounttype'); 244 // FIXME: duplicate of FileList._createRow logic for external folder, 245 // need to refactor the icon logic into a single code path eventually 246 if (mountType && mountType.indexOf('external') === 0) { 247 shareFolderIcon = OC.MimeType.getIconUrl('dir-external'); 248 $tr.attr('data-icon', shareFolderIcon); 249 } else { 250 shareFolderIcon = OC.MimeType.getIconUrl('dir'); 251 // back to default 252 $tr.removeAttr('data-icon'); 253 } 254 $tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')'); 255 } 256 // update share action text / icon 257 if (hasShares || owner) { 258 recipients = $tr.attr('data-share-recipients'); 259 action.addClass('shared-style'); 260 261 message = t('core', 'Shared'); 262 // even if reshared, only show "Shared by" 263 if (owner) { 264 message = this._formatRemoteShare(owner); 265 } 266 else if (recipients) { 267 message = t('core', 'Shared with {recipients}', {recipients: this._formatShareList(recipients.split(", ")).join(", ")}, 0, {escape: false}); 268 } 269 action.html('<span> ' + message + '</span>').prepend(icon); 270 if (owner || recipients) { 271 action.find('.remoteAddress').tipsy({gravity: 's'}); 272 } 273 } 274 else { 275 action.html('<span class="hidden-visually">' + t('core', 'Shared') + '</span>').prepend(icon); 276 } 277 if (hasLink) { 278 iconClass = 'icon-public'; 279 } 280 icon.removeClass('icon-shared icon-public').addClass(iconClass); 281 }, 282 /** 283 * 284 * @param itemType 285 * @param itemSource 286 * @param callback - optional. If a callback is given this method works 287 * asynchronous and the callback will be provided with data when the request 288 * is done. 289 * @returns {OC.Share.Types.ShareInfo} 290 */ 291 loadItem:function(itemType, itemSource, callback) { 292 var data = ''; 293 var checkReshare = true; 294 var async = !_.isUndefined(callback); 295 if (typeof OC.Share.statuses[itemSource] === 'undefined') { 296 // NOTE: Check does not always work and misses some shares, fix later 297 var checkShares = true; 298 } else { 299 var checkShares = true; 300 } 301 $.ajax({type: 'GET', url: OC.filePath('core', 'ajax', 'share.php'), data: { fetch: 'getItem', itemType: itemType, itemSource: itemSource, checkReshare: checkReshare, checkShares: checkShares }, async: async, success: function(result) { 302 if (result && result.status === 'success') { 303 data = result.data; 304 } else { 305 data = false;
306 } 307 if(async) { 308 callback(data); 309 } 310 }}); 311 312 return data; 313 }, 314 share:function(itemType, itemSource, shareType, shareWith, permissions, itemSourceName, expirationDate, callback, errorCallback) { 315 // Add a fallback for old share() calls without expirationDate. 316 // We should remove this in a later version, 317 // after the Apps have been updated. 318 if (typeof callback === 'undefined' && 319 typeof expirationDate === 'function') { 320 callback = expirationDate; 321 expirationDate = ''; 322 console.warn( 323 "Call to 'OC.Share.share()' with too few arguments. " + 324 "'expirationDate' was assumed to be 'callback'. " + 325 "Please revisit the call and fix the list of arguments." 326 ); 327 } 328 329 return $.post(OC.filePath('core', 'ajax', 'share.php'), 330 { 331 action: 'share', 332 itemType: itemType, 333 itemSource: itemSource, 334 shareType: shareType, 335 shareWith: shareWith, 336 permissions: permissions, 337 itemSourceName: itemSourceName, 338 expirationDate: expirationDate 339 }, function (result) { 340 if (result && result.status === 'success') { 341 if (callback) { 342 callback(result.data); 343 } 344 } else { 345 if (_.isUndefined(errorCallback)) { 346 var msg = t('core', 'Error'); 347 if (result.data && result.data.message) { 348 msg = result.data.message; 349 } 350 OC.dialogs.alert(msg, t('core', 'Error while sharing')); 351 } else { 352 errorCallback(result); 353 } 354 } 355 } 356 ); 357 }, 358 unshare:function(itemType, itemSource, shareType, shareWith, callback) { 359 $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'unshare', itemType: itemType, itemSource: itemSource, shareType: shareType, shareWith: shareWith }, function(result) { 360 if (result && result.status === 'success') { 361 if (callback) { 362 callback(); 363 } 364 } else { 365 OC.dialogs.alert(t('core', 'Error while unsharing'), t('core', 'Error')); 366 } 367 }); 368 }, 369 showDropDown:function(itemType, itemSource, appendTo, link, possiblePermissions, filename) { 370 var configModel = new OC.Share.ShareConfigModel(); 371 var attributes = {itemType: itemType, itemSource: itemSource, possiblePermissions: possiblePermissions}; 372 var itemModel = new OC.Share.ShareItemModel(attributes, {configModel: configModel}); 373 var dialogView = new OC.Share.ShareDialogView({ 374 id: 'dropdown', 375 model: itemModel, 376 configModel: configModel, 377 className: 'drop shareDropDown', 378 attributes: { 379 'data-item-source-name': filename, 380 'data-item-type': itemType, 381 'data-item-source': itemSource 382 } 383 }); 384 var $dialog = dialogView.render().$el; 385 $dialog.appendTo(appendTo); 386 $dialog.slideDown(OC.menuSpeed, function() { 387 OC.Share.droppedDown = true; 388 }); 389 itemModel.fetch(); 390 }, 391 hideDropDown:function(callback) { 392 OC.Share.currentShares = null; 393 $('#dropdown').slideUp(OC.menuSpeed, function() { 394 OC.Share.droppedDown = false; 395 $('#dropdown').remove(); 396 if (typeof FileActions !== 'undefined') { 397 $('tr').removeClass('mouseOver'); 398 } 399 if (callback) { 400 callback.call(); 401 } 402 }); 403 }, 404 dirname:function(path) { 405 return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, ''); 406 } 407}); 408 409$(document).ready(function() { 410 411 if(typeof monthNames != 'undefined'){ 412 // min date should always be the next day 413 var minDate = new Date(); 414 minDate.setDate(minDate.getDate()+1); 415 $.datepicker.setDefaults({ 416 monthNames: monthNames, 417 monthNamesShort: monthNamesShort, 418 dayNames: dayNames, 419 dayNamesMin: dayNamesMin, 420 dayNamesShort: dayNamesShort, 421 firstDay: firstDay, 422 minDate : minDate 423 }); 424 } 425 426 $(this).click(function(event) { 427 var target = $(event.target); 428 var isMatched = !target.is('.drop, .ui-datepicker-next, .ui-datepicker-prev, .ui-icon') 429 && !target.closest('#ui-datepicker-div').length && !target.closest('.ui-autocomplete').length; 430 if (OC.Share && OC.Share.droppedDown && isMatched && $('#dropdown').has(event.target).length === 0) { 431 OC.Share.hideDropDown(); 432 } 433 }); 434 435});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.