[ADD] tentative removal of attachments
Attachments ought be removed only if they are not used in an existing page/web view. Theoretically this could be set directly in unlink(), but: * that would make a nice error message significantly harder * the expenses of performing a text search in all view archs would be a bit expensive Notes: * the views set could be reduced to only "web" views * the search is likely sensible to false negatives e.g. different order of query parameters. It *will* remove images still being used. bzr revid: xmo@openerp.com-20140304110716-u14w6uo8fbkfa42i
This commit is contained in:
@@ -9,6 +9,7 @@ import urlparse
|
||||
|
||||
import werkzeug
|
||||
import werkzeug.exceptions
|
||||
import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
# optional python-slugify import (https://github.com/un33k/python-slugify)
|
||||
try:
|
||||
@@ -594,6 +595,33 @@ class ir_attachment(osv.osv):
|
||||
return super(ir_attachment, self).create(
|
||||
cr, uid, values, context=context)
|
||||
|
||||
def try_remove(self, cr, uid, ids, context=None):
|
||||
""" Removes a web-based image attachment if it is used by no view
|
||||
(template)
|
||||
|
||||
Returns a dict mapping attachments which would not be removed (if any)
|
||||
mapped to the views preventing their removal
|
||||
"""
|
||||
Views = self.pool['ir.ui.view']
|
||||
attachments_to_remove = []
|
||||
# views blocking removal of the attachment
|
||||
removal_blocked_by = {}
|
||||
|
||||
for attachment in self.browse(cr, uid, ids, context=context):
|
||||
# in-document URLs are html-escaped, a straight search will not
|
||||
# find them
|
||||
url = werkzeug.utils.escape(attachment.website_url)
|
||||
ids = Views.search(cr, uid, [('arch', 'like', url)], context=context)
|
||||
|
||||
if ids:
|
||||
removal_blocked_by[attachment.id] = Views.read(
|
||||
cr, uid, ids, ['name'], context=context)
|
||||
else:
|
||||
attachments_to_remove.append(attachment.id)
|
||||
if attachments_to_remove:
|
||||
self.unlink(cr, uid, attachments_to_remove, context=context)
|
||||
return removal_blocked_by
|
||||
|
||||
class res_partner(osv.osv):
|
||||
_inherit = "res.partner"
|
||||
|
||||
|
||||
@@ -251,6 +251,26 @@ ul.oe_menu_editor .disclose {
|
||||
.existing-attachments .pager .disabled {
|
||||
display: none;
|
||||
}
|
||||
.existing-attachments .existing-attachment-cell {
|
||||
position: relative;
|
||||
}
|
||||
.existing-attachments .existing-attachment-cell .img {
|
||||
border: 1px solid #848490;
|
||||
}
|
||||
.existing-attachments .existing-attachment-cell .existing-attachment-remove {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 15px;
|
||||
cursor: pointer;
|
||||
background: white;
|
||||
padding: 2px;
|
||||
border: 1px solid #848490;
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
-moz-border-radius-bottomright: 8px;
|
||||
-webkit-border-bottom-right-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
|
||||
.cke_widget_wrapper {
|
||||
position: static !important;
|
||||
|
||||
@@ -211,9 +211,27 @@ ul.oe_menu_editor
|
||||
.font-icons-selected
|
||||
background-color: #ddd
|
||||
|
||||
$attachment-border-color: #848490
|
||||
.existing-attachments
|
||||
.pager .disabled
|
||||
display: none
|
||||
|
||||
.existing-attachments .pager .disabled
|
||||
display: none
|
||||
.existing-attachment-cell
|
||||
position: relative
|
||||
.img
|
||||
border: 1px solid $attachment-border-color
|
||||
.existing-attachment-remove
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 15px // padding-left on col-*
|
||||
|
||||
cursor: pointer
|
||||
background: white
|
||||
padding: 2px
|
||||
border: 1px solid $attachment-border-color
|
||||
border-top: none
|
||||
border-left: none
|
||||
+border-bottom-right-radius(8px)
|
||||
|
||||
// wrapper positioned relatively for drag&drop widget which is disabled below.
|
||||
// Breaks completely horribly crazy products listing page, so take it out.
|
||||
|
||||
@@ -1441,6 +1441,7 @@
|
||||
this.page += $target.hasClass('previous') ? -1 : 1;
|
||||
this.display_attachments();
|
||||
},
|
||||
'click .existing-attachment-remove': 'try_remove',
|
||||
}),
|
||||
init: function (parent) {
|
||||
this.image = null;
|
||||
@@ -1473,6 +1474,7 @@
|
||||
this.display_attachments();
|
||||
},
|
||||
display_attachments: function () {
|
||||
this.$('.help-block').empty();
|
||||
var per_screen = IMAGES_PER_ROW * IMAGES_ROWS;
|
||||
|
||||
var from = this.page * per_screen;
|
||||
@@ -1500,6 +1502,34 @@
|
||||
}
|
||||
this.close()
|
||||
},
|
||||
|
||||
try_remove: function (e) {
|
||||
var $help_block = this.$('.help-block').empty();
|
||||
var self = this;
|
||||
var id = parseInt($(e.target).data('id'), 10);
|
||||
var attachment = _.findWhere(this.records, {id: id});
|
||||
|
||||
return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
|
||||
model: 'ir.attachment',
|
||||
method: 'try_remove',
|
||||
args: [],
|
||||
kwargs: {
|
||||
ids: [id],
|
||||
context: website.get_context()
|
||||
}
|
||||
}).then(function (prevented) {
|
||||
if (_.isEmpty(prevented)) {
|
||||
self.records = _.without(self.records, attachment);
|
||||
self.display_attachments();
|
||||
return;
|
||||
}
|
||||
$help_block.replaceWith(openerp.qweb.render(
|
||||
'website.editor.dialog.image.existing.error', {
|
||||
views: prevented[id]
|
||||
}
|
||||
));
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function get_selected_link(editor) {
|
||||
|
||||
@@ -140,8 +140,9 @@
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 class="modal-title">Select a Picture</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body has-error">
|
||||
<div class="existing-attachments"/>
|
||||
<div class="help-block"/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" data-dismiss="modal" aria-hidden="true">Discard</a>
|
||||
@@ -157,12 +158,27 @@
|
||||
<li class="next disabled"><a href="#">Next →</a></li>
|
||||
</ul>
|
||||
<div class="row mt16" t-foreach="rows" t-as="row">
|
||||
<div class="col-sm-2" t-foreach="row" t-as="attachment">
|
||||
<div class="col-sm-2 existing-attachment-cell"
|
||||
t-foreach="row" t-as="attachment">
|
||||
<i class="fa fa-times existing-attachment-remove" t-att-data-id="attachment.id"/>
|
||||
<img t-att-src="attachment.website_url" t-att-alt="attachment.name" class="img img-responsive"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="website.editor.dialog.image.existing.error">
|
||||
<div class="help-block">
|
||||
<p>The image could not be deleted because it is used in the
|
||||
following pages or views:</p>
|
||||
<ul t-foreach="views" t-as="view">
|
||||
<li>
|
||||
<a t-attf-href="/web#model=ir.ui.view&id=#{view.id}">
|
||||
<t t-esc="view.name"/>
|
||||
</a> (id <t t-esc="view.id"/>)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="website.editor.table.panel">
|
||||
<table class="editorbar-panel">
|
||||
<tr t-foreach="rows"><td t-foreach="cols">​</td></tr>
|
||||
|
||||
Reference in New Issue
Block a user