[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:
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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):
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user