зеркало из https://github.com/mozilla/FlightDeck.git
cleaning, coping with adding and removing attachments in same request, auto show attachments, fix syntax highlighting (bug 612293 bug 618500)
This commit is contained in:
Родитель
013e48c328
Коммит
f2cc3835bb
|
@ -1,6 +1,6 @@
|
|||
from django.db.models.manager import Manager
|
||||
from django.shortcuts import _get_queryset
|
||||
from django.http import Http404
|
||||
from django.http import Http404, HttpRequest
|
||||
|
||||
|
||||
def get_object_or_create(klass, *args, **kwargs):
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
var FDBespin = new Class({
|
||||
Implements: [Options, Events],
|
||||
options: {
|
||||
validSyntaxes: ['js', 'html', 'plain']
|
||||
validSyntaxes: ['js', 'html', 'plain', 'css']
|
||||
},
|
||||
initialize: function(element, options) {
|
||||
var self = this;
|
||||
|
@ -43,15 +43,10 @@ var FDBespin = new Class({
|
|||
},
|
||||
setSyntax: function(syntax) {
|
||||
if (!this.options.validSyntaxes.contains(syntax)) {
|
||||
if (syntax == 'css') {
|
||||
syntax = 'html'
|
||||
if (syntax == 'json') {
|
||||
syntax = 'js'
|
||||
} else {
|
||||
if (syntax == 'json') {
|
||||
syntax = 'js'
|
||||
|
||||
} else {
|
||||
syntax = 'plain'
|
||||
}
|
||||
syntax = 'plain'
|
||||
}
|
||||
}
|
||||
// XXX Switched off as incompatible with MooTools
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Extending Flightdeck with Editor functionality
|
||||
* Extending Flightdeck with Editor functionality
|
||||
*/
|
||||
|
||||
FlightDeck = Class.refactor(FlightDeck,{
|
||||
|
@ -59,7 +59,7 @@ FlightDeck = Class.refactor(FlightDeck,{
|
|||
} else {
|
||||
assignToEl(selector);
|
||||
}
|
||||
$('modules').addEvent('click:relay(.{file_listing_class} li a)'.substitute(this.options),
|
||||
$('modules').addEvent('click:relay(.{file_listing_class} li a)'.substitute(this.options),
|
||||
function(e, el) {
|
||||
var li = $(el).getParent('li');
|
||||
// assign switch_mode_on to newly created modules
|
||||
|
@ -87,5 +87,5 @@ FlightDeck = Class.refactor(FlightDeck,{
|
|||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
|
|
@ -31,14 +31,17 @@ var Package = new Class({
|
|||
// origin_url: '', // link to a revision used to created this one
|
||||
// revision_author: '',
|
||||
// modules: [], // a list of module filename, author pairs
|
||||
attachments: [],
|
||||
readonly: false,
|
||||
package_info_el: 'package-info',
|
||||
test_el: 'try_in_browser'
|
||||
},
|
||||
modules: {},
|
||||
attachments: {},
|
||||
initialize: function(options) {
|
||||
this.setOptions(options);
|
||||
this.instantiate_modules();
|
||||
this.instantiate_attachments();
|
||||
$('revisions_list').addEvent('click', this.show_revision_list);
|
||||
|
||||
// testing
|
||||
|
@ -47,30 +50,6 @@ var Package = new Class({
|
|||
this.test_url = $(this.options.test_el).get('href');
|
||||
$(this.options.test_el).addEvent('click', this.boundTestAddon)
|
||||
}
|
||||
if ($('attachments')) $('attachments').addEvent(
|
||||
'click:relay(.UI_File_Listing a)',
|
||||
function(e, target) {
|
||||
e.stop();
|
||||
var url = target.get('href');
|
||||
var ext = target.get('rel');
|
||||
var filename = target.get('text').escapeAll();
|
||||
var template_start = '<div id="attachment_view"><h3>'+filename+'</h3><div class="UI_Modal_Section">';
|
||||
var template_end = '</div><div class="UI_Modal_Actions"><ul><li><input type="reset" value="Close" class="closeModal"/></li></ul></div></div>';
|
||||
var template_middle = 'Download <a href="'+url+'">'+filename+'</a>';
|
||||
if (['jpg', 'gif', 'png'].contains(ext)) template_middle = '<img src="'+url+'"/>';
|
||||
if (['css', 'js', 'txt'].contains(ext)) {
|
||||
new Request({
|
||||
url: url,
|
||||
onSuccess: function(response) {
|
||||
template_middle = '<pre>'+response.escapeAll()+'</pre>';
|
||||
this.attachmentWindow = fd.displayModal(template_start+template_middle+template_end);
|
||||
}
|
||||
}).send();
|
||||
} else {
|
||||
this.attachmentWindow = fd.displayModal(template_start+template_middle+template_end);
|
||||
}
|
||||
}.bind(this)
|
||||
)
|
||||
},
|
||||
testAddon: function(e){
|
||||
var el;
|
||||
|
@ -89,12 +68,12 @@ var Package = new Class({
|
|||
} else {
|
||||
fd.whenAddonInstalled(function() {
|
||||
fd.message.alert(
|
||||
'Add-on Builder Helper',
|
||||
'Add-on Builder Helper',
|
||||
'Now that you have installed the Add-ons Builder Helper, loading the add-on into your browser for testing...'
|
||||
);
|
||||
this.testAddon();
|
||||
}.bind(this));
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
installAddon: function() {
|
||||
|
@ -121,6 +100,13 @@ var Package = new Class({
|
|||
this.modules[module.filename] = new Module(this,module);
|
||||
}, this);
|
||||
},
|
||||
instantiate_attachments: function() {
|
||||
// iterate through attachments
|
||||
this.options.attachments.each(function(attachment) {
|
||||
attachment.readonly = this.options.readonly;
|
||||
this.attachments[attachment.uid] = new Attachment(this,attachment);
|
||||
}, this);
|
||||
},
|
||||
show_revision_list: function(e) {
|
||||
if (e) e.stop();
|
||||
new Request({
|
||||
|
@ -132,8 +118,164 @@ var Package = new Class({
|
|||
}
|
||||
});
|
||||
|
||||
var File = Class({
|
||||
destroy: function() {
|
||||
// refactor me
|
||||
if (this.textarea) this.textarea.destroy();
|
||||
this.trigger.getParent('li').destroy();
|
||||
$('attachments-counter').set('text', '('+ $(this.options.counter).getElements('.UI_File_Listing li').length +')')
|
||||
delete fd.getItem().modules[this.options.filename];
|
||||
delete fd.editor_contents[this.get_editor_id()];
|
||||
if (this.active) {
|
||||
// switch editor!
|
||||
mod = null;
|
||||
// try to switch to first element
|
||||
first = false;
|
||||
$each(fd.getItem().modules, function(mod) {
|
||||
if (!first) {
|
||||
first = true;
|
||||
mod.switchBespin();
|
||||
mod.trigger.getParent('li').switch_mode_on();
|
||||
}
|
||||
});
|
||||
if (!first) {
|
||||
fd.cleanBespin();
|
||||
}
|
||||
}
|
||||
},
|
||||
switchBespin: function() {
|
||||
if (!fd.editor_contents[this.get_editor_id()]) {
|
||||
this.loadCode();
|
||||
}
|
||||
fd.switchBespinEditor(this.get_editor_id(), this.options.type);
|
||||
if (fd.getItem()) {
|
||||
$each(fd.getItem().modules, function(mod) {
|
||||
mod.active = false;
|
||||
});
|
||||
}
|
||||
this.active = true;
|
||||
},
|
||||
get_editor_id: function() {
|
||||
if (!this._editor_id)
|
||||
this._editor_id = this.get_css_id() + this.options.code_editor_suffix;
|
||||
return this._editor_id;
|
||||
},
|
||||
get_trigger_id: function() {
|
||||
if (!this._trigger_id)
|
||||
this._trigger_id = this.get_css_id() + this.options.code_trigger_suffix;
|
||||
return this._trigger_id;
|
||||
},
|
||||
});
|
||||
|
||||
var Attachment = new Class({
|
||||
Extends: File,
|
||||
Implements: [Options, Events],
|
||||
options: {
|
||||
code_trigger_suffix: '_attachment_switch', // id of an element which is used to switch editors
|
||||
code_editor_suffix: '_attachment_textarea', // id of the textarea
|
||||
active: false,
|
||||
type: 'js',
|
||||
append: false,
|
||||
readonly: false,
|
||||
counter: 'attachments'
|
||||
},
|
||||
is_editable: function() {
|
||||
return ['css', 'txt', 'js', 'html'].contains(this.options.type);
|
||||
},
|
||||
initialize: function(pack, options) {
|
||||
this.setOptions(options);
|
||||
this.pack = pack;
|
||||
if (this.options.append) {
|
||||
this.append();
|
||||
}
|
||||
|
||||
// connect trigger with editor
|
||||
if ($(this.get_trigger_id())) {
|
||||
this.trigger = $(this.get_trigger_id());
|
||||
this.trigger.store('Attachment', this);
|
||||
this.editor = new FDEditor({
|
||||
element: this.get_editor_id(),
|
||||
activate: this.options.main || this.options.executable,
|
||||
type: this.options.type,
|
||||
readonly: this.options.readonly
|
||||
});
|
||||
// connect trigger
|
||||
this.trigger.addEvent('click', function(e) {
|
||||
if (e) e.preventDefault();
|
||||
if (this.is_editable()) {
|
||||
this.switchBespin();
|
||||
this.highlightMenu();
|
||||
} else {
|
||||
var target = e.target;
|
||||
var url = target.get('href');
|
||||
var ext = target.get('rel');
|
||||
var filename = target.get('text').escapeAll();
|
||||
var template_start = '<div id="attachment_view"><h3>'+filename+'</h3><div class="UI_Modal_Section">';
|
||||
var template_end = '</div><div class="UI_Modal_Actions"><ul><li><input type="reset" value="Close" class="closeModal"/></li></ul></div></div>';
|
||||
var template_middle = 'Download <a href="'+url+'">'+filename+'</a>';
|
||||
if (['jpg', 'gif', 'png'].contains(ext)) template_middle += '<p><img src="'+url+'"/></p>';
|
||||
this.attachmentWindow = fd.displayModal(template_start+template_middle+template_end);
|
||||
}
|
||||
}.bind(this));
|
||||
if (this.options.active && this.is_editable()) {
|
||||
this.switchBespin();
|
||||
this.highlightMenu();
|
||||
}
|
||||
if (!this.options.readonly) {
|
||||
// here special functionality for edit page
|
||||
var rm_mod_trigger = this.trigger.getElement('span.File_close');
|
||||
if (rm_mod_trigger) {
|
||||
rm_mod_trigger.addEvent('click', function(e) {
|
||||
this.pack.removeAttachmentAction(e);
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
highlightMenu: function() {
|
||||
var li = this.trigger.getParent('li')
|
||||
fd.assignModeSwitch(li);
|
||||
li.switch_mode_on();
|
||||
},
|
||||
loadCode: function() {
|
||||
// load data synchronously
|
||||
new Request({
|
||||
url: this.options.get_url,
|
||||
async: false,
|
||||
useSpinner: true,
|
||||
spinnerTarget: 'editor-wrapper',
|
||||
onSuccess: function(text, html) {
|
||||
fd.editor_contents[this.get_editor_id()] = text;
|
||||
}.bind(this)
|
||||
}).send();
|
||||
},
|
||||
get_css_id: function() {
|
||||
return this.options.uid;
|
||||
},
|
||||
append: function() {
|
||||
var html = '<a title="" href="'+ this.options.get_url + '" class="Module_file" id="' + this.get_trigger_id() + '">'+
|
||||
'{filename}.{ext}<span class="File_status"></span>'+
|
||||
'<span class="File_close"></span>'+
|
||||
'</a>';
|
||||
var li = new Element('li',{
|
||||
'class': 'UI_File_normal',
|
||||
'html': html.substitute(this.options)
|
||||
}).inject($('add_attachment_div').getPrevious('ul'));
|
||||
$('attachments-counter').set('text', '('+ $('attachments').getElements('.UI_File_Listing li').length +')')
|
||||
|
||||
if (this.is_editable()) {
|
||||
var textarea = new Element('textarea', {
|
||||
'id': this.get_editor_id(),
|
||||
'class': 'UI_Editor_Area',
|
||||
'name': this.get_editor_id(),
|
||||
'html': ''
|
||||
}).inject('editor-wrapper');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var Module = new Class({
|
||||
Extends: File,
|
||||
Implements: [Options, Events],
|
||||
options: {
|
||||
// data
|
||||
|
@ -148,7 +290,8 @@ var Module = new Class({
|
|||
executable: false,
|
||||
active: false,
|
||||
type: 'js',
|
||||
append: false
|
||||
append: false,
|
||||
counter: 'modules'
|
||||
},
|
||||
initialize: function(pack, options) {
|
||||
this.setOptions(options);
|
||||
|
@ -173,80 +316,35 @@ var Module = new Class({
|
|||
}.bind(this));
|
||||
if (this.options.main || this.options.executable) {
|
||||
this.trigger.getParent('li').switch_mode_on();
|
||||
}
|
||||
}
|
||||
if (this.options.active) {
|
||||
this.switchBespin();
|
||||
var li = this.trigger.getParent('li')
|
||||
fd.assignModeSwitch(li);
|
||||
li.switch_mode_on();
|
||||
}
|
||||
if (!this.options.readonly) {
|
||||
// here special functionality for edit page
|
||||
var rm_mod_trigger = this.trigger.getElement('span.File_close');
|
||||
if (rm_mod_trigger) {
|
||||
rm_mod_trigger.addEvent('click', function(e) {
|
||||
this.pack.removeModuleAction(e);
|
||||
}.bind(this));
|
||||
}
|
||||
if (!this.options.readonly) {
|
||||
// here special functionality for edit page
|
||||
var rm_mod_trigger = this.trigger.getElement('span.File_close');
|
||||
if (rm_mod_trigger) {
|
||||
rm_mod_trigger.addEvent('click', function(e) {
|
||||
this.pack.removeModuleAction(e);
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
loadCode: function() {
|
||||
// load data synchronously
|
||||
new Request.JSON({
|
||||
url: this.options.get_url,
|
||||
async: false,
|
||||
useSpinner: true,
|
||||
spinnerTarget: 'editor-wrapper',
|
||||
onSuccess: function(mod) {
|
||||
fd.editor_contents[this.get_editor_id()] = mod.code;
|
||||
}.bind(this)
|
||||
}).send();
|
||||
},
|
||||
switchBespin: function() {
|
||||
if (!fd.editor_contents[this.get_editor_id()]) {
|
||||
this.loadCode();
|
||||
}
|
||||
fd.switchBespinEditor(this.get_editor_id(), this.options.type);
|
||||
if (fd.getItem()) {
|
||||
$each(fd.getItem().modules, function(mod) {
|
||||
mod.active = false;
|
||||
});
|
||||
}
|
||||
this.active = true;
|
||||
},
|
||||
destroy: function() {
|
||||
if (this.textarea) this.textarea.destroy();
|
||||
this.trigger.getParent('li').destroy();
|
||||
$('modules-counter').set('text', '('+ $('modules').getElements('.UI_File_Listing li').length +')')
|
||||
delete fd.getItem().modules[this.options.filename];
|
||||
delete fd.editor_contents[this.get_editor_id()];
|
||||
if (this.active) {
|
||||
// switch editor!
|
||||
mod = null;
|
||||
// try to switch to first element
|
||||
first = false;
|
||||
$each(fd.getItem().modules, function(mod) {
|
||||
if (!first) {
|
||||
first = true;
|
||||
mod.switchBespin();
|
||||
mod.trigger.getParent('li').switch_mode_on();
|
||||
}
|
||||
});
|
||||
if (!first) {
|
||||
fd.cleanBespin();
|
||||
}
|
||||
}
|
||||
},
|
||||
get_editor_id: function() {
|
||||
if (!this._editor_id)
|
||||
this._editor_id = this.options.filename + this.options.code_editor_suffix;
|
||||
return this._editor_id;
|
||||
},
|
||||
get_trigger_id: function() {
|
||||
if (!this._trigger_id)
|
||||
this._trigger_id = this.options.filename + this.options.code_trigger_suffix;
|
||||
return this._trigger_id;
|
||||
loadCode: function() {
|
||||
// load data synchronously
|
||||
new Request.JSON({
|
||||
url: this.options.get_url,
|
||||
async: false,
|
||||
useSpinner: true,
|
||||
spinnerTarget: 'editor-wrapper',
|
||||
onSuccess: function(mod) {
|
||||
fd.editor_contents[this.get_editor_id()] = mod.code;
|
||||
}.bind(this)
|
||||
}).send();
|
||||
},
|
||||
append: function() {
|
||||
var html = '<a title="" href="#" class="Module_file" id="{filename}_switch">'+
|
||||
|
@ -258,16 +356,19 @@ var Module = new Class({
|
|||
'html': html.substitute(this.options)
|
||||
}).inject($('add_module_div').getPrevious('ul'));
|
||||
$('modules-counter').set('text', '('+ $('modules').getElements('.UI_File_Listing li').length +')')
|
||||
|
||||
|
||||
var textarea = new Element('textarea', {
|
||||
'id': this.options.filename + '_textarea',
|
||||
'class': 'UI_Editor_Area',
|
||||
'name': this.options.filename + '_textarea',
|
||||
'html': this.options.code
|
||||
}).inject('editor-wrapper');
|
||||
}
|
||||
})
|
||||
},
|
||||
get_css_id: function() {
|
||||
return this.options.filename;
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
Package.View = new Class({
|
||||
Extends: Package,
|
||||
|
@ -353,51 +454,48 @@ Package.Edit = new Class({
|
|||
// assign menu items
|
||||
this.appSidebarValidator = new Form.Validator.Inline('app-sidebar-form');
|
||||
$(this.options.package_info_el).addEvent('click', this.editInfo.bind(this));
|
||||
|
||||
|
||||
// save
|
||||
this.boundSaveAction = this.saveAction.bind(this);
|
||||
$(this.options.save_el).addEvent('click', this.boundSaveAction);
|
||||
|
||||
|
||||
// submit Info
|
||||
this.boundSubmitInfo = this.submitInfo.bind(this);
|
||||
|
||||
|
||||
// add/remove module
|
||||
this.boundAddModuleAction = this.addModuleAction.bind(this);
|
||||
this.boundRemoveModuleAction = this.removeModuleAction.bind(this);
|
||||
$(this.options.add_module_el).addEvent('click',
|
||||
$(this.options.add_module_el).addEvent('click',
|
||||
this.boundAddModuleAction);
|
||||
|
||||
|
||||
// assign/remove library
|
||||
this.boundAssignLibraryAction = this.assignLibraryAction.bind(this);
|
||||
this.boundRemoveLibraryAction = this.removeLibraryAction.bind(this);
|
||||
$(this.options.assign_library_el).addEvent('click',
|
||||
this.boundAssignLibraryAction);
|
||||
$$('#libraries .UI_File_Listing .File_close').each(function(close) {
|
||||
$$('#libraries .UI_File_Listing .File_close').each(function(close) {
|
||||
close.addEvent('click', this.boundRemoveLibraryAction);
|
||||
},this);
|
||||
|
||||
|
||||
// add attachments
|
||||
this.add_attachment_el = $('add_attachment');
|
||||
this.attachment_template = '<a title="" rel="{ext}" href="{display_url}" class="Module_file" id="{filename}{ext}_display">'+
|
||||
'{basename}<span class="File_close"></span>'+
|
||||
'</a>';
|
||||
this.add_attachment_el.addEvent('change', this.sendMultipleFiles.bind(this));
|
||||
this.boundRemoveAttachmentAction = this.removeAttachmentAction.bind(this);
|
||||
$$('#attachments .UI_File_Listing .File_close').each(function(close) {
|
||||
$$('#attachments .UI_File_Listing .File_close').each(function(close) {
|
||||
close.addEvent('click', this.boundRemoveAttachmentAction);
|
||||
},this);
|
||||
this.attachments_counter = $('attachments-counter');
|
||||
|
||||
|
||||
var fakeFileInput = $('add_attachment_fake'), fakeFileSubmit = $('add_attachment_action_fake');
|
||||
this.add_attachment_el.addEvents({
|
||||
change: function(){
|
||||
fakeFileInput.set('value', this.get('value'));
|
||||
},
|
||||
|
||||
|
||||
mouseover: function(){
|
||||
fakeFileSubmit.addClass('hover');
|
||||
},
|
||||
|
||||
|
||||
mouseout: function(){
|
||||
fakeFileSubmit.removeClass('hover');
|
||||
}
|
||||
|
@ -415,7 +513,7 @@ Package.Edit = new Class({
|
|||
// change url to the SDK lib code
|
||||
$('core_library_lib').getElement('a').set(
|
||||
'href', response.lib_url);
|
||||
// change name of the SDK lib
|
||||
// change name of the SDK lib
|
||||
$('core_library_lib').getElement('span').set(
|
||||
'text', response.lib_name);
|
||||
fd.message.alert(response.message_title, response.message);
|
||||
|
@ -431,56 +529,60 @@ Package.Edit = new Class({
|
|||
|
||||
sendMultipleFiles: function() {
|
||||
self = this;
|
||||
self.spinner = false;
|
||||
self.spinner = false;
|
||||
sendMultipleFiles({
|
||||
url: this.get_add_attachment_url.bind(this),
|
||||
|
||||
|
||||
// list of files to upload
|
||||
files: this.add_attachment_el.files,
|
||||
|
||||
|
||||
// clear the container
|
||||
onloadstart:function(){
|
||||
if (self.spinner) {
|
||||
self.spinner.position();
|
||||
} else {
|
||||
self.spinner = new Spinner($('attachments')).show();
|
||||
}
|
||||
if (self.spinner) {
|
||||
self.spinner.position();
|
||||
} else {
|
||||
self.spinner = new Spinner($('attachments')).show();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// do something during upload ...
|
||||
//onprogress:function(rpe){
|
||||
// $log('progress');
|
||||
//},
|
||||
|
||||
onpartialload: function(rpe, xhr) {
|
||||
$log('FD: file uploaded');
|
||||
// here parse xhr.responseText and append a DOM Element
|
||||
$log('FD: attachment uploaded');
|
||||
response = JSON.parse(xhr.responseText);
|
||||
new Element('li',{
|
||||
'class': 'UI_File_Normal',
|
||||
'html': self.attachment_template.substitute(response)
|
||||
}).inject($('attachments_ul'));
|
||||
$(response.filename+response.ext+'_display').getElement('.File_close').addEvent('click', self.boundRemoveAttachmentAction);
|
||||
fd.setURIRedirect(response.view_url);
|
||||
self.setUrls(response);
|
||||
self.attachments_counter.set('text', '('+ $('attachments').getElements('.UI_File_Listing li').length +')')
|
||||
fd.message.alert(response.message_title, response.message);
|
||||
var attachment = new Attachment(self,{
|
||||
append: true,
|
||||
active: true,
|
||||
filename: response.filename,
|
||||
ext: response.ext,
|
||||
author: response.author,
|
||||
code: response.code,
|
||||
get_url: response.get_url,
|
||||
uid: response.uid,
|
||||
type: response.ext
|
||||
});
|
||||
self.attachments[response.uid] = attachment;
|
||||
},
|
||||
|
||||
|
||||
// fired when last file has been uploaded
|
||||
onload:function(rpe, xhr){
|
||||
if (self.spinner) self.spinner.destroy();
|
||||
if (self.spinner) self.spinner.destroy();
|
||||
$log('FD: all files uploaded');
|
||||
$(self.add_attachment_el).set('value','');
|
||||
$('add_attachment_fake').set('value','')
|
||||
},
|
||||
|
||||
|
||||
// if something is wrong ... (from native instance or because of size)
|
||||
onerror:function(){
|
||||
if (self.spinner) self.spinner.destroy();
|
||||
if (self.spinner) self.spinner.destroy();
|
||||
fd.error.alert(
|
||||
'Error {status}'.substitute(xhr),
|
||||
'Error {status}'.substitute(xhr),
|
||||
'{statusText}<br/>{responseText}'.substitute(xhr)
|
||||
);
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -507,8 +609,8 @@ Package.Edit = new Class({
|
|||
onSuccess: function(response) {
|
||||
fd.setURIRedirect(response.view_url);
|
||||
self.setUrls(response);
|
||||
$(response.filename+response.ext+'_display').getParent('li').destroy();
|
||||
self.attachments_counter.set('text', '('+ $('attachments').getElements('.UI_File_Listing li').length +')')
|
||||
var attachment = self.attachments[response.uid];
|
||||
attachment.destroy();
|
||||
}
|
||||
}).send();
|
||||
},
|
||||
|
@ -545,7 +647,7 @@ Package.Edit = new Class({
|
|||
filename: response.filename,
|
||||
author: response.author,
|
||||
code: response.code,
|
||||
get_url: response.get_url
|
||||
get_url: response.get_url
|
||||
});
|
||||
this.modules[response.filename] = mod;
|
||||
}.bind(this)
|
||||
|
@ -619,7 +721,7 @@ Package.Edit = new Class({
|
|||
'class': 'UI_File_Normal',
|
||||
'html': html.substitute(lib)
|
||||
}).inject($('assign_library_div').getPrevious('ul'));
|
||||
$$('#library_{library_name} .File_close'.substitute(lib)).each(function(close) {
|
||||
$$('#library_{library_name} .File_close'.substitute(lib)).each(function(close) {
|
||||
close.addEvent('click', this.boundRemoveLibraryAction);
|
||||
},this);
|
||||
$('libraries-counter').set('text', '('+ $('libraries').getElements('.UI_File_Listing li').length +')')
|
||||
|
@ -661,7 +763,7 @@ Package.Edit = new Class({
|
|||
this.savenow = false;
|
||||
fd.editPackageInfoModal = fd.displayModal(settings.edit_package_info_template.substitute(this.data || this.options));
|
||||
$('package-info_form').addEvent('submit', this.boundSubmitInfo);
|
||||
|
||||
|
||||
// XXX: this will change after moving the content to other forms
|
||||
$('version_name').addEvent('change', function() { fd.fireEvent('change'); });
|
||||
$('full_name').addEvent('change', function() { fd.fireEvent('change'); });
|
||||
|
@ -709,6 +811,9 @@ Package.Edit = new Class({
|
|||
$each(this.modules, function(module, filename) {
|
||||
this.data[filename] = fd.editor_contents[filename + module.options.code_editor_suffix]
|
||||
}, this);
|
||||
$each(this.attachments, function(attachment) {
|
||||
this.data[attachment.options.uid] = fd.editor_contents[attachment.options.uid + attachment.options.code_editor_suffix]
|
||||
}, this);
|
||||
},
|
||||
testAddon: function(e){
|
||||
this.collectData();
|
||||
|
@ -745,7 +850,7 @@ Package.Edit = new Class({
|
|||
if ($(this.options.test_el).getParent('li').hasClass('pressed')) {
|
||||
// only one add-on of the same id should be allowed on the Helper side
|
||||
this.installAddon();
|
||||
}
|
||||
}
|
||||
fd.fireEvent('save');
|
||||
}.bind(this),
|
||||
onFailure: function() {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import os
|
||||
import csv
|
||||
import shutil
|
||||
import time
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
|
@ -201,7 +202,6 @@ class Package(models.Model):
|
|||
i = i + 1
|
||||
return _get_full_name(full_name, username, type_id, i)
|
||||
|
||||
|
||||
name = settings.DEFAULT_PACKAGE_FULLNAME.get(self.type,
|
||||
self.author.username)
|
||||
self.full_name = _get_full_name(name, self.author.username, self.type)
|
||||
|
@ -557,7 +557,7 @@ class PackageRevision(models.Model):
|
|||
returns False if the package revision contains a module with given
|
||||
filename
|
||||
"""
|
||||
if self.modules.filter(filename=filename).count() > 0:
|
||||
if self.modules.filter(filename=filename).count():
|
||||
return False
|
||||
return True
|
||||
|
||||
|
@ -566,7 +566,7 @@ class PackageRevision(models.Model):
|
|||
returns False if the package revision contains a module with given
|
||||
filename
|
||||
"""
|
||||
if self.attachments.filter(filename=filename, ext=ext).count() > 0:
|
||||
if self.attachments.filter(filename=filename, ext=ext).count():
|
||||
return False
|
||||
return True
|
||||
|
||||
|
@ -611,40 +611,44 @@ class PackageRevision(models.Model):
|
|||
self.save()
|
||||
return self.modules.remove(mod)
|
||||
|
||||
def module_update(self, mod, save=True):
|
||||
def update(self, change, save=True):
|
||||
" to update a module, new package revision has to be created "
|
||||
if save:
|
||||
self.save()
|
||||
self.modules.remove(mod)
|
||||
mod.id = None
|
||||
mod.save()
|
||||
self.modules.add(mod)
|
||||
|
||||
def modules_update(self, modules):
|
||||
" update more than one module "
|
||||
change.increment(self)
|
||||
|
||||
def updates(self, changes):
|
||||
"""Changes from the server."""
|
||||
self.save()
|
||||
for mod in modules:
|
||||
self.module_update(mod, False)
|
||||
for change in changes:
|
||||
self.update(change, False)
|
||||
|
||||
def attachment_create(self, **kwargs):
|
||||
def attachment_create(self, author, filename):
|
||||
" create attachment and add to attachments "
|
||||
# validate if given filename is valid
|
||||
if not self.validate_attachment_filename(kwargs['filename'],
|
||||
kwargs['ext']):
|
||||
filename, ext = os.path.splitext(filename)
|
||||
ext = ext.split('.')[1].lower() if ext else ''
|
||||
|
||||
if not self.validate_attachment_filename(filename, ext):
|
||||
raise FilenameExistException(
|
||||
('Sorry, there is already an attachment in your add-on with '
|
||||
'the name "%s.%s". Each attachment in your add-on needs to '
|
||||
'have a unique name.') % (kwargs['filename'], kwargs['ext'])
|
||||
'have a unique name.') % (filename, ext)
|
||||
)
|
||||
att = Attachment.objects.create(**kwargs)
|
||||
self.attachment_add(att)
|
||||
|
||||
att = Attachment.objects.create(filename=filename,
|
||||
ext=ext, author=author)
|
||||
|
||||
att.save()
|
||||
self.attachment_add(att, check=False)
|
||||
return att
|
||||
|
||||
def attachment_add(self, att):
|
||||
def attachment_add(self, att, check=True):
|
||||
" copy to new revision, add attachment "
|
||||
# save as new version
|
||||
# validate if given filename is valid
|
||||
if not self.validate_attachment_filename(att.filename, att.ext):
|
||||
if (check and
|
||||
not self.validate_attachment_filename(att.filename, att.ext)):
|
||||
raise FilenameExistException(
|
||||
'Attachment with filename %s.%s already exists' % (
|
||||
att.filename, att.ext)
|
||||
|
@ -734,6 +738,18 @@ class PackageRevision(models.Model):
|
|||
] if self.modules.count() > 0 else []
|
||||
return simplejson.dumps(m_list)
|
||||
|
||||
def get_attachments_list_json(self):
|
||||
" returns attachments list as JSON object "
|
||||
a_list = [{
|
||||
'uid': a.get_uid,
|
||||
'filename': a.filename,
|
||||
'author': a.author.username,
|
||||
'type': a.ext,
|
||||
'get_url': reverse('jp_attachment', args=[a.get_uid])
|
||||
} for a in self.attachments.all()
|
||||
] if self.attachments.count() > 0 else []
|
||||
return simplejson.dumps(a_list)
|
||||
|
||||
def get_sdk_name(self):
|
||||
" returns the name of the directory to which SDK should be copied "
|
||||
return '%s-%s-%s' % (self.sdk.version,
|
||||
|
@ -922,6 +938,12 @@ class Module(models.Model):
|
|||
'code': self.code,
|
||||
'author': self.author.username})
|
||||
|
||||
def increment(self, revision):
|
||||
revision.modules.remove(self)
|
||||
self.pk = None
|
||||
self.save()
|
||||
revision.modules.add(self)
|
||||
|
||||
|
||||
class Attachment(models.Model):
|
||||
"""
|
||||
|
@ -948,6 +970,16 @@ class Attachment(models.Model):
|
|||
" attachment ordering "
|
||||
ordering = ('filename',)
|
||||
|
||||
@property
|
||||
def get_uid(self):
|
||||
"""A uid that contains, filename and extension and is suitable
|
||||
for use in css selectors (eg: no spaces)."""
|
||||
return str(int(self.pk))
|
||||
|
||||
@property
|
||||
def is_editable(self):
|
||||
return self.ext in ["html", "css", "js", "txt"]
|
||||
|
||||
def get_filename(self):
|
||||
" returns human readable filename with extension "
|
||||
name = self.filename
|
||||
|
@ -955,21 +987,52 @@ class Attachment(models.Model):
|
|||
name = "%s.%s" % (name, self.ext)
|
||||
return name
|
||||
|
||||
def save(self, **kwargs):
|
||||
" overloaded to prevent from updating "
|
||||
if self.id:
|
||||
raise UpdateDeniedException(
|
||||
'Attachment can not be updated in the same row')
|
||||
return super(Attachment, self).save(**kwargs)
|
||||
def get_display_url(self):
|
||||
"""Returns URL to display the attachment."""
|
||||
return reverse('jp_attachment', args=[self.get_uid])
|
||||
|
||||
def create_path(self):
|
||||
args = (self.pk, self.filename, self.ext)
|
||||
self.path = os.path.join(time.strftime('%Y/%m/%d'), '%s-%s.%s' % args)
|
||||
|
||||
def get_file_path(self):
|
||||
if self.path:
|
||||
return os.path.join(settings.UPLOAD_DIR, self.path)
|
||||
raise ValueError("self.path not set.")
|
||||
|
||||
def read(self):
|
||||
"""Reads the file, if it doesn't exist return empty."""
|
||||
if self.path and os.path.exists(self.get_file_path()):
|
||||
return open(self.get_file_path(), 'rb').read()
|
||||
return ""
|
||||
|
||||
def changed(self):
|
||||
return not self.read() == self.data
|
||||
|
||||
def write(self):
|
||||
"""Writes the file."""
|
||||
self.create_path()
|
||||
self.save()
|
||||
|
||||
directory = os.path.dirname(self.get_file_path())
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
handle = open(self.get_file_path(), 'wb')
|
||||
handle.write(self.data)
|
||||
handle.close()
|
||||
|
||||
def export_file(self, static_dir):
|
||||
" copies from uploads to the package's data directory "
|
||||
shutil.copy('%s/%s' % (settings.UPLOAD_DIR, self.path),
|
||||
'%s/%s.%s' % (static_dir, self.filename, self.ext))
|
||||
|
||||
def get_display_url(self):
|
||||
" returns URL to display the attachment "
|
||||
return reverse('jp_attachment', args=[self.path])
|
||||
def increment(self, revision):
|
||||
revision.attachments.remove(self)
|
||||
self.pk = None
|
||||
self.write()
|
||||
self.save()
|
||||
revision.attachments.add(self)
|
||||
|
||||
|
||||
class SDK(models.Model):
|
||||
|
|
|
@ -10,6 +10,7 @@ def get_package_revision(id_name, type_id,
|
|||
"""
|
||||
Return revision of the package
|
||||
"""
|
||||
|
||||
if not (revision_number or version_name):
|
||||
# get default revision - one linked via Package:version
|
||||
package = get_object_with_related_or_404(Package, id_number=id_name,
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{% if attachment.is_editable %}
|
||||
<textarea id="{{ attachment.get_uid }}_attachment_textarea" class="UI_Editor_Area" name="{{ attachment.get_uid }}_attachment_textarea">{{ attachment.read }}</textarea>
|
||||
{% endif %}
|
|
@ -1,2 +1 @@
|
|||
<textarea {% if readonly %}readonly="readonly" {% endif %}id="{{ module.filename }}_textarea" class="UI_Editor_Area" name="{{ module.filename }}_textarea">{{ module.code }}</textarea>
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
{% block core_library %}
|
||||
{% if revision.sdk %}
|
||||
<li class="UI_File_Normal Core_library" id="core_library_lib">
|
||||
<a title="" href="{{ revision.get_sdk_revision.get_absolute_url }}"
|
||||
target="{{ revision.get_sdk_revision.package.name }}"
|
||||
<a title="" href="{{ revision.get_sdk_revision.get_absolute_url }}"
|
||||
target="{{ revision.get_sdk_revision.package.name }}"
|
||||
class="library_link">
|
||||
<span>{{ revision.get_sdk_revision.package.full_name }}</span>
|
||||
</a>
|
||||
|
@ -56,5 +56,7 @@
|
|||
{% for module in revision.modules.all %}{% if module.filename != revision.module_main %}
|
||||
{% include "_module_code_textarea.html" %}
|
||||
{% endif %}{% endfor %}
|
||||
{% for attachment in revision.attachments.all %}
|
||||
{% include "_attachment_code_textarea.html" %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
<ul class="UI_File_Listing" id='attachments_ul'>
|
||||
{% for attachment in attachments %}
|
||||
<li class="UI_File_Normal">
|
||||
<a title="" href="{{ attachment.get_display_url }}" rel="{{ attachment.ext }}" class="Module_file" id="{{ attachment.filename }}{{ attachment.ext }}_display">{{ attachment.get_filename }}<span class="File_close"></span></a>
|
||||
<a title="" href="{{ attachment.get_display_url }}" rel="{{ attachment.ext }}" class="Module_file" id="{{ attachment.get_uid }}_attachment_switch">{{ attachment.get_filename }}<span class="File_close"></span></a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -23,6 +23,7 @@ fd.item = new Package.Edit({
|
|||
revision_author: '{{ revision.author }}',
|
||||
switch_sdk_url: '{{ revision.get_switch_sdk_url }}',
|
||||
modules: {{ revision.get_modules_list_json|safe }},
|
||||
attachments: {{ revision.get_attachments_list_json|safe }},
|
||||
// Actions
|
||||
save_url: '{{ revision.get_save_url }}',
|
||||
add_module_url: '{{ revision.get_add_module_url }}',
|
||||
|
|
|
@ -21,6 +21,7 @@ fd.item = new Package.View({
|
|||
origin_url: '{{ revision.origin.get_absolute_url }}',
|
||||
revision_author: '{{ revision.author }}',
|
||||
modules: {{ revision.get_modules_list_json|safe }},
|
||||
attachments: {{ revision.get_attachments_list_json|safe }},
|
||||
package_info: '{% escape_template "_view_package_info.html" %}',
|
||||
copy_url: '{{ revision.get_copy_url }}'
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"ext": "{{ attachment.ext }}",
|
||||
"basename": "{{ attachment.get_filename }}",
|
||||
"display_url": "{{ attachment.get_display_url }}",
|
||||
"author": "{{ attachment.author.username }}"
|
||||
"author": "{{ attachment.author.username }}",
|
||||
"uid": "{{ attachment.get_uid }}",
|
||||
"get_url": "{% url jp_attachment attachment.get_uid %}"
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
"message_title": "Success",
|
||||
"message": "Attachment {{ attachment.filename }} removed",
|
||||
"filename": "{{ attachment.filename }}",
|
||||
"ext": "{{ attachment.ext }}"
|
||||
"ext": "{{ attachment.ext }}",
|
||||
"uid": "{{ attachment.get_uid }}"
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import os
|
||||
import tempfile
|
||||
from test_utils import TestCase
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
|
||||
from jetpack.models import Attachment
|
||||
from jetpack.errors import UpdateDeniedException
|
||||
|
||||
|
||||
class AttachmentTest(TestCase):
|
||||
|
@ -16,28 +16,27 @@ class AttachmentTest(TestCase):
|
|||
def setUp(self):
|
||||
self.author = User.objects.get(username='john')
|
||||
|
||||
if not os.path.exists(settings.UPLOAD_DIR):
|
||||
os.mkdir(settings.UPLOAD_DIR)
|
||||
self.old = settings.UPLOAD_DIR
|
||||
settings.UPLOAD_DIR = tempfile.mkdtemp()
|
||||
|
||||
# Simulating upload.
|
||||
handle = open(os.path.join(settings.UPLOAD_DIR, 'test_filename.txt'),
|
||||
'w')
|
||||
handle.write('unit test file')
|
||||
handle.close()
|
||||
self.attachment = Attachment.objects.create(
|
||||
filename='test_filename',
|
||||
ext='txt',
|
||||
path='test_filename.txt',
|
||||
filename='test_filename.txt',
|
||||
author=self.author
|
||||
)
|
||||
self.attachment.create_path()
|
||||
self.attachment.data = 'test'
|
||||
self.attachment.write()
|
||||
|
||||
self.path = self.attachment.path
|
||||
|
||||
def tearDown(self):
|
||||
os.remove(os.path.join(settings.UPLOAD_DIR, 'test_filename.txt'))
|
||||
|
||||
def test_update_attachment_using_save(self):
|
||||
" updating attachment is not allowed "
|
||||
self.assertRaises(UpdateDeniedException, self.attachment.save)
|
||||
os.remove(os.path.join(settings.UPLOAD_DIR, self.path))
|
||||
settings.UPLOAD_DIR = self.old
|
||||
|
||||
def test_export_file(self):
|
||||
self.attachment.export_file('/tmp')
|
||||
self.failUnless(os.path.isfile('/tmp/test_filename.txt'))
|
||||
destination = tempfile.mkdtemp()
|
||||
filename = '%s.%s' % (self.attachment.filename, self.attachment.ext)
|
||||
filename = os.path.join(destination, filename)
|
||||
self.attachment.export_file(destination)
|
||||
self.failUnless(os.path.isfile(filename))
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import tempfile
|
||||
import os
|
||||
|
||||
from test_utils import TestCase
|
||||
|
||||
from nose.tools import eq_
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from jetpack.models import Package, PackageRevision, Module, Attachment
|
||||
|
@ -178,9 +183,7 @@ class PackageRevisionTest(TestCase):
|
|||
addon.save()
|
||||
first = addon.latest
|
||||
first.attachment_create(
|
||||
filename='test',
|
||||
ext='txt',
|
||||
path='/tmp/testupload',
|
||||
filename='test.txt',
|
||||
author=self.author
|
||||
)
|
||||
|
||||
|
@ -189,8 +192,26 @@ class PackageRevisionTest(TestCase):
|
|||
first = revisions[1]
|
||||
second = revisions[0]
|
||||
|
||||
self.assertEqual(0, first.attachments.count())
|
||||
self.assertEqual(1, second.attachments.count())
|
||||
eq_(0, first.attachments.count())
|
||||
eq_(1, second.attachments.count())
|
||||
|
||||
def test_read_write_attachment(self):
|
||||
"""Test that we can read and write to an attachment."""
|
||||
addon = Package(author=self.author, type='a')
|
||||
addon.save()
|
||||
first = addon.latest
|
||||
filename = tempfile.mkstemp()[1]
|
||||
try:
|
||||
attachment = first.attachment_create(
|
||||
filename='test.txt',
|
||||
author=self.author
|
||||
)
|
||||
attachment.data = 'This is a test.'
|
||||
attachment.write()
|
||||
assert attachment.read() == attachment.data
|
||||
assert not attachment.changed()
|
||||
finally:
|
||||
os.remove(filename)
|
||||
|
||||
def test_updating_module(self):
|
||||
" Updating module has some additional action "
|
||||
|
@ -202,7 +223,7 @@ class PackageRevisionTest(TestCase):
|
|||
author=self.author
|
||||
)
|
||||
mod.code = 'test'
|
||||
first.module_update(mod)
|
||||
first.update(mod)
|
||||
|
||||
# create new revision on module update
|
||||
self.assertEqual(3, addon.revisions.count())
|
||||
|
@ -233,25 +254,19 @@ class PackageRevisionTest(TestCase):
|
|||
self.assertRaises(FilenameExistException, first.module_add, mod)
|
||||
|
||||
def test_adding_attachment_with_existing_filename(self):
|
||||
" filname is unique per packagerevision "
|
||||
"""Filename is unique per packagerevision."""
|
||||
first = PackageRevision.objects.filter(package__pk=self.addon.pk)[0]
|
||||
first.attachment_create(
|
||||
filename='test_filename',
|
||||
ext='.txt',
|
||||
path='/tmp/upload_path',
|
||||
filename='test_filename.txt',
|
||||
author=self.author
|
||||
)
|
||||
self.assertRaises(FilenameExistException, first.attachment_create,
|
||||
**{'filename': 'test_filename',
|
||||
'ext': '.txt',
|
||||
'author': self.author,
|
||||
'path': '/tmp/upload_path'}
|
||||
**{'filename': 'test_filename.txt',
|
||||
'author': self.author}
|
||||
)
|
||||
|
||||
att = Attachment.objects.create(
|
||||
filename='test_filename',
|
||||
ext='.txt',
|
||||
path='/tmp/upload_path',
|
||||
ext='txt',
|
||||
author=self.author
|
||||
)
|
||||
self.assertRaises(FilenameExistException, first.attachment_add, att)
|
||||
|
|
|
@ -1,12 +1,30 @@
|
|||
from django.core.urlresolvers import reverse
|
||||
import os
|
||||
import json
|
||||
import StringIO
|
||||
from datetime import datetime
|
||||
|
||||
import test_utils
|
||||
from test_utils import TestCase
|
||||
from nose.tools import eq_
|
||||
from nose import SkipTest
|
||||
from mock import patch
|
||||
|
||||
from pyquery import PyQuery as pq
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
class TestViews(test_utils.TestCase):
|
||||
from jetpack.models import PackageRevision
|
||||
from jetpack.errors import FilenameExistException
|
||||
|
||||
|
||||
def next(revision):
|
||||
number = revision.revision_number
|
||||
return (PackageRevision.objects.filter(revision_number__gt=number)
|
||||
.order_by('-revision_number')[:1])[0]
|
||||
|
||||
|
||||
class TestViews(TestCase):
|
||||
fixtures = ('mozilla_user', 'core_sdk', 'users', 'packages')
|
||||
|
||||
def setUp(self):
|
||||
|
@ -29,3 +47,120 @@ class TestViews(test_utils.TestCase):
|
|||
r = self.client.get(next)
|
||||
doc = pq(r.content)
|
||||
eq_(doc('#app-content h2').text(), 'XPI Not Ready')
|
||||
|
||||
|
||||
class TestAttachments(TestCase):
|
||||
fixtures = ['mozilla_user', 'users', 'core_sdk', 'packages']
|
||||
|
||||
def setUp(self):
|
||||
if not os.path.exists(settings.UPLOAD_DIR):
|
||||
os.makedirs(settings.UPLOAD_DIR)
|
||||
|
||||
self.author = User.objects.get(username='john')
|
||||
self.author.set_password('password')
|
||||
self.author.save()
|
||||
|
||||
self.package = self.author.packages_originated.addons()[0:1].get()
|
||||
self.revision = self.package.revisions.all()[0]
|
||||
|
||||
self.add_url = self.get_add_url(self.revision.revision_number)
|
||||
self.change_url = self.get_change_url(self.revision.revision_number)
|
||||
self.client.login(username=self.author.username, password='password')
|
||||
|
||||
def test_attachment_error(self):
|
||||
res = self.client.post(self.add_url, {})
|
||||
eq_(res.status_code, 500)
|
||||
|
||||
def get_add_url(self, revision):
|
||||
args = [self.package.id_number, revision]
|
||||
return reverse('jp_addon_revision_add_attachment', args=args)
|
||||
|
||||
def get_change_url(self, revision):
|
||||
args = [self.package.id_number, revision]
|
||||
return reverse('jp_addon_revision_save', args=args)
|
||||
|
||||
def get_revision(self):
|
||||
return PackageRevision.objects.get(pk=self.revision.pk)
|
||||
|
||||
def post(self, url, data, filename):
|
||||
# A post that matches the JS and uses raw_post_data.
|
||||
return self.client.post(url, data,
|
||||
content_type='text/plain',
|
||||
HTTP_X_FILE_NAME=filename)
|
||||
|
||||
def test_attachment_path(self):
|
||||
res = self.post(self.add_url, 'foo', 'some.txt')
|
||||
eq_(res.status_code, 200)
|
||||
revision = PackageRevision.objects.get(package=self.package,
|
||||
revision_number=1)
|
||||
att = revision.attachments.all()[0]
|
||||
bits = att.path.split(os.path.sep)
|
||||
now = datetime.now()
|
||||
eq_(bits[-4:-1], [str(now.year), str(now.month), str(now.day)])
|
||||
|
||||
def test_attachment_add_read(self):
|
||||
res = self.post(self.add_url, 'foo', 'some.txt')
|
||||
eq_(res.status_code, 200)
|
||||
|
||||
revision = PackageRevision.objects.get(package=self.package,
|
||||
revision_number=1)
|
||||
eq_(revision.attachments.count(), 1)
|
||||
eq_(revision.attachments.all()[0].read(), 'foo')
|
||||
|
||||
def test_attachment_add(self):
|
||||
res = self.post(self.add_url, 'foo', 'some.txt')
|
||||
eq_(res.status_code, 200)
|
||||
json.loads(res.content)
|
||||
|
||||
revision = PackageRevision.objects.get(package=self.package,
|
||||
revision_number=1)
|
||||
eq_(revision.attachments.count(), 1)
|
||||
|
||||
def test_attachment_large(self):
|
||||
raise SkipTest()
|
||||
# A test for large attachments... really slow things
|
||||
# down, so before you remove the above, clean this up
|
||||
# or drop down the file size limit.
|
||||
temp = StringIO.StringIO()
|
||||
for x in range(0, 1024 * 32):
|
||||
temp.write("x" * 1024)
|
||||
|
||||
self.post(self.add_url, temp.getvalue(), 'some-big-file.txt')
|
||||
|
||||
def test_attachment_same_fails(self):
|
||||
self.test_attachment_add()
|
||||
self.assertRaises(FilenameExistException, self.post,
|
||||
self.get_add_url(1), 'foo bar', 'some.txt')
|
||||
|
||||
def test_attachment_revision_count(self):
|
||||
revisions = PackageRevision.objects.filter(package=self.package)
|
||||
eq_(revisions.count(), 1)
|
||||
self.test_attachment_add()
|
||||
eq_(revisions.count(), 2)
|
||||
# Double check that adding a revision does not create a new version.
|
||||
|
||||
def test_attachment_same_change(self):
|
||||
self.test_attachment_add()
|
||||
revision = PackageRevision.objects.get(package=self.package,
|
||||
revision_number=1)
|
||||
eq_(revision.attachments.count(), 1)
|
||||
|
||||
data = {revision.attachments.all()[0].get_uid: 'foo bar'}
|
||||
res = self.client.post(self.get_change_url(1), data)
|
||||
eq_(res.status_code, 200)
|
||||
|
||||
eq_(revision.attachments.all()[0].read(), 'foo')
|
||||
|
||||
revision = PackageRevision.objects.get(package=self.package,
|
||||
revision_number=2)
|
||||
eq_(revision.attachments.count(), 1)
|
||||
eq_(revision.attachments.all()[0].read(), 'foo bar')
|
||||
|
||||
def test_attachment_two_files(self):
|
||||
self.post(self.add_url, 'foo', 'some.txt')
|
||||
revision = PackageRevision.objects.get(package=self.package,
|
||||
revision_number=1)
|
||||
assert revision.attachments.count(), 1
|
||||
|
||||
self.post(self.add_url, 'foo', 'some-other.txt')
|
||||
assert revision.attachments.count(), 2
|
||||
|
|
|
@ -113,7 +113,7 @@ urlpatterns = patterns('jetpack.views',
|
|||
{'type_id': 'l'}, name='jp_library_revision_remove_attachment'),
|
||||
|
||||
# display attachment
|
||||
url(r'^attachment/(?P<path>.*)$',
|
||||
url(r'^attachment/(?P<uid>.*)$',
|
||||
'download_attachment', name='jp_attachment'),
|
||||
|
||||
# autocomplete library
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
"""
|
||||
Views for the Jetpack application
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
import commonware.log
|
||||
|
||||
from django.contrib import messages
|
||||
|
@ -81,6 +79,7 @@ def package_view_or_edit(r, id_number, type_id, revision_number=None,
|
|||
else:
|
||||
return package_view(r, revision)
|
||||
|
||||
|
||||
@login_required
|
||||
def package_edit(r, revision):
|
||||
"""
|
||||
|
@ -179,8 +178,6 @@ def package_copy(r, id_number, type_id,
|
|||
escape(source.package.get_type_name()))
|
||||
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
def package_disable(r, id_number):
|
||||
"""
|
||||
|
@ -344,31 +341,17 @@ def package_add_attachment(r, id_number, type_id,
|
|||
% escape(revision.package.get_type_name()))
|
||||
|
||||
content = r.raw_post_data
|
||||
path = r.META.get('HTTP_X_FILE_NAME', False)
|
||||
filename = r.META.get('HTTP_X_FILE_NAME')
|
||||
|
||||
if not path:
|
||||
if not filename:
|
||||
log_msg = 'Path not found: %s, package: %s.' % (
|
||||
path, id_number)
|
||||
filename, id_number)
|
||||
log.debug(log_msg)
|
||||
return HttpResponseServerError
|
||||
return HttpResponseServerError('Path not found.')
|
||||
|
||||
filename, ext = os.path.splitext(path)
|
||||
ext = ext.split('.')[1].lower() if ext else ''
|
||||
|
||||
upload_path = "%s_%s_%s.%s" % (revision.package.id_number,
|
||||
time.strftime("%m-%d-%H-%M-%S"),
|
||||
filename, ext)
|
||||
|
||||
handle = open(os.path.join(settings.UPLOAD_DIR, upload_path), 'w')
|
||||
handle.write(content)
|
||||
handle.close()
|
||||
|
||||
attachment = revision.attachment_create(
|
||||
author=r.user,
|
||||
filename=filename,
|
||||
ext=ext,
|
||||
path=upload_path
|
||||
)
|
||||
attachment = revision.attachment_create(r.user, filename)
|
||||
attachment.data = content
|
||||
attachment.write()
|
||||
|
||||
return render_to_response("json/attachment_added.json",
|
||||
{'revision': revision, 'attachment': attachment},
|
||||
|
@ -416,13 +399,14 @@ def package_remove_attachment(r, id_number, type_id, revision_number):
|
|||
mimetype='application/json')
|
||||
|
||||
|
||||
def download_attachment(r, path):
|
||||
def download_attachment(request, uid):
|
||||
"""
|
||||
Display attachment from PackageRevision
|
||||
"""
|
||||
get_object_or_404(Attachment, path=path)
|
||||
response = serve(r, path, settings.UPLOAD_DIR, show_indexes=False)
|
||||
#response['Content-Type'] = 'application/octet-stream';
|
||||
attachment = get_object_or_404(Attachment, id=uid)
|
||||
response = serve(request, attachment.path,
|
||||
settings.UPLOAD_DIR, show_indexes=False)
|
||||
response['Content-Disposition'] = 'filename=%s' % attachment.filename
|
||||
return response
|
||||
|
||||
|
||||
|
@ -482,16 +466,23 @@ def package_save(r, id_number, type_id, revision_number=None,
|
|||
revision.package.description = package_description
|
||||
response_data['package_description'] = package_description
|
||||
|
||||
modules = []
|
||||
changes = []
|
||||
for mod in revision.modules.all():
|
||||
if r.POST.get(mod.filename, False):
|
||||
code = r.POST[mod.filename]
|
||||
if mod.code != code:
|
||||
mod.code = code
|
||||
modules.append(mod)
|
||||
changes.append(mod)
|
||||
|
||||
if modules:
|
||||
revision.modules_update(modules)
|
||||
for attachment in revision.attachments.all():
|
||||
if attachment.get_uid in r.POST:
|
||||
data = r.POST[attachment.get_uid]
|
||||
attachment.data = data
|
||||
if attachment.changed():
|
||||
changes.append(attachment)
|
||||
|
||||
if changes:
|
||||
revision.updates(changes)
|
||||
save_revision = False
|
||||
|
||||
if save_revision:
|
||||
|
@ -574,7 +565,6 @@ def library_autocomplete(r):
|
|||
except:
|
||||
found = []
|
||||
|
||||
|
||||
return render_to_response('json/library_autocomplete.json',
|
||||
{'libraries': found},
|
||||
context_instance=RequestContext(r),
|
||||
|
@ -663,5 +653,3 @@ def get_revisions_list_html(r, id_number):
|
|||
|
||||
|
||||
# ---------------------------- XPI ---------------------------------
|
||||
|
||||
|
||||
|
|
|
@ -121,18 +121,19 @@ class XPIBuildTest(TestCase):
|
|||
self.addonrev.module_main)))
|
||||
|
||||
def test_addon_export_with_attachment(self):
|
||||
" test if attachment file is coped "
|
||||
"""Test if attachment file is copied."""
|
||||
self.makeSDKDir()
|
||||
# create attachment in upload dir
|
||||
handle = open(self.attachment_file_name, 'w')
|
||||
handle.write('unit test file')
|
||||
handle.close()
|
||||
self.addonrev.attachment_create(
|
||||
filename='test_filename',
|
||||
ext='txt',
|
||||
path='test_filename.txt',
|
||||
attachment = self.addonrev.attachment_create(
|
||||
filename='test_filename.txt',
|
||||
author=self.author
|
||||
)
|
||||
attachment.create_path()
|
||||
attachment.data = ''
|
||||
attachment.write()
|
||||
self.addonrev.export_files_with_dependencies(
|
||||
'%s/packages' % self.SDKDIR)
|
||||
self.failUnless(os.path.isfile(self.attachment_file_name))
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -42,8 +42,8 @@ sendFile = (function(toString, maxSize){
|
|||
} else {
|
||||
setTimeout(function(){
|
||||
if(xhr.readyState === 4){
|
||||
if(isFunction(handler.onpartialload))
|
||||
handler.onpartialload(rpe, xhr);
|
||||
if(isFunction(handler.onpartialload))
|
||||
handler.onpartialload(rpe, xhr);
|
||||
if(isFunction(handler.onload))
|
||||
handler.onload(rpe, xhr);
|
||||
} else
|
||||
|
@ -112,12 +112,12 @@ if(isset($_GET['upload']) && $_GET['upload'] === 'true'){
|
|||
$file->name = basename($headers['X-File-Name']);
|
||||
$file->size = $headers['X-File-Size'];
|
||||
$file->content = file_get_contents("php://input");
|
||||
|
||||
|
||||
// if everything is ok, save the file somewhere
|
||||
if(file_put_contents('files/'.$file->name, $file->content))
|
||||
exit('OK');
|
||||
}
|
||||
|
||||
|
||||
// if there is an error this will be the output instead of "OK"
|
||||
exit('Error');
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче