├── ticket_viewer ├── models │ ├── __init__.py │ └── ticket.py ├── controllers │ ├── __init__.py │ └── ticket.py ├── __init__.py ├── static │ ├── description │ │ └── icon.png │ ├── src │ │ ├── less │ │ │ └── ticket_viewer.less │ │ ├── xml │ │ │ └── ticket_views.xml │ │ └── js │ │ │ ├── models.js │ │ │ └── controllers.js │ └── lib │ │ └── js │ │ └── router.js ├── data │ ├── ir.model.access.csv │ └── ticket_security.xml ├── demo │ └── ticket_demo.xml ├── __manifest__.py └── views │ ├── ticket_views.xml │ └── ticket_templates.xml ├── .gitignore ├── README.md └── LICENSE /ticket_viewer/models/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | from . import ticket 3 | -------------------------------------------------------------------------------- /ticket_viewer/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | from . import ticket 3 | -------------------------------------------------------------------------------- /ticket_viewer/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | from . import models 3 | from . import controllers 4 | -------------------------------------------------------------------------------- /ticket_viewer/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bouvyd/odoo-js-demo/HEAD/ticket_viewer/static/description/icon.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dotfiles 2 | .* 3 | !.gitignore 4 | # compiled python files 5 | *.py[co] 6 | 7 | # various virtualenv 8 | /bin/ 9 | /build/ 10 | /dist/ 11 | /include/ 12 | /lib/ 13 | /man/ 14 | /share/ 15 | /src/ 16 | 17 | -------------------------------------------------------------------------------- /ticket_viewer/data/ir.model.access.csv: -------------------------------------------------------------------------------- 1 | id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 2 | access_demo_ticket_user,Demo Ticket - User,model_demo_ticket,base.group_user,1,1,1,1 3 | access_demo_ticket_portal,Demo Ticket - Portal,model_demo_ticket,base.group_portal,1,1,1,0 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Odoo Experience 2017 - JS Demo 2 | 3 | This repository contains a small demo shown at Odoo Experience 2017. 4 | 5 | This very small app shows how one can use the Odoo JS Framework to have a minimal frontend application up and running in a few minutes. 6 | 7 | To follow the tutorial yourself, you can read the commit messages. 8 | -------------------------------------------------------------------------------- /ticket_viewer/data/ticket_security.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Demo Ticket - Own Ticket on Portal 5 | 6 | [('partner_id', '=', user.partner_id.id)] 7 | 8 | 9 | -------------------------------------------------------------------------------- /ticket_viewer/demo/ticket_demo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Unable to login 5 | 6 | I keep entering every password I know without success :( 7 | 8 | 9 | 10 | 11 | 12 | Unable to work when internet is down 13 | 14 | This is unacceptable. 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ticket_viewer/__manifest__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | { 3 | 'name': 'Ticket Viewer', 4 | 'version': '1.0', 5 | 'author': 'Damien Bouvy', 6 | 'website': 'https://www.damienbouvy.be', 7 | 'summary': 'Demo a WebApp to view tickets online', 8 | 'depends': ['web', 'base_setup', 'bus'], 9 | 'description': """ 10 | Ticket Viewer Demo 11 | ================== 12 | View & submit support tickets online. 13 | Odoo Experience 2017 demo of the Odoo Javascript Framework. 14 | """, 15 | "data": [ 16 | "views/ticket_views.xml", 17 | "views/ticket_templates.xml", 18 | "data/ir.model.access.csv", 19 | "data/ticket_security.xml", 20 | ], 21 | "demo": [ 22 | "demo/ticket_demo.xml", 23 | ], 24 | 'installable': True, 25 | 'application': True, 26 | 'license': 'LGPL-3', 27 | } 28 | -------------------------------------------------------------------------------- /ticket_viewer/controllers/ticket.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | from odoo.addons.bus.controllers.main import BusController 3 | from odoo.http import request, route 4 | 5 | 6 | class TicketController(BusController): 7 | def _poll(self, dbname, channels, last, options): 8 | """Add the relevant channels to the BusController polling.""" 9 | if options.get('demo.ticket'): 10 | channels = list(channels) 11 | ticket_channel = ( 12 | request.db, 13 | 'demo.ticket', 14 | options.get('demo.ticket') 15 | ) 16 | channels.append(ticket_channel) 17 | return super(TicketController, self)._poll(dbname, channels, last, options) 18 | 19 | @route(['/tickets', '/tickets/'], auth='user') 20 | def view_tickets(self, **kwargs): 21 | tickets = request.env['demo.ticket'].search([]) 22 | return request.render('ticket_viewer.ticket_list', {'tickets': tickets}) 23 | -------------------------------------------------------------------------------- /ticket_viewer/models/ticket.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | from odoo import api, fields, models 3 | 4 | 5 | class DemoTicket(models.Model): 6 | _name = 'demo.ticket' 7 | _description = 'Demo Ticket' 8 | 9 | name = fields.Char(required=True) 10 | description = fields.Text() 11 | partner_id = fields.Many2one('res.partner', string='Customer', required=True, default=lambda s: s.env.user.partner_id) 12 | 13 | @api.model 14 | def create(self, vals): 15 | ticket = super(DemoTicket, self).create(vals) 16 | (channel, message) = ((self._cr.dbname, 'demo.ticket', ticket.partner_id.id), ('new_ticket', ticket.id)) 17 | self.env['bus.bus'].sendone(channel, message) 18 | return ticket 19 | 20 | def unlink(self): 21 | notifications = [] 22 | for ticket in self: 23 | notifications.append(((self._cr.dbname, 'demo.ticket', ticket.partner_id.id), ('unlink_ticket', ticket.id))) 24 | self.env['bus.bus'].sendmany(notifications) 25 | return super(DemoTicket, self).unlink() 26 | -------------------------------------------------------------------------------- /ticket_viewer/views/ticket_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tickets 5 | ir.actions.act_window 6 | demo.ticket 7 | tree 8 | 9 | 10 | 11 | demo.ticket.tree 12 | demo.ticket 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Damien Bouvy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ticket_viewer/static/src/less/ticket_viewer.less: -------------------------------------------------------------------------------- 1 | .table-ticket { 2 | th { 3 | font-weight: 600; 4 | } 5 | } 6 | 7 | img.o_user_avatar { 8 | border-radius: 50%; 9 | max-width: 20px !important; 10 | } 11 | 12 | /* With any luck, this should no longer be needed after the v11 freeze 13 | as I am currently trying to have the notification center entirely 14 | untangled from the web client. */ 15 | .o_notification_manager { 16 | width: 300px; 17 | max-width: 100%; 18 | top: @navbar-height; 19 | right: 0; 20 | position: fixed; 21 | 22 | 23 | z-index: 1100; // Bootstrap modal z-index is 1050 24 | 25 | .o_notification { 26 | padding: 0; 27 | margin: 5px 0 0 0; 28 | 29 | opacity: 0; 30 | 31 | background-color: #FCFBEA; 32 | position: relative; 33 | .o_close { 34 | .o-position-absolute(5px, 5px); 35 | color: rgba(0, 0, 0, 0.3); 36 | text-decoration: none; 37 | } 38 | 39 | .o_notification_title { 40 | .o-flex-display(); 41 | .o-align-items(center); 42 | 43 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 44 | padding: 10px 10px 10px 20px; 45 | 46 | font-weight: bold; 47 | 48 | .o_icon { 49 | display: inline-block; 50 | margin-right: 20px; 51 | color: rgba(0, 0, 0, 0.3); 52 | } 53 | } 54 | 55 | .o_notification_content { 56 | padding: 10px; 57 | } 58 | 59 | &.o_error { 60 | color: white; 61 | background-color: #F16567; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /ticket_viewer/views/ticket_templates.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | -------------------------------------------------------------------------------- /ticket_viewer/static/lib/js/router.js: -------------------------------------------------------------------------------- 1 | /* Shamelessy taken form KRASIMIR TSONEV for the purpose of this demo 2 | http://krasimirtsonev.com/blog/article/A-modern-JavaScript-router-in-100-lines-history-api-pushState-hash-url 3 | */ 4 | odoo.define('demo.router', function (require) { 5 | 'use strict'; 6 | 7 | var Router = { 8 | routes: [], 9 | mode: null, 10 | root: '/', 11 | config: function(options) { 12 | this.mode = options && options.mode && options.mode == 'history' 13 | && !!(history.pushState) ? 'history' : 'hash'; 14 | this.root = options && options.root ? '/' + this.clearSlashes(options.root) + '/' : '/'; 15 | return this; 16 | }, 17 | getFragment: function() { 18 | var fragment = ''; 19 | if(this.mode === 'history') { 20 | fragment = this.clearSlashes(decodeURI(location.pathname + location.search)); 21 | fragment = fragment.replace(/\?(.*)$/, ''); 22 | fragment = this.root != '/' ? fragment.replace(this.root, '') : fragment; 23 | } else { 24 | var match = window.location.href.match(/#(.*)$/); 25 | fragment = match ? match[1] : ''; 26 | } 27 | return this.clearSlashes(fragment); 28 | }, 29 | clearSlashes: function(path) { 30 | return path.toString().replace(/\/$/, '').replace(/^\//, ''); 31 | }, 32 | add: function(re, handler) { 33 | if(typeof re == 'function') { 34 | handler = re; 35 | re = ''; 36 | } 37 | this.routes.push({ re: re, handler: handler}); 38 | return this; 39 | }, 40 | remove: function(param) { 41 | for(var i=0, r; i 2 | 3 | 4 |
5 | 22 |
23 |
24 | 25 |
26 |
27 | 28 | 29 | 30 |
31 |

Current Tickets

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
TitleDescription
44 |
45 |

No tickets to see

46 |
47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 |
59 |
60 | 61 | 62 |
63 |
64 | 65 |