[IMP] simplify ckeditor image and link dialogs

& add easy linking to existing site pages and attachments

bzr revid: xmo@openerp.com-20130821143816-0hvp0m0eklh6ti1m
This commit is contained in:
Xavier Morel
2013-08-21 16:38:16 +02:00
7 changed files with 204 additions and 30 deletions
+18 -8
View File
@@ -27,6 +27,10 @@ def auth_method_public():
http.auth_methods['public'] = auth_method_public
# PIL images have a type flag, but no MIME. Reverse type flag to MIME.
PIL_MIME_MAPPING = {'PNG': 'image/png', 'JPEG': 'image/jpeg', 'GIF': 'image/gif', }
# Completely arbitrary limits
MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768)
class Website(openerp.addons.web.controllers.main.Home):
@http.route('/', type='http', auth="admin")
def index(self, **kw):
@@ -45,7 +49,8 @@ class Website(openerp.addons.web.controllers.main.Home):
newview = view.browse(request.cr, request.uid, newview_id, context={})
newview.write({
'arch': newview.arch.replace("website.default_page", path),
'name': "page/%s" % path
'name': "page/%s" % path,
'page': True,
})
if '.' in path:
module, idname = path.split('.')
@@ -145,15 +150,20 @@ class Website(openerp.addons.web.controllers.main.Home):
buf = cStringIO.StringIO(base64.decodestring(attachment.datas))
image = Image.open(buf)
image.thumbnail((1024, 768), Image.ANTIALIAS)
mime = PIL_MIME_MAPPING[image.format]
response = werkzeug.wrappers.Response(status=200, mimetype={
'PNG': 'image/png',
'JPEG': 'image/jpeg',
'GIF': 'image/gif',
}[image.format])
w, h = image.size
resized = w > MAX_IMAGE_WIDTH or h > MAX_IMAGE_HEIGHT
# If saving unnecessary, just send the image buffer, don't go through
# Image.save() (especially as it breaks animated gifs)
if not resized:
buf.seek(0)
return werkzeug.wrappers.Response(buf, status=200, mimetype=mime)
image.thumbnail(IMAGE_LIMITS, Image.ANTIALIAS)
response = werkzeug.wrappers.Response(status=200, mimetype=mime)
image.save(response.stream, image.format)
return response
@http.route('/website/image', type='http', auth="public")
+141 -1
View File
@@ -250,7 +250,7 @@
]},{
name: 'span', items: [
"Link", "Unlink", "Blockquote", "BulletedList",
"NumberedList", "Indent", "Outdent",
"NumberedList", "Indent", "Outdent"
]},{
name: 'justify', items: [
"JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock"
@@ -439,6 +439,146 @@
},
});
function noop() {}
var alter_dialog = {
image: function (definition) {
definition.removeContents('Link');
definition.removeContents('advanced');
var upload = definition.getContents('Upload');
upload.add({
type: 'select',
label: 'Existing attachments',
id: 'ir_attachment',
items: [['']],
/**
* On dialog load, fetch all attachments (on ir.ui.view =>
* previously uploaded images) and add them to the select's
* options (items array & add method)
*/
onLoad: function () {
var field = this;
// FIXME: fuck this garbage, also fuck openerp.Model
return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
model: 'ir.attachment',
method: 'search_read',
args: [],
kwargs: {
fields: ['name'],
domain: [['res_model', '=', 'ir.ui.view']],
order: 'name',
}
}).then(function (results) {
_(results).each(function (result) {
field.add(result.name, result.id);
});
});
},
/**
* The image widgets uses "txtUrl" to do most of its stuff.
* Synchronize select & txtUrl by generating the correct URL
* and setting it there
*/
onChange: function () {
var id = this.getValue();
var url = this.getDialog().getContentElement('info', 'txtUrl');
if (!id) {
url.setValue('');
return;
}
url.setValue('/website/attachment/' + id);
},
});
// Override uploadButton to send its information to the select
// created above instead of directly to txtUrl. The select will
// propagate to txtUrl
upload.get('uploadButton').filebrowser = {
onSelect: function (url) {
var id = url.split('/').pop();
var attachments = this.getDialog().getContentElement('Upload', 'ir_attachment');
// TODO: return supplementary info to get image/attachment name?
attachments.add(id, id);
attachments.setValue(id);
}
};
var old_show = definition.onShow;
definition.onShow = function () {
// CKEDITOR does not *override* onShow, is smashes the existing
// one instead, so override "by hand"
if (old_show) {
old_show.call(this);
}
// Assloads of code in the image plugin just go and tear into
// the info tab without a care, so can't just remove the tab or
// its content. Hide the tab instead, the effect is roughly the
// same.
this.hidePage('info');
// Force the dialog to always and only display the Upload tab
this.selectPage('Upload');
this.on('selectPage', function (e) {
setTimeout(function () {
if (e.data.page !== 'Upload') {
this.selectPage('Upload');
}
}.bind(this), 0);
});
}
},
link: function (definition) {
definition.removeContents('target');
definition.removeContents('advanced');
var info = definition.getContents('info');
info.remove('linkType');
info.remove('anchorOptions');
info.remove('emailOptions');
info.get('urlOptions').children[0].widths = [ '0%', '100%' ];
info.get('protocol').style = 'display: none';
// TODO: sync edition of url to website_pages?
info.add({
type: 'select',
label: "Existing page",
id: 'website_pages',
items: [['']],
/**
* onload fetch all the pages existing in the website, then
* display that.
*/
onLoad: function () {
var field = this;
return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
model: 'website',
method: 'list_pages',
args: [],
kwargs: {}
}).then(function (results) {
_(results).each(function (result) {
field.add(result.name, result.url);
});
});
},
onChange: function () {
var url = this.getValue();
var url_field = this.getDialog().getContentElement('info', 'url');
if (!url) {
url_field.setValue('');
return;
}
url_field.setValue(url);
}
})
}
};
CKEDITOR.on('dialogDefinition', function (ev) {
(alter_dialog[ev.data.name] || noop)(ev.data.definition);
});
var all_ready = null;
var dom_ready = $.Deferred();
$(dom_ready.resolve);
+3 -3
View File
@@ -132,7 +132,7 @@
</xpath>
</template>
<template id="homepage">
<template id="homepage" page="True">
<t t-call="website.layout">
<section>
@@ -318,7 +318,7 @@
</template>
<template id="contactus" name="Contact us">
<template id="contactus" name="Contact us" page="True">
<t t-call="website.layout">
<t t-set="title">Contact us - <t t-raw="res_company.name"/></t>
<div class="container">
@@ -353,7 +353,7 @@
</t>
</template>
<template id="aboutus">
<template id="aboutus" page="True">
<t t-call="website.layout">
<t t-set="title">About us - <t t-raw="res_company.name"/></t>
<div class="container">
+38 -14
View File
@@ -93,19 +93,16 @@ class website(osv.osv):
def pager(self, url, total, page=1, step=30, scope=5, url_args=None):
# Compute Pager
d = {}
d["page_count"] = int(math.ceil(float(total) / step))
page_count = int(math.ceil(float(total) / step))
page = max(1, min(int(page), d["page_count"]))
d["offset"] = (page-1) * step
page = max(1, min(int(page), page_count))
scope -= 1
pmin = max(page - int(math.floor(scope/2)), 1)
pmax = min(pmin + scope, d["page_count"])
pmax = min(pmin + scope, page_count)
if pmax - pmin < scope:
pmin = pmax - scope > 0 and pmax - scope or 1
pmin = pmax - scope if pmax - scope > 0 else 1
def get_url(page):
_url = "%spage/%s/" % (url, page)
@@ -113,14 +110,41 @@ class website(osv.osv):
_url = "%s?%s" % (_url, urllib.urlencode(url_args))
return _url
d["page"] = {'url': get_url(page), 'num': page}
d["page_start"] = {'url': get_url(pmin), 'num': pmin}
d["page_end"] = {'url': get_url(min(pmax, page+1)), 'num': min(pmax, page+1)}
d["pages"] = []
for page in range(pmin, pmax+1):
d["pages"].append({'url': get_url(page), 'num': page})
return {
"page_count": page_count,
"offset": (page - 1) * step,
"page": {'url': get_url(page), 'num': page},
"page_start": {'url': get_url(pmin), 'num': pmin},
"page_end": {'url': get_url(min(pmax, page + 1)),
'num': min(pmax, page + 1)},
"pages": [
{'url': get_url(page), 'num': page}
for page in xrange(pmin, pmax+1)
]
}
return d
def list_pages(self, cr, uid, context=None):
""" Available pages in the website/CMS. This is mostly used for links
generation and can be overridden by modules setting up new HTML
controllers for dynamic pages (e.g. blog).
By default, returns template views marked as pages.
:returns: a list of mappings with two keys: ``name`` is the displayable
name of the resource (page), ``url`` is the absolute URL
of the same.
:rtype: list({name: str, url: str})
"""
View = self.pool['ir.ui.view']
views = View.search_read(cr, uid, [['page', '=', True]],
fields=['name'], order='name', context=context)
xids = View.get_external_id(cr, uid, [view['id'] for view in views], context=context)
return [
{'name': view['name'], 'url': '/page/' + xids[view['id']]}
for view in views
if xids[view['id']]
]
class res_partner(osv.osv):
+1 -1
View File
@@ -15,7 +15,7 @@
<!-- Page -->
<template id="index" name="Events">
<template id="index" name="Events" page="True">
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_hr/static/src/js/website_hr.js"></script>
+1 -1
View File
@@ -23,7 +23,7 @@
<!-- Page -->
<template id="index" name="Blogs">
<template id="index" name="Blogs" page="True">
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_mail/static/src/js/blog.js"></script>
+2 -2
View File
@@ -93,7 +93,7 @@
<!-- Product list -->
<template id="products">
<template id="products" page="True">
<t t-call="website_sale.layout">
<t t-set="title">Product</t>
<t t-set="shop_content">
@@ -184,7 +184,7 @@
<!-- Page Shop my cart -->
<template id="mycart" name="My cart">
<template id="mycart" name="My cart" page="True">
<t t-call="website_sale.layout">
<t t-set="title">My cart</t>
<t t-set="shop_content">