summaryrefslogtreecommitdiff
path: root/addons/note
diff options
context:
space:
mode:
Diffstat (limited to 'addons/note')
-rw-r--r--addons/note/__init__.py3
-rw-r--r--addons/note/__manifest__.py5
-rw-r--r--addons/note/controllers/__init__.py4
-rw-r--r--addons/note/controllers/note.py27
-rw-r--r--addons/note/data/mail_activity_data.xml12
-rw-r--r--addons/note/models/__init__.py1
-rw-r--r--addons/note/models/mail_activity.py16
-rw-r--r--addons/note/models/res_users.py27
-rw-r--r--addons/note/static/src/js/systray.js138
-rw-r--r--addons/note/static/src/less/note.less38
-rw-r--r--addons/note/static/src/xml/systray.xml32
-rw-r--r--addons/note/static/tests/systray_tests.js66
-rw-r--r--addons/note/views/mail_activity_views.xml14
-rw-r--r--addons/note/views/note_templates.xml7
14 files changed, 388 insertions, 2 deletions
diff --git a/addons/note/__init__.py b/addons/note/__init__.py
index dc5e6b693d1..3ea0f0cac45 100644
--- a/addons/note/__init__.py
+++ b/addons/note/__init__.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import models
+from . import controllers
+from . import models \ No newline at end of file
diff --git a/addons/note/__manifest__.py b/addons/note/__manifest__.py
index 7a0edd704a9..02cb893ca1d 100644
--- a/addons/note/__manifest__.py
+++ b/addons/note/__manifest__.py
@@ -15,13 +15,18 @@
'data': [
'security/note_security.xml',
'security/ir.model.access.csv',
+ 'data/mail_activity_data.xml',
'data/note_data.xml',
'views/note_views.xml',
'views/note_templates.xml',
+ 'views/mail_activity_views.xml',
],
'demo': [
'data/note_demo.xml',
],
+ 'qweb': [
+ 'static/src/xml/systray.xml',
+ ],
'test': [
],
'installable': True,
diff --git a/addons/note/controllers/__init__.py b/addons/note/controllers/__init__.py
new file mode 100644
index 00000000000..4d2e8b5ad27
--- /dev/null
+++ b/addons/note/controllers/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import note \ No newline at end of file
diff --git a/addons/note/controllers/note.py b/addons/note/controllers/note.py
new file mode 100644
index 00000000000..bdcf2e85059
--- /dev/null
+++ b/addons/note/controllers/note.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import http
+from odoo.http import request
+
+
+class NoteController(http.Controller):
+
+ @http.route('/note/new', type='json', auth='user')
+ def note_new_from_systray(self, note, activity_type_id=None, date_deadline=None):
+ """ Route to create note and their activity directly from the systray """
+ note = request.env['note.note'].create({'memo': note})
+ if date_deadline:
+ activity_values = {
+ 'note': note.memo,
+ 'date_deadline': date_deadline,
+ 'res_model_id': request.env.ref("note.model_note_note").id,
+ 'res_id': note.id,
+ 'note_id': note.id,
+ }
+ if not activity_type_id:
+ activity_type_id = request.env['mail.activity.type'].sudo().search([('category', '=', 'reminder')], limit=1).id
+ if activity_type_id:
+ activity_values['activity_type_id'] = activity_type_id
+ request.env['mail.activity'].create(activity_values)
+ return note.id
diff --git a/addons/note/data/mail_activity_data.xml b/addons/note/data/mail_activity_data.xml
new file mode 100644
index 00000000000..5b2a5e6c905
--- /dev/null
+++ b/addons/note/data/mail_activity_data.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data noupdate="1">
+ <record id="mail_activity_data_reminder" model="mail.activity.type">
+ <field name="name">Reminder</field>
+ <field name="category">reminder</field>
+ <field name="icon">fa-tasks</field>
+ <field name="days">0</field>
+ <field name="sequence">20</field>
+ </record>
+ </data>
+</odoo>
diff --git a/addons/note/models/__init__.py b/addons/note/models/__init__.py
index 5c651fca9a0..db6de6e3c7f 100644
--- a/addons/note/models/__init__.py
+++ b/addons/note/models/__init__.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from . import mail_activity
from . import note
from . import res_users
diff --git a/addons/note/models/mail_activity.py b/addons/note/models/mail_activity.py
new file mode 100644
index 00000000000..67f7ea82966
--- /dev/null
+++ b/addons/note/models/mail_activity.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models, fields
+
+
+class MailActivityType(models.Model):
+ _inherit = "mail.activity.type"
+
+ category = fields.Selection(selection_add=[('reminder', 'Reminder')])
+
+
+class MailActivity(models.Model):
+ _inherit = "mail.activity"
+
+ note_id = fields.Many2one('note.note', string="Related Note", ondelete='cascade')
diff --git a/addons/note/models/res_users.py b/addons/note/models/res_users.py
index cfeba15922f..be218c27b28 100644
--- a/addons/note/models/res_users.py
+++ b/addons/note/models/res_users.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import api, models
+from odoo import api, models, modules, _
class Users(models.Model):
@@ -19,3 +19,28 @@ class Users(models.Model):
if stage:
stage.sudo().copy(default={'user_id': user.id})
return user
+
+ @api.model
+ def activity_user_count(self):
+ """ If user have not scheduled any note, it will not appear in activity menu.
+ Making note activity always visible with number of notes on label. If there is no notes,
+ activity menu not visible for note.
+ """
+ activities = super(Users, self).activity_user_count()
+ notes_count = self.env['note.note'].search_count([('user_id', '=', self.env.uid), ('open', '=', True)])
+ if notes_count:
+ note_index = next((index for (index, a) in enumerate(activities) if a["model"] == "note.note"), None)
+ note_label = ("%s (%d)") % (_('Notes'), notes_count)
+ if note_index is not None:
+ activities[note_index]['name'] = note_label
+ else:
+ activities.append({
+ 'name': note_label,
+ 'model': 'note.note',
+ 'icon': modules.module.get_module_icon(self.env['note.note']._original_module),
+ 'total_count': 0,
+ 'today_count': 0,
+ 'overdue_count': 0,
+ 'planned_count': 0
+ })
+ return activities
diff --git a/addons/note/static/src/js/systray.js b/addons/note/static/src/js/systray.js
new file mode 100644
index 00000000000..54a2fe6bdd1
--- /dev/null
+++ b/addons/note/static/src/js/systray.js
@@ -0,0 +1,138 @@
+odoo.define('note.systray', function (require) {
+"use strict";
+
+var core = require('web.core');
+var ActivityMenu = require('mail.systray').ActivityMenu;
+var datepicker = require('web.datepicker');
+
+var _t = core._t;
+
+ActivityMenu.include({
+ events: _.extend({}, ActivityMenu.prototype.events, {
+ 'click .o_note_show': '_onAddNoteClick',
+ 'click .o_note_save': '_onNoteSaveClick',
+ 'click .o_note_set_datetime': '_onNoteDateTimeSetClick',
+ 'keydown input.o_note_input': '_onNoteInputKeyDown',
+ 'click .o_note': '_onNewNoteClick',
+ }),
+ //--------------------------------------------------
+ // Private
+ //--------------------------------------------------
+ /**
+ * Moving notes at first place
+ * @override
+ */
+ _getActivityData: function () {
+ var self = this;
+ return this._super.apply(this, arguments).then(function() {
+ var reminderIndex = _.findIndex(self.activities, function (val) { return val.model == 'note.note'; });
+ if (reminderIndex > 0) {
+ self.activities.splice(0, 0, self.activities.splice(reminderIndex, 1)[0]);
+ }
+ });
+ },
+ /**
+ * Save the note to database using datepicker date and field as note
+ * @private
+ */
+ _saveNote: function () {
+ var note = this.$('.o_note_input').val().trim();
+ if (! note) {
+ return;
+ }
+ var params = {'note': note};
+ var noteDateTime = this.noteDateTimeWidget.getValue();
+ if (noteDateTime) {
+ params = _.extend(params, {'date_deadline': noteDateTime});
+ }
+ this.$('.o_note_show').removeClass('hidden');
+ this.$('.o_note').addClass('hidden');
+ this._rpc({
+ route: '/note/new',
+ params: params,
+ }).then(this._updateActivityPreview.bind(this));
+ },
+ //-----------------------------------------
+ // Handlers
+ //-----------------------------------------
+ /**
+ * @override
+ */
+ _onActivityFilterClick: function (ev) {
+ var $el = $(ev.currentTarget);
+ if(!$el.hasClass("o_note")) {
+ var data = _.extend({}, $el.data(), $(ev.target).data());
+ if (data.res_model === "note.note" && data.filter === "my") {
+ this.do_action({
+ type: 'ir.actions.act_window',
+ name: data.model_name,
+ res_model: data.res_model,
+ views: [[false, 'kanban'], [false, 'form'], [false, 'list']]
+ });
+ } else {
+ this._super.apply(this, arguments);
+ }
+ }
+ },
+ /**
+ * When add new note button clicked, toggling quick note create view inside
+ * Systray activity view
+ *
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onAddNoteClick: function (ev) {
+ ev.stopPropagation();
+ if (!this.noteDateTimeWidget){
+ this.noteDateTimeWidget = new datepicker.DateWidget(this, {useCurrent: true});
+ }
+ this.noteDateTimeWidget.appendTo(this.$('.o_note_datetime'));
+ this.noteDateTimeWidget.$input.attr('placeholder', _t("Today"));
+ this.$('.o_note_show, .o_note').toggleClass('hidden');
+ this.$('.o_note_input').val('').focus();
+
+ },
+ /**
+ * When focusing on input for new quick note systerm tray must be open.
+ * Preventing to close
+ *
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onNewNoteClick: function (ev) {
+ ev.stopPropagation();
+ },
+ /**
+ * Opens datetime picker for note.
+ * Quick FIX due to no option for set custom icon instead of caret in datepicker.
+ *
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onNoteDateTimeSetClick: function (ev) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ this.noteDateTimeWidget.$input.click();
+ },
+ /**
+ * Saving note (quick create) and updating activity preview
+ *
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onNoteSaveClick: function (ev) {
+ this._saveNote();
+ },
+ /**
+ * Handling Enter key for quick create note.
+ *
+ * @private
+ * @param {KeyboardEvent} ev
+ */
+ _onNoteInputKeyDown: function (ev) {
+ if (ev.which === $.ui.keyCode.ENTER) {
+ this._saveNote();
+ }
+ },
+});
+});
diff --git a/addons/note/static/src/less/note.less b/addons/note/static/src/less/note.less
index c92c5cd0500..926e7ddb913 100644
--- a/addons/note/static/src/less/note.less
+++ b/addons/note/static/src/less/note.less
@@ -23,3 +23,41 @@
}
}
}
+
+// Quick create notes from systray
+.o_note.o_mail_channel_preview {
+ background-color: white;
+ .o_channel_info {
+ .o_channel_title {
+ .o_channel_name {
+ .o-flex(1, 1, 100%);
+ }
+ }
+ .o_note_input_box {
+ .o-flex-display();
+ p {
+ .o-flex(1, 1, auto);
+ }
+ }
+ .o_note_save {
+ font-size: 11px;
+ font-weight: bold;
+ }
+ }
+ .o_note_input {
+ border: none;
+ }
+ .o_note_datetime {
+ .o_datepicker {
+ .o_datepicker_input {
+ float: right;
+ text-align: right;
+ border: none;
+ font-size: 11px;
+ }
+ .o_datepicker_button {
+ display: none;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/addons/note/static/src/xml/systray.xml b/addons/note/static/src/xml/systray.xml
new file mode 100644
index 00000000000..4b2052c3b85
--- /dev/null
+++ b/addons/note/static/src/xml/systray.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates>
+ <t t-extend="mail.chat.ActivityMenuPreview">
+ <t t-jquery="t[t-foreach*='activities'][t-as*='activity']" t-operation="after">
+ <div class="o_note_show">
+ <a class="btn btn-block text-center">Add new note</a>
+ </div>
+ <div class="o_note o_mail_channel_preview hidden">
+ <div class="o_mail_channel_image o_mail_channel_app">
+ <img src="/note/static/description/icon.png"/>
+ </div>
+ <div class="o_channel_info">
+ <div class="o_channel_title">
+ <span class="o_channel_name"><strong>Add a note</strong></span>
+ <div class="o_note_datetime"/>
+ <span class="ml4">
+ <a class="o_note_set_datetime text-muted">
+ <span class="fa fa-clock-o"/>
+ </a>
+ </span>
+ </div>
+ <div class="o_note_input_box">
+ <p><input class="o_note_input" type="text" placeholder="Remember..." /></p>
+ <span class="ml8 mr4">
+ <a class="o_note_save">SAVE</a>
+ </span>
+ </div>
+ </div>
+ </div>
+ </t>
+ </t>
+</templates> \ No newline at end of file
diff --git a/addons/note/static/tests/systray_tests.js b/addons/note/static/tests/systray_tests.js
new file mode 100644
index 00000000000..2b07bf85757
--- /dev/null
+++ b/addons/note/static/tests/systray_tests.js
@@ -0,0 +1,66 @@
+odoo.define('note.systray_tests', function (require) {
+"use strict";
+
+var ChatManager = require('mail.ChatManager');
+var systray = require('mail.systray');
+var testUtils = require('web.test_utils');
+var createBusService = require('mail.testUtils').createBusService;
+
+QUnit.module('note', {}, function () {
+
+QUnit.module("ActivityMenu", {
+ beforeEach: function () {
+ this.services = [ChatManager, createBusService()];
+ this.data = {
+ 'mail.activity.menu': {
+ records: [],
+ },
+ };
+ }
+});
+
+QUnit.test('note activity menu widget: create note from activity menu', function (assert) {
+ assert.expect(8);
+ var self = this;
+ var activityMenu = new systray.ActivityMenu();
+ testUtils.addMockEnvironment(activityMenu, {
+ services: this.services,
+ mockRPC: function (route, args) {
+ if (args.method === 'activity_user_count') {
+ return $.when(self.data['mail.activity.menu']['records']);
+ }
+ if (route === '/note/new') {
+ return $.when();
+ }
+ return this._super(route, args);
+ },
+ });
+ activityMenu.appendTo($('#qunit-fixture'));
+ assert.ok(activityMenu.$el.hasClass('o_mail_navbar_item'), 'should be the instance of widget');
+
+ // toggle quick create for note
+ var step = 1;
+ activityMenu.$('.dropdown-toggle').click();
+ assert.strictEqual(activityMenu.$('.o_note_show').hasClass("hidden"), false, 'ActivityMenu should have Add new note CTA');
+ activityMenu.$('.o_note_show').click();
+ assert.strictEqual(activityMenu.$('.o_note_show').hasClass("hidden"), true, 'ActivityMenu should hide CTA when entering a new note');
+ assert.strictEqual(activityMenu.$('.o_note').hasClass("hidden"), false, 'ActivityMenu should display input for new note');
+
+ // creating quick note without date
+ activityMenu.$("input.o_note_input").val("New Note");
+ activityMenu.$(".o_note_save").click();
+ assert.strictEqual(activityMenu.$('.o_note_show').hasClass("hidden"), false, 'ActivityMenu add note button should be displayed');
+ assert.strictEqual(activityMenu.$('.o_note').hasClass("hidden"), true, 'ActivityMenu add note input should be hidden');
+
+ // creating quick note with date
+ step = 2;
+ activityMenu.$('.o_note_show').click();
+ activityMenu.$('input.o_note_input').val("New Note");
+ activityMenu.$('.o_note_set_datetime').click();
+ activityMenu.$(".o_note_save").click();
+ assert.strictEqual(activityMenu.$('.o_note_show').hasClass("hidden"), false, 'ActivityMenu add note button should be displayed');
+ assert.strictEqual(activityMenu.$('.o_note').hasClass("hidden"), true, 'ActivityMenu add note input should be hidden');
+ activityMenu.destroy();
+});
+});
+});
diff --git a/addons/note/views/mail_activity_views.xml b/addons/note/views/mail_activity_views.xml
new file mode 100644
index 00000000000..bc980423e3f
--- /dev/null
+++ b/addons/note/views/mail_activity_views.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<odoo>
+
+ <record id="mail_activity_type_view_form" model="ir.ui.view">
+ <field name="name">mail.activity.type.form.inherit.note</field>
+ <field name="model">mail.activity.type</field>
+ <field name="inherit_id" ref="mail.mail_activity_type_view_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='category']" position="attributes">
+ <attribute name="invisible">0</attribute>
+ </xpath>
+ </field>
+ </record>
+</odoo> \ No newline at end of file
diff --git a/addons/note/views/note_templates.xml b/addons/note/views/note_templates.xml
index 3b6c3e1acc4..c80d00d84e0 100644
--- a/addons/note/views/note_templates.xml
+++ b/addons/note/views/note_templates.xml
@@ -3,6 +3,13 @@
<template id="assets_backend" inherit_id="web.assets_backend">
<xpath expr="link[last()]" position="after">
<link rel="stylesheet" type="text/less" href="/note/static/src/less/note.less"/>
+ <script type="text/javascript" src="/note/static/src/js/systray.js"></script>
+ </xpath>
+ </template>
+
+ <template id="qunit_suite" name="note_tests" inherit_id="web.qunit_suite">
+ <xpath expr="//t[@t-set='head']" position="inside">
+ <script type="text/javascript" src="/note/static/tests/systray_tests.js"></script>
</xpath>
</template>
</odoo>