summaryrefslogtreecommitdiff
path: root/addons/web_editor/controllers
diff options
context:
space:
mode:
authorChristophe Matthieu <chm@odoo.com>2015-07-07 16:02:53 +0200
committerChristophe Matthieu <chm@odoo.com>2015-07-10 17:00:11 +0200
commit8c559044a5e1be111dcb577ad7e5ab462c1002a0 (patch)
treeba1602e15b2bca38babd6cca63a41da1d05b4321 /addons/web_editor/controllers
parenta616c57c609da0e5e56835dcb45e1c05590d25b8 (diff)
[IMP] web_editor: split website into web_editor (to use editor in backend for html field) and website
Diffstat (limited to 'addons/web_editor/controllers')
-rw-r--r--addons/web_editor/controllers/__init__.py1
-rw-r--r--addons/web_editor/controllers/main.py276
2 files changed, 277 insertions, 0 deletions
diff --git a/addons/web_editor/controllers/__init__.py b/addons/web_editor/controllers/__init__.py
new file mode 100644
index 00000000000..8ee9bae18d9
--- /dev/null
+++ b/addons/web_editor/controllers/__init__.py
@@ -0,0 +1 @@
+import main
diff --git a/addons/web_editor/controllers/main.py b/addons/web_editor/controllers/main.py
new file mode 100644
index 00000000000..7d9b6206ae7
--- /dev/null
+++ b/addons/web_editor/controllers/main.py
@@ -0,0 +1,276 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request, STATIC_CACHE
+from openerp.addons.web import http
+import json
+import io
+from PIL import Image, ImageFont, ImageDraw
+from openerp import tools
+import cStringIO
+import werkzeug.wrappers
+import time
+import logging
+logger = logging.getLogger(__name__)
+
+
+class Web_Editor(http.Controller):
+ #------------------------------------------------------
+ # Backend snippet
+ #------------------------------------------------------
+ @http.route('/web_editor/snippets', type='json', auth="user")
+ def snippets(self, **kwargs):
+ return request.registry["ir.ui.view"].render(request.cr, request.uid, 'web_editor.snippets', None, context=request.context)
+
+ #------------------------------------------------------
+ # Backend html field
+ #------------------------------------------------------
+ @http.route('/web_editor/field/html', type='http', auth="user")
+ def FieldTextHtml(self, model=None, res_id=None, field=None, callback=None, **kwargs):
+ cr, uid, context = request.cr, request.uid, request.context
+
+ kwargs.update(
+ model=model,
+ res_id=res_id,
+ field=field,
+ datarecord=json.loads(kwargs['datarecord']),
+ debug='debug' in kwargs)
+
+ for k in kwargs:
+ if isinstance(kwargs[k], basestring) and kwargs[k].isdigit():
+ kwargs[k] = int(kwargs[k])
+
+ trans = dict(
+ lang=kwargs.get('lang', context.get('lang')),
+ translatable=kwargs.get('translatable'),
+ edit_translations=kwargs.get('edit_translations'),
+ editable=kwargs.get('enable_editor'))
+
+ context.update(trans)
+ kwargs.update(trans)
+
+ record = None
+ if model and kwargs.get('res_id'):
+ record = request.registry[model].browse(cr, uid, kwargs.get('res_id'), context)
+
+ kwargs.update(content=record and getattr(record, field) or "")
+
+ return request.render(kwargs.get("template") or "web_editor.FieldTextHtml", kwargs, uid=request.uid)
+
+ #------------------------------------------------------
+ # Backend html field in inline mode
+ #------------------------------------------------------
+ @http.route('/web_editor/field/html/inline', type='http', auth="user")
+ def FieldTextHtmlInline(self, model=None, res_id=None, field=None, callback=None, **kwargs):
+ kwargs['inline_mode'] = True
+ kwargs['dont_load_assets'] = not kwargs.get('enable_editor') and not kwargs.get('edit_translations')
+ return self.FieldTextHtml(model, res_id, field, callback, **kwargs)
+
+ #------------------------------------------------------
+ # convert font into picture
+ #------------------------------------------------------
+ @http.route([
+ '/web_editor/font_to_img/<icon>',
+ '/web_editor/font_to_img/<icon>/<color>',
+ '/web_editor/font_to_img/<icon>/<color>/<int:size>',
+ '/web_editor/font_to_img/<icon>/<color>/<int:size>/<int:alpha>',
+ ], type='http', auth="none")
+ def export_icon_to_png(self, icon, color='#000', size=100, alpha=255, font='/web/static/lib/fontawesome/fonts/fontawesome-webfont.ttf'):
+ """ This method converts FontAwesom pictograms to Images and is Used only
+ for mass mailing becuase custom fonts are not supported in mail.
+ :param icon : character from FontAwesom cheatsheet
+ :param color : RGB code of the color
+ :param size : Pixels in integer
+ :param alpha : transparency of the image from 0 to 255
+ :param font : font path
+
+ :returns PNG image converted from given font
+ """
+ # Initialize font
+ addons_path = http.addons_manifest['web']['addons_path']
+ font_obj = ImageFont.truetype(addons_path + font, size)
+
+ # Determine the dimensions of the icon
+ image = Image.new("RGBA", (size, size), color=(0, 0, 0, 0))
+ draw = ImageDraw.Draw(image)
+
+ boxw, boxh = draw.textsize(icon, font=font_obj)
+ draw.text((0, 0), icon, font=font_obj)
+ left, top, right, bottom = image.getbbox()
+
+ # Create an alpha mask
+ imagemask = Image.new("L", (boxw, boxh), 0)
+ drawmask = ImageDraw.Draw(imagemask)
+ drawmask.text((-left, -top), icon, font=font_obj, fill=alpha)
+
+ # Create a solid color image and apply the mask
+ if color.startswith('rgba'):
+ color = color.replace('rgba', 'rgb')
+ color = ','.join(color.split(',')[:-1])+')'
+ iconimage = Image.new("RGBA", (boxw, boxh), color)
+ iconimage.putalpha(imagemask)
+
+ # Create output image
+ outimage = Image.new("RGBA", (boxw, size), (0, 0, 0, 0))
+ outimage.paste(iconimage, (left, top))
+
+ # output image
+ output = io.BytesIO()
+ outimage.save(output, format="PNG")
+ response = werkzeug.wrappers.Response()
+ response.mimetype = 'image/png'
+ response.data = output.getvalue()
+ response.headers['Cache-Control'] = 'public, max-age=604800'
+ response.headers['Access-Control-Allow-Origin'] = '*'
+ response.headers['Access-Control-Allow-Methods'] = 'GET, POST'
+ response.headers['Connection'] = 'close'
+ response.headers['Date'] = time.strftime("%a, %d-%b-%Y %T GMT", time.gmtime())
+ response.headers['Expires'] = time.strftime("%a, %d-%b-%Y %T GMT", time.gmtime(time.time()+604800*60))
+
+ return response
+
+ #------------------------------------------------------
+ # image route for browse record
+ #------------------------------------------------------
+ def placeholder(self, response):
+ return request.registry['ir.attachment']._image_placeholder(response)
+
+ @http.route([
+ '/web_editor/image',
+ '/web_editor/image/<xmlid>',
+ '/web_editor/image/<xmlid>/<int:max_width>x<int:max_height>',
+ '/web_editor/image/<xmlid>/<field>',
+ '/web_editor/image/<xmlid>/<field>/<int:max_width>x<int:max_height>',
+ '/web_editor/image/<model>/<id>/<field>',
+ '/web_editor/image/<model>/<id>/<field>/<int:max_width>x<int:max_height>'
+ ], type='http', auth="public")
+ def image(self, model=None, id=None, field=None, xmlid=None, max_width=None, max_height=None):
+ """ Fetches the requested field and ensures it does not go above
+ (max_width, max_height), resizing it if necessary.
+
+ If the record is not found or does not have the requested field,
+ returns a placeholder image via :meth:`~.placeholder`.
+
+ Sets and checks conditional response parameters:
+ * :mailheader:`ETag` is always set (and checked)
+ * :mailheader:`Last-Modified is set iif the record has a concurrency
+ field (``__last_update``)
+
+ The requested field is assumed to be base64-encoded image data in
+ all cases.
+
+ xmlid can be used to load the image. But the field image must by base64-encoded
+ """
+ if xmlid and "." in xmlid:
+ try:
+ record = request.env.ref(xmlid)
+ model, id = record._name, record.id
+ except:
+ raise werkzeug.exceptions.NotFound()
+ if model == 'ir.attachment' and not field:
+ if record.sudo().type == "url":
+ field = "url"
+ else:
+ field = "datas"
+
+ if not model or not id or not field:
+ raise werkzeug.exceptions.NotFound()
+
+ try:
+ idsha = str(id).split('_')
+ id = idsha[0]
+ response = werkzeug.wrappers.Response()
+ return request.registry['ir.attachment']._image(
+ request.cr, request.uid, model, id, field, response, max_width, max_height,
+ cache=STATIC_CACHE if len(idsha) > 1 else None)
+ except Exception:
+ logger.exception("Cannot render image field %r of record %s[%s] at size(%s,%s)",
+ field, model, id, max_width, max_height)
+ response = werkzeug.wrappers.Response()
+ return self.placeholder(response)
+
+ #------------------------------------------------------
+ # add attachment (images or link)
+ #------------------------------------------------------
+ @http.route('/web_editor/attachment/add', type='http', auth='user', methods=['POST'])
+ def attach(self, func, upload=None, url=None, disable_optimization=None, **kwargs):
+ # the upload argument doesn't allow us to access the files if more than
+ # one file is uploaded, as upload references the first file
+ # therefore we have to recover the files from the request object
+ Attachments = request.registry['ir.attachment'] # registry for the attachment table
+
+ uploads = []
+ message = None
+ if not upload: # no image provided, storing the link and the image name
+ uploads.append({'website_url': url})
+ name = url.split("/").pop() # recover filename
+ attachment_id = Attachments.create(request.cr, request.uid, {
+ 'name': name,
+ 'type': 'url',
+ 'url': url,
+ 'res_model': 'ir.ui.view',
+ }, request.context)
+ else: # images provided
+ try:
+ attachment_ids = []
+ for c_file in request.httprequest.files.getlist('upload'):
+ image_data = c_file.read()
+ image = Image.open(cStringIO.StringIO(image_data))
+ w, h = image.size
+ if w*h > 42e6: # Nokia Lumia 1020 photo resolution
+ raise ValueError(
+ u"Image size excessive, uploaded images must be smaller "
+ u"than 42 million pixel")
+
+ if not disable_optimization and image.format in ('PNG', 'JPEG'):
+ image_data = tools.image_save_for_web(image)
+
+ attachment_id = Attachments.create(request.cr, request.uid, {
+ 'name': c_file.filename,
+ 'datas': image_data.encode('base64'),
+ 'datas_fname': c_file.filename,
+ 'res_model': 'ir.ui.view',
+ }, request.context)
+ attachment_ids.append(attachment_id)
+
+ uploads = Attachments.read(
+ request.cr, request.uid, attachment_ids, ['website_url'],
+ context=request.context)
+ except Exception, e:
+ logger.exception("Failed to upload image to attachment")
+ message = unicode(e)
+
+ return """<script type='text/javascript'>
+ window.parent['%s'](%s, %s);
+ </script>""" % (func, json.dumps(uploads), json.dumps(message))
+
+ #------------------------------------------------------
+ # remove attachment (images or link)
+ #------------------------------------------------------
+ @http.route('/web_editor/attachment/remove', type='json', auth='user')
+ def remove(self, ids, **kwargs):
+ """ 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
+ """
+ cr, uid, context = request.cr, request.uid, request.context
+ Attachment = request.registry['ir.attachment']
+ Views = request.registry['ir.ui.view']
+
+ attachments_to_remove = []
+ # views blocking removal of the attachment
+ removal_blocked_by = {}
+
+ for attachment in Attachment.browse(cr, uid, ids, context=context):
+ # in-document URLs are html-escaped, a straight search will not
+ # find them
+ url = tools.html_escape(attachment.local_url)
+ ids = Views.search(cr, uid, ["|", ('arch_db', 'like', '"%s"' % url), ('arch_db', 'like', "'%s'" % 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:
+ Attachment.unlink(cr, uid, attachments_to_remove, context=context)
+ return removal_blocked_by