summaryrefslogtreecommitdiff
path: root/addons/website_slides/static/src
diff options
context:
space:
mode:
authorMantavya Gajjar <mga@openerp.com>2014-08-21 17:46:21 +0530
committerThibault Delavallée <tde@openerp.com>2015-01-16 16:01:59 +0100
commit51be5bbfb38db784cb3406877cc0e8806b1bf55a (patch)
tree350f81248ccd5a7d1cfc00521333ef8642847dda /addons/website_slides/static/src
parentcec43b93d24429385af3de73ff05b2a5ebf4e122 (diff)
[ADD] website_slides: new website module to handle presentations, documents,
videos, infographics. Some kind of slideshare clone, to summarize. Featuring : - channel management: classify slides (presentations, videos, documents, ...) and tune their access rights for visibility, uploading and downloading - full branding new shiny website module ! - automatic fetch of youtube and google docs information based on URL - statistics: views, likes, interfaced with messaging, ... - embed mechanism to embed slides in other websites Courtesy of RD India, notably Mantavya Gajjar, Ravi Gadhia, Vijay Patel, Pariket Trivedi Courtesy of Use Merge Team, notably Jerome Maes and Richard Mathot Let us not forget our usability testing teams that tested it quite a lot of time. Special thanks to Martin Trigaux that did nothing.
Diffstat (limited to 'addons/website_slides/static/src')
-rw-r--r--addons/website_slides/static/src/img/Open Days 2014 |Infographic.pngbin0 -> 1275540 bytes
-rw-r--r--addons/website_slides/static/src/img/document.pngbin0 -> 7414 bytes
-rw-r--r--addons/website_slides/static/src/img/keynote.pdfbin0 -> 254685 bytes
-rw-r--r--addons/website_slides/static/src/img/keynote.pngbin0 -> 49381 bytes
-rw-r--r--addons/website_slides/static/src/img/odoo-cms-comparision.pdfbin0 -> 2204482 bytes
-rw-r--r--addons/website_slides/static/src/img/odoo-cms-comparision.pngbin0 -> 61537 bytes
-rw-r--r--addons/website_slides/static/src/js/slides.js184
-rw-r--r--addons/website_slides/static/src/js/slides_embed.js197
-rw-r--r--addons/website_slides/static/src/js/slides_upload.js357
-rw-r--r--addons/website_slides/static/src/less/website_slides.less156
-rw-r--r--addons/website_slides/static/src/xml/website_slides.xml75
11 files changed, 969 insertions, 0 deletions
diff --git a/addons/website_slides/static/src/img/Open Days 2014 |Infographic.png b/addons/website_slides/static/src/img/Open Days 2014 |Infographic.png
new file mode 100644
index 00000000000..f022583758e
--- /dev/null
+++ b/addons/website_slides/static/src/img/Open Days 2014 |Infographic.png
Binary files differ
diff --git a/addons/website_slides/static/src/img/document.png b/addons/website_slides/static/src/img/document.png
new file mode 100644
index 00000000000..68ebd70706c
--- /dev/null
+++ b/addons/website_slides/static/src/img/document.png
Binary files differ
diff --git a/addons/website_slides/static/src/img/keynote.pdf b/addons/website_slides/static/src/img/keynote.pdf
new file mode 100644
index 00000000000..ab16e7c28bd
--- /dev/null
+++ b/addons/website_slides/static/src/img/keynote.pdf
Binary files differ
diff --git a/addons/website_slides/static/src/img/keynote.png b/addons/website_slides/static/src/img/keynote.png
new file mode 100644
index 00000000000..fa1eb12359d
--- /dev/null
+++ b/addons/website_slides/static/src/img/keynote.png
Binary files differ
diff --git a/addons/website_slides/static/src/img/odoo-cms-comparision.pdf b/addons/website_slides/static/src/img/odoo-cms-comparision.pdf
new file mode 100644
index 00000000000..0861f3e6cd1
--- /dev/null
+++ b/addons/website_slides/static/src/img/odoo-cms-comparision.pdf
Binary files differ
diff --git a/addons/website_slides/static/src/img/odoo-cms-comparision.png b/addons/website_slides/static/src/img/odoo-cms-comparision.png
new file mode 100644
index 00000000000..0069277b71c
--- /dev/null
+++ b/addons/website_slides/static/src/img/odoo-cms-comparision.png
Binary files differ
diff --git a/addons/website_slides/static/src/js/slides.js b/addons/website_slides/static/src/js/slides.js
new file mode 100644
index 00000000000..d758d814639
--- /dev/null
+++ b/addons/website_slides/static/src/js/slides.js
@@ -0,0 +1,184 @@
+/*global $, openerp, _, PDFJS */
+$(document).ready(function () {
+
+ "use strict";
+
+ var website = openerp.website;
+ var _t = openerp._t;
+ var widget_parent = $('body');
+
+ website.slide = {};
+ website.slide.page_widgets = {};
+
+ $("timeago.timeago").each(function (index, el) {
+ var datetime = $(el).attr('datetime'),
+ datetime_obj = openerp.str_to_datetime(datetime),
+ // if presentation 7 days, 24 hours, 60 min, 60 second, 1000 millis old(one week)
+ // then return fix formate string else timeago
+ display_str = "";
+ if (datetime_obj && new Date().getTime() - datetime_obj.getTime() > 7 * 24 * 60 * 60 * 1000) {
+ display_str = datetime_obj.toDateString();
+ } else {
+ display_str = $.timeago(datetime_obj);
+ }
+ $(el).text(display_str);
+ });
+
+ /*
+ * Like/Dislike Buttons Widget
+ */
+ website.slide.LikeButton = openerp.Widget.extend({
+ setElement: function($el){
+ this._super.apply(this, arguments);
+ this.$el.on('click', this, _.bind(this.apply_action, this));
+ },
+ apply_action: function(ev){
+ var button = $(ev.currentTarget);
+ var slide_id = button.data('slide-id');
+ var user_id = button.data('user-id');
+ var is_public = button.data('public-user');
+ var href = button.data('href');
+ if(is_public){
+ this.popover_alert(button, _.str.sprintf(_t('Please <a href="/web?redirect=%s">login</a> to vote this slide'), (document.URL)));
+ }else{
+ var target = button.find('.fa');
+ if (localStorage['slide_vote_' + slide_id] !== user_id.toString()) {
+ openerp.jsonRpc(href, 'call', {slide_id: slide_id}).then(function (data) {
+ target.text(data);
+ localStorage['slide_vote_' + slide_id] = user_id;
+ });
+ } else {
+ this.popover_alert(button, _t('You have already voted for this slide'));
+ }
+ }
+ },
+ popover_alert: function($el, message){
+ $el.popover({
+ trigger: 'focus',
+ placement: 'bottom',
+ container: 'body',
+ html: true,
+ content: function(){
+ return message;
+ }
+ }).popover('toggle');
+ },
+ });
+
+ website.slide.page_widgets['likeButton'] = new website.slide.LikeButton(widget_parent).setElement($('.oe_slide_js_like'));
+ website.slide.page_widgets['dislikeButton'] = new website.slide.LikeButton(widget_parent).setElement($('.oe_slide_js_unlike'));
+
+ /*
+ * Embedded Code Widget
+ */
+ website.slide.SlideSocialEmbed = openerp.Widget.extend({
+ events: {
+ 'change input' : 'change_page',
+ },
+ init: function(parent, max_page){
+ this._super(parent);
+ this.max_page = max_page || false;
+ },
+ change_page: function(ev){
+ ev.preventDefault();
+ var input = this.$('input');
+ var page = parseInt(input.val());
+ if (this.max_page && !(page > 0 && page <= this.max_page)) {
+ page = 1;
+ }
+ this.update_embedded_code(page);
+ },
+ update_embedded_code: function(page){
+ var embed_input = this.$('.slide_embed_code');
+ var new_code = embed_input.val().replace(/(page=).*?([^\d]+)/, '$1' + page + '$2');
+ embed_input.val(new_code);
+ },
+ });
+
+ $('iframe').ready(function() {
+ // TODO : make it work. For now, once the iframe is loaded, the value of #page_count is
+ // still now set (the pdf is still loading)
+ var max_page = $('iframe').contents().find('#page_count').val();
+ var slide_social_embed = new website.slide.SlideSocialEmbed($(this), max_page).setElement($('.oe_slide_js_embed_code_widget'));
+ });
+
+
+ /*
+ * Send by email Widget
+ */
+ website.slide.ShareMail = openerp.Widget.extend({
+ events: {
+ 'click button' : 'send_mail',
+ },
+ send_mail: function(ev){
+ var self = this;
+ var input = this.$('input');
+ var slide_id = this.$('button').data('slide-id');
+ if(input.val() && input[0].checkValidity()){
+ this.$el.removeClass('has-error');
+ openerp.jsonRpc('/slides/slide/send_share_email', 'call', {
+ slide_id: slide_id,
+ email: input.val(),
+ }).then(function () {
+ self.$el.html($('<div class="alert alert-info" role="alert"><strong>Thank you!</strong> Mail has been sent.</div>'));
+ });
+ }else{
+ this.$el.addClass('has-error');
+ input.focus();
+ }
+ },
+ });
+
+ website.slide.page_widgets['share_mail'] = new website.slide.ShareMail(widget_parent).setElement($('.oe_slide_js_share_email'));
+
+ /*
+ * Social Sharing Statistics Widget
+ */
+ if ($('div#statistic').length) {
+ var socialgatter = function (app_url, url, callback) {
+ $.ajax({
+ url: app_url + url,
+ dataType: 'jsonp',
+ success: callback
+ });
+ };
+ var current_url = window.location.origin + window.location.pathname;
+ socialgatter('https://www.linkedin.com/countserv/count/share?url=', current_url, function (data) {
+ $('#linkedin-badge').text(data.count || 0);
+ $('#total-share').text(parseInt($('#total-share').text()) + parseInt($('#linkedin-badge').text()));
+ });
+ socialgatter('https://cdn.api.twitter.com/1/urls/count.json?url=', current_url, function (data) {
+ $('#twitter-badge').text(data.count || 0);
+ $('#total-share').text(parseInt($('#total-share').text()) + parseInt($('#twitter-badge').text()));
+ });
+ socialgatter('https://graph.facebook.com/?id=', current_url, function (data) {
+ $('#facebook-badge').text(data.shares || 0);
+ $('#total-share').text(parseInt($('#total-share').text()) + parseInt($('#facebook-badge').text()));
+ });
+
+ $.ajax({
+ url: 'https://clients6.google.com/rpc',
+ type: "POST",
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify([{
+ "method": "pos.plusones.get",
+ "id": "p",
+ "params": {
+ "nolog": true,
+ "id": current_url,
+ "source": "widget",
+ "userId": "@viewer",
+ "groupId": "@self"
+ },
+ // TDE NOTE: should there be a key here ?
+ "jsonrpc": "2.0",
+ "apiVersion": "v1"
+ }]),
+ success: function (data) {
+ $('#google-badge').text(data[0].result.metadata.globalCounts.count || 0);
+ $('#total-share').text(parseInt($('#total-share').text()) + parseInt($('#google-badge').text()));
+ },
+ });
+ }
+});
diff --git a/addons/website_slides/static/src/js/slides_embed.js b/addons/website_slides/static/src/js/slides_embed.js
new file mode 100644
index 00000000000..69c94f7e94c
--- /dev/null
+++ b/addons/website_slides/static/src/js/slides_embed.js
@@ -0,0 +1,197 @@
+/*global $, _, PDFJS, PDFSlidesViewer, Bootstrap */
+
+/**
+This file is a minimal version of the PDFViewer widget.
+It is NOT use in the website_slides module, but it is
+called when embedding a slide/video/document.
+This code can depend on pdf.js, JQuery and Bootstrap
+(see website_slides.slide_embed_assets bundle, in website_slides_embed.xml)
+**/
+
+$(document).ready(function () {
+
+ if($('#PDFViewer') && $('#PDFViewerCanvas')){ // check if presentation only
+
+ // define embedded viewer (minimal object of the website.slide.PDFViewer widget)
+ var EmbeddedViewer = function($viewer){
+ var self = this;
+ this.viewer = $viewer;
+ this.slide_url = $viewer.find('#PDFSlideViewer').data('slideurl');
+ this.slide_id = $viewer.find('#PDFSlideViewer').data('slideid');
+ this.defaultpage = parseInt($viewer.find('#PDFSlideViewer').data('defaultpage'));
+ this.canvas = $viewer.find('canvas')[0];
+
+ this.pdf_viewer = new PDFSlidesViewer(this.slide_url, this.canvas, true);
+ this.pdf_viewer.loadDocument().then(function(file_content){
+ self.on_loaded_file();
+ });
+ }
+ EmbeddedViewer.prototype.__proto__ = {
+ // jquery inside the object (like openerp.Widget)
+ $: function(selector){
+ return this.viewer.find($(selector));
+ },
+ // post process action (called in '.then()')
+ on_loaded_file: function(){
+ this.$('canvas').show();
+ this.$('#page_count').text(this.pdf_viewer.pdf_page_total);
+ this.$('#PDFViewerLoader').hide();
+ // init first page to display
+ var initpage = this.defaultpage;
+ var pageNum = (initpage > 0 && initpage <= this.pdf_viewer.pdf_page_total)? initpage : 1;
+ this.render_page(pageNum);
+ },
+ on_rendered_page: function(page_number){
+ if(page_number){
+ this.$('#page_number').val(page_number);
+ }
+ },
+ // page switching
+ render_page: function (page_number) {
+ this.pdf_viewer.renderPage(page_number).then(this.on_rendered_page);
+ },
+ change_page: function (ev) {
+ var page_asked = parseInt(this.$('#page_number').val(), 10);
+ if(1 <= page_asked && page_asked <= this.pdf_viewer.pdf_page_total){
+ this.pdf_viewer.changePage(page_asked).then(this.on_rendered_page);
+ }else{
+ // if page number out of range, reset the page_counter to the actual page
+ this.$('#page_number').val(this.pdf_viewer.pdf_page_current);
+ }
+ },
+ next: function (ev) {
+ var self = this;
+ this.pdf_viewer.nextPage().then(function(page_num){
+ if(page_num){
+ self.on_rendered_page(page_num);
+ }else{
+ if(self.pdf_viewer.pdf){ // avoid display suggestion when pdf is not loaded yet
+ self.display_suggested_slides();
+ }
+ }
+ });
+ },
+ previous: function (ev) {
+ var self = this;
+ this.pdf_viewer.previousPage().then(function(page_num){
+ if(page_num){
+ self.on_rendered_page(page_num);
+ }
+ self.$("#slide_suggest").hide();
+ });
+ },
+ first: function (ev) {
+ var self = this;
+ this.pdf_viewer.firstPage().then(function(page_num){
+ self.on_rendered_page(page_num);
+ self.$("#slide_suggest").hide();
+ });
+ },
+ last: function (ev) {
+ var self = this;
+ this.pdf_viewer.lastPage().then(function(page_num){
+ self.on_rendered_page(page_num);
+ self.$("#slide_suggest").hide();
+ });
+ },
+ // full screen mode
+ fullscreen: function (ev) {
+ this.pdf_viewer.toggleFullScreen();
+ },
+ // display suggestion displayed after last slide
+ display_suggested_slides: function () {
+ this.$("#slide_suggest").show();
+ },
+ };
+
+ // embedded pdf viewer
+ embedded_viewer = new EmbeddedViewer($('#PDFViewer'));
+
+ // bind the actions
+ $('#previous').on('click', function(){
+ embedded_viewer.previous();
+ });
+ $('#next').on('click', function(){
+ embedded_viewer.next();
+ });
+ $('#first').on('click', function(){
+ embedded_viewer.first();
+ });
+ $('#last').on('click', function(){
+ embedded_viewer.last();
+ });
+ $('#page_number').on('change', function(){
+ embedded_viewer.change_page();
+ });
+ $('#fullscreen').on('click',function(){
+ embedded_viewer.fullscreen();
+ });
+
+ // switching slide with keyboard
+ $(document).keydown(function (ev) {
+ if (ev.keyCode === 37) {
+ embedded_viewer.previous();
+ }
+ if (ev.keyCode === 39) {
+ embedded_viewer.next();
+ }
+ });
+
+ // display the option panels
+ $('.oe_slide_js_embed_option_link').on('click', function (ev) {
+ ev.preventDefault();
+ var toggleDiv = $(this).data('slide-option-id');
+ $('.oe_slide_embed_option').not(toggleDiv).each(function() {
+ $(this).hide();
+ });
+ $(toggleDiv).slideToggle();
+ });
+
+ // animation for the suggested slides
+ $('.oe_slides_suggestion_media').hover(
+ function () {
+ $(this).find('.oe_slides_suggestion_caption').stop().slideDown(250);
+ },
+ function () {
+ $(this).find('.oe_slides_suggestion_caption').stop().slideUp(250);
+ }
+ );
+
+ // embed widget page selector
+ $('.oe_slide_js_embed_code_widget input').on('change', function (e) {
+ var page = parseInt($(this).val());
+ if (!(page > 0 && page <= embedded_viewer.pdf_viewer.pdf_page_total)) {
+ page = 1;
+ }
+ var actual_code = embedded_viewer.$('.slide_embed_code').val();
+ var new_code = actual_code.replace(/(page=).*?([^\d]+)/, '$1' + page + '$2');
+ embedded_viewer.$('.slide_embed_code').val(new_code);
+ });
+
+ // To avoid create a dependancy to openerpframework.js, we use JQuery AJAX to post data instead of openerp.jsonRpc
+ $('.oe_slide_js_share_email button').on('click', function () {
+ var widget = $('.oe_slide_js_share_email');
+ var input = widget.find('input');
+ var slide_id = widget.find('button').data('slide-id');
+ if(input.val() && input[0].checkValidity()){
+ widget.removeClass('has-error');
+ $.ajax({
+ type: "POST",
+ dataType: 'json',
+ url: '/slides/slide/send_share_email',
+ contentType: "application/json; charset=utf-8",
+ data: JSON.stringify({'jsonrpc': "2.0", 'method': "call", "params": {'slide_id': slide_id, 'email': input.val()}}),
+ success: function (data) {
+ widget.html($('<div class="alert alert-info" role="alert"><strong>Thank you!</strong> Mail has been sent.</div>'));
+ },
+ error: function(data){
+ console.log("ERROR ", data);
+ }
+ });
+ }else{
+ widget.addClass('has-error');
+ input.focus();
+ }
+ });
+ }
+}); \ No newline at end of file
diff --git a/addons/website_slides/static/src/js/slides_upload.js b/addons/website_slides/static/src/js/slides_upload.js
new file mode 100644
index 00000000000..0ffed1ccb0e
--- /dev/null
+++ b/addons/website_slides/static/src/js/slides_upload.js
@@ -0,0 +1,357 @@
+/*global $, openerp, _, PDFJS */
+$(document).ready(function () {
+
+ "use strict";
+
+ var website = openerp.website;
+ var _t = openerp._t;
+
+ website.slide.template = website.add_template_file('/website_slides/static/src/xml/website_slides.xml');
+
+ website.slide.Dialog = openerp.Widget.extend({
+ template: 'website.slide.upload',
+ events: {
+ 'hidden.bs.modal': 'destroy',
+ 'click button.save': 'save',
+ 'click button[data-dismiss="modal"]': 'cancel',
+ 'change input#upload': 'slide_upload',
+ 'change input#url': 'slide_url',
+ 'click .list-group-item': function (ev) {
+ this.$('.list-group-item').removeClass('active');
+ $(ev.target).closest('li').addClass('active');
+ }
+ },
+ init: function (el, channel_id) {
+ this._super(el, channel_id);
+ this.channel_id = parseInt(channel_id, 10);
+ this.file = {};
+ this.index_content = "";
+ },
+ start: function () {
+ this.$el.modal({
+ backdrop: 'static'
+ });
+ this.set_category_id();
+ this.set_tag_ids();
+ },
+ slide_url: function (ev) {
+ var self = this,
+ value = {
+ 'url': $(ev.target).val(),
+ 'channel_id': self.channel_id
+ };
+ this.$('.alert-warning').remove();
+ this.is_valid_url = false;
+ this.$('.save').button('loading');
+ openerp.jsonRpc('/slides/dialog_preview/', 'call', value).then(function (data) {
+ self.$('.save').button('reset');
+ if (data.error) {
+ self.display_alert(data.error);
+ } else {
+ self.$("#slide-image").attr("src", data.url_src);
+ self.$('#name').val(data.title);
+ self.$('#description').val(data.description);
+ self.is_valid_url = true;
+ }
+ });
+ },
+ check_unique_slide: function (file_name) {
+ var self = this;
+ return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
+ model: 'slide.slide',
+ method: 'search_count',
+ args: [[['channel_id', '=', self.channel_id], ['name', '=', file_name]]],
+ kwargs: {}
+ });
+ },
+ slide_upload: function (ev) {
+ var self = this,
+ file = ev.target.files[0],
+ is_image = /^image\/.*/.test(file.type),
+ loaded = false;
+ this.file.name = file.name;
+ this.file.type = file.type;
+ if (!(is_image || this.file.type === 'application/pdf')) {
+ this.display_alert(_t("Invalid file type. Please select pdf or image file"));
+ this.reset_file();
+ return;
+ }
+ if (file.size / 1024 / 1024 > 15) {
+ this.display_alert(_t("File is too big. Please select file upto 15MB"));
+ this.reset_file();
+ return;
+ }
+ this.$('.alert-warning').remove();
+ var BinaryReader = new FileReader();
+ // file read as DataURL
+ BinaryReader.readAsDataURL(file);
+ BinaryReader.onloadend = function (upload) {
+ var buffer = upload.target.result;
+ if (is_image) {
+ self.$("#slide-image").attr("src", buffer);
+ }
+ buffer = buffer.split(',')[1];
+ self.file.data = buffer;
+ };
+
+ if (file.type === 'application/pdf') {
+ var ArrayReader = new FileReader();
+ this.$('.save').button('loading');
+ // file read as ArrayBuffer for PDFJS get_Document API
+ ArrayReader.readAsArrayBuffer(file);
+ ArrayReader.onload = function (evt) {
+ var buffer = evt.target.result;
+ var passwordNeeded = function () {
+ self.display_alert(_t("You can not upload password protected file."));
+ self.reset_file();
+ self.$('.save').button('reset');
+ };
+ PDFJS.getDocument(new Uint8Array(buffer), null, passwordNeeded).then(function getPdf(pdf) {
+ pdf.getPage(1).then(function getFirstPage(page) {
+ var scale = 1;
+ var viewport = page.getViewport(scale);
+ var canvas = document.getElementById('data_canvas');
+ var context = canvas.getContext('2d');
+ canvas.height = viewport.height;
+ canvas.width = viewport.width;
+ // Render PDF page into canvas context
+ page.render({
+ canvasContext: context,
+ viewport: viewport
+ }).then(function () {
+ var image_data = self.$('#data_canvas')[0].toDataURL();
+ self.$("#slide-image").attr("src", image_data);
+ if (loaded) {
+ self.$('.save').button('reset');
+ }
+ loaded = true;
+
+ });
+ });
+ var maxPages = pdf.pdfInfo.numPages;
+ var page, j;
+ self.index_content = "";
+ for (j = 1; j <= maxPages; j += 1) {
+ page = pdf.getPage(j);
+ page.then(function (page_obj) {
+ var page_number = page_obj.pageIndex + 1;
+ page_obj.getTextContent().then(function (data) {
+ var page_content = '';
+ _.each(data.items, function (obj) {
+ page_content = page_content + obj.str + " ";
+ });
+ self.index_content = self.index_content + page_number + ". " + page_content + '\n';
+ if (maxPages === page_number) {
+ if (loaded) {
+ self.$('.save').button('reset');
+ }
+ loaded = true;
+ }
+ });
+ });
+ }
+ });
+ };
+ }
+
+ var input = file.name;
+ var input_val = input.substr(0, input.lastIndexOf('.')) || input;
+ this.check_unique_slide(input_val).then(function (exist) {
+ if (exist) {
+ var message = _t("Channel contains the given title, please change before Save or Publish.");
+ self.display_alert(message);
+ }
+ self.$('#name').val(input_val);
+ });
+ },
+ reset_file: function () {
+ var control = this.$('#upload');
+ control.replaceWith(control = control.clone(true));
+ this.file.name = false;
+ },
+ display_alert: function (message) {
+ this.$('.alert-warning').remove();
+ $('<div class="alert alert-warning" role="alert">' + message + '</div>').insertBefore(this.$('form'));
+ },
+
+ /**
+ Wrapper for select2 load data from server at once and store it.
+
+ @param {String} Placeholder for element.
+ @param {bool} true for multiple selection box, false for single selection
+ @param {Function} Function to fetch data from remote location should return $.deferred object
+ resolved data should be array of object with id and name. eg. [{'id': id, 'name': 'text'}, ...]
+ @returns {Object} select2 wrapper object
+ */
+ select2_wrapper: function (tag, multi, fetch_fnc) {
+ return {
+ width: '100%',
+ placeholder: tag,
+ allowClear: true,
+ formatNoMatches: _.str.sprintf(_t("No matches found. Type to create new %s"), tag),
+ multiple: multi,
+ selection_data: false,
+ fetch_rpc_fnc : fetch_fnc,
+ formatSelection: function (data) {
+ if (data.tag) {
+ data.text = data.tag;
+ }
+ return data.text;
+ },
+ createSearchChoice: function (term) {
+ return {
+ id: _.uniqueId('tag_'),
+ create: true,
+ tag: term,
+ text: _.str.sprintf(_t("Create New %s '%s'"), tag, term)
+ };
+ },
+ fill_data: function (query, data) {
+ var that = this,
+ tags = {results: []};
+ _.each(data, function (obj) {
+ if (that.matcher(query.term, obj.name)) {
+ tags.results.push({id: obj.id, text: obj.name });
+ }
+ });
+ query.callback(tags);
+ },
+ query: function (query) {
+ var that = this;
+ // fetch data only once and store it
+ if (!this.selection_data) {
+ this.fetch_rpc_fnc().then(function (data) {
+ that.fill_data(query, data);
+ that.selection_data = data;
+ });
+ } else {
+ this.fill_data(query, this.selection_data);
+ }
+ }
+ };
+ },
+ // Category management from select2
+ set_category_id: function () {
+ var self = this;
+ $('#category_id').select2(this.select2_wrapper(_t('Category'), false,
+ function () {
+ return openerp.jsonRpc("/web/dataset/call_kw", 'call', {
+ model: 'slide.category',
+ method: 'search_read',
+ args: [],
+ kwargs: {
+ fields: ['name'],
+ domain: [['channel_id', '=', self.channel_id]],
+ context: website.get_context()
+ }
+ });
+ }));
+ },
+ get_category_id: function () {
+ var value = $('#category_id').select2('data');
+ if (value && value.create) {
+ return [0, {'name': value.text}];
+ }
+ return [value ? value.id : null];
+ },
+ // Tags management from select2
+ set_tag_ids: function () {
+ $('#tag_ids').select2(this.select2_wrapper(_t('Tags'), true, function () {
+ return openerp.jsonRpc("/web/dataset/call_kw", 'call', {
+ model: 'slide.tag',
+ method: 'search_read',
+ args: [],
+ kwargs: {
+ fields: ['name'],
+ context: website.get_context()
+ }
+ });
+ }));
+ },
+ get_tag_ids: function () {
+ var res = [];
+ _.each($('#tag_ids').select2('data'),
+ function (val) {
+ if (val.create) {
+ res.push([0, 0, {'name': val.text}]);
+ } else {
+ res.push([4, val.id]);
+ }
+ });
+ return res;
+ },
+ // Values and save
+ get_value: function () {
+ var canvas = this.$('#data_canvas')[0],
+ values = {
+ 'channel_id': this.channel_id || '',
+ 'name': this.$('#name').val(),
+ 'url': this.$('#url').val(),
+ 'description': this.$('#description').val(),
+ 'tag_ids': this.get_tag_ids(),
+ 'category_id': this.get_category_id()
+ };
+ if (this.file.type === 'application/pdf') {
+ _.extend(values, {
+ 'image': canvas.toDataURL().split(',')[1],
+ 'index_content': this.index_content,
+ 'slide_type': canvas.height > canvas.width ? 'document' : 'presentation',
+ 'mime_type': this.file.type,
+ 'datas': this.file.data
+ });
+ }
+ if (/^image\/.*/.test(this.file.type)) {
+ _.extend(values, {
+ 'slide_type': 'infographic',
+ 'mime_type': this.file.type,
+ 'datas': this.file.data
+ });
+ }
+ return values;
+ },
+ validate: function () {
+ this.$('.form-group').removeClass('has-error');
+ if (!this.$('#name').val()) {
+ this.$('#name').closest('.form-group').addClass('has-error');
+ return false;
+ }
+ var url = this.$('#url').val() ? this.is_valid_url : false;
+ if (!(this.file.name || url)) {
+ this.$('#url').closest('.form-group').addClass('has-error');
+ return false;
+ }
+ return true;
+ },
+ save: function (ev) {
+ var self = this;
+ if (this.validate()) {
+ var values = this.get_value();
+ if ($(ev.target).data('published')) {
+ values.website_published = true;
+ }
+ this.$('.oe_slides_upload_loading').show();
+ this.$('.modal-footer, .modal-body').hide();
+ openerp.jsonRpc("/slides/add_slide", 'call', values).then(function (data) {
+ if (data.error) {
+ self.display_alert(data.error);
+ self.$('.oe_slides_upload_loading').hide();
+ self.$('.modal-footer, .modal-body').show();
+
+ } else {
+ window.location = data.url;
+ }
+ });
+ }
+ },
+ cancel: function () {
+ this.trigger("cancel");
+ }
+ });
+
+ // bind the event to the button
+ $('.oe_slide_js_upload').on('click', function () {
+ var channel_id = $(this).attr('channel_id');
+ website.slide.page_widgets['upload_dialog'] = new website.slide.Dialog(this, channel_id).appendTo(document.body);
+ });
+
+}); \ No newline at end of file
diff --git a/addons/website_slides/static/src/less/website_slides.less b/addons/website_slides/static/src/less/website_slides.less
new file mode 100644
index 00000000000..ae1dd9a2551
--- /dev/null
+++ b/addons/website_slides/static/src/less/website_slides.less
@@ -0,0 +1,156 @@
+.define-color-if-not-defined() {
+ @nav-tabs-border-color: #dddddd;
+ @brand-info: #dddddd;
+ @body-bg: #FFFFFF;
+ @brand-primary: #428bca;
+ @link-color: @brand-primary;
+ @panel-footer-bg: #f5f5f5;
+ @nav-link-hover-bg: #FFFFFF;
+ @navbar-default-link-color: #777;
+ @navbar-default-link-hover-color: #333;
+ @navbar-default-bg: #f8f8f8;
+} .define-color-if-not-defined();
+
+.oe_slides_panel_footer {
+ background-color: @navbar-default-bg;
+}
+
+.oe_slides_panel_footer .fa, .oe_slide_js_embed_option_link{
+ color : @brand-primary;
+ cursor: pointer;
+}
+
+.oe_slide_embed_option {
+ position: absolute;
+ padding: 30px 30px 30px 30px;
+ top: 0px;
+ left: 0px;
+ right: 0px;
+ bottom: 0px;
+ background-color: @body-bg;
+ opacity: 1;
+ display:none;
+ overflow-y: auto !important;
+}
+
+.oe_slide_embed_wrapper {
+ position: relative;
+}
+
+section.oe_slides_promote_box {
+ background-color: @panel-footer-bg;
+ border-bottom: 1px solid @nav-tabs-border-color;
+ border-top: 1px solid @nav-tabs-border-color;
+}
+
+img.oe_slides_channel_thumbnail {
+ height: 250px;
+ width: 100%
+}
+
+img.oe_slides_opacity {
+ opacity: .5;
+}
+
+img.oe_slides_promote_image {
+ max-height: 300px;
+ width: 100%;
+ overflow: hidden;
+ display: block;
+}
+
+.oe_slides_box_shadow {
+ box-shadow: 0 0 5px #CCC;
+}
+
+.oe_slides_apart_small {
+ height: 60px;
+ width: 90px;
+}
+
+.oe_slides_statistics_title {
+ border-bottom: 1px solid @nav-tabs-border-color;
+ padding-bottom: 5px;
+}
+.oe_slides_transcript {
+ overflow-y: auto;
+ max-height: 500px;
+}
+
+.oe_slides_bottom_border {
+ border-bottom: 1px solid @nav-tabs-border-color;
+}
+
+.oe_slide_js_like, .oe_slide_js_unlike{
+ cursor: pointer;
+ color: @link-color;
+}
+
+// Thumbnails
+.oe_slides_thumbnail_container{
+ height: 300px
+}
+
+.oe_slides_grid_thumbnail {
+ height: 200px;
+ width: 100%;
+ overflow: hidden;
+}
+.oe_slides_ellipsis {
+ white-space: nowrap;
+ width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis
+}
+
+// Suggested slides grid
+.oe_slides_suggestion_caption {
+ position:absolute;
+ top:0;
+ right:0;
+ background:rgba(10, 10, 10, 0.75);
+ width:100%;
+ height:100%;
+ display: none;
+ text-align:center;
+ color:#fff !important;
+ z-index:2;
+}
+
+.oe_slides_suggestion_media {
+ margin-top: 10px;
+ position: relative;
+}
+.oe_slides_suggestion_media img {
+ height: 33%;
+ width: 100%;
+ overflow: hidden;
+}
+
+// PDF Viewer Loader
+.oe_slides_loader{
+ position: relative;
+ width: 100%; /* for IE 6 */
+}
+.oe_slides_loader h4{
+ position: absolute;
+ bottom: 20px;
+ left: 0;
+ width: 35%;
+
+ color: white;
+ background: rgb(0, 0, 0); /* fallback color */
+ background: rgba(0, 0, 0, 0.7);
+ padding: 10px;
+}
+
+.oe_slides_panel{
+ padding-bottom: 50px;
+ height:100%;
+ margin-bottom:0px;
+}
+
+// PDF Viewer Share bar
+.oe_slides_share_bar{
+ padding: 10px 0;
+}
diff --git a/addons/website_slides/static/src/xml/website_slides.xml b/addons/website_slides/static/src/xml/website_slides.xml
new file mode 100644
index 00000000000..0c5c62c3466
--- /dev/null
+++ b/addons/website_slides/static/src/xml/website_slides.xml
@@ -0,0 +1,75 @@
+<templates id="template" xml:space="preserve">
+
+ <t t-name="website.slide.upload">
+ <div class="modal" tabindex="-1" role="dialog" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h3 class="modal-title">Upload Presentation</h3>
+ </div>
+ <div class="oe_slides_upload_loading text-center" style="display:none">
+ <h4><i class='fa fa-spinner fa-spin'></i> Uploading presentation... </h4>
+ </div>
+ <div class="modal-body">
+ <form class="clearfix form-horizontal">
+ <div class="form-group">
+ <div class="col-sm-4">
+ <img src="/website_slides/static/src/img/document.png" id="slide-image" title="Content Preview" class="img-responsive img-thumbnail"/>
+ </div>
+ <div class="col-sm-8">
+ <ul class="list-group">
+ <li class="list-group-item active">
+ <h5 class="list-group-item-heading">
+ <label for="upload" class="control-label">PDF or Image File</label>
+ </h5>
+ <input id="upload" name="file" class="form-control" accept="image/*,application/pdf" type="file"/>
+ </li>
+ <li class="list-group-item">
+ <h5 class="list-group-item-heading">
+ <label for="url" class="control-label">Youtube or Google Doc URL</label>
+ </h5>
+ <input id="url" name="url" class="form-control" placeholder="Youtube Video URL"/>
+ </li>
+ </ul>
+ </div>
+ </div>
+ <div class="form-group">
+ <label for="name" class="control-label col-sm-2">Title</label>
+ <div class="col-sm-10">
+ <input id="name" placeholder="Title" class="form-control" required="required"/>
+ </div>
+ </div>
+ <div class="form-group">
+ <label for="tag_ids" class="control-label col-sm-2">Tags</label>
+ <div class="controls col-sm-10">
+ <input id="tag_ids" type="hidden"/>
+ </div>
+ </div>
+ <div class="form-group">
+ <label for="category_id" class="control-label col-sm-2">Category</label>
+ <div class="controls col-sm-10">
+ <input id="category_id" type="hidden"/>
+ </div>
+ </div>
+ <div class="form-group">
+ <label for="description" class="control-label col-sm-2">Description</label>
+ <div class="controls col-sm-10">
+ <textarea id="description" rows="4" placeholder="Description" class="form-control"></textarea>
+ </div>
+ </div>
+ <canvas id="data_canvas" style="display: none;"></canvas>
+ </form>
+ </div>
+ <div class="modal-footer">
+ <button type="button" data-loading-text="Loading..." class="btn btn-primary save">Save as Draft</button>
+ <button type="button" data-loading-text="Loading..." data-published="true" class="btn btn-primary save">Save and Publish</button>
+ or
+ <a href="#" data-dismiss="modal" aria-hidden="true">Discard</a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </t>
+
+</templates>