diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 1e6c23baeed..2b5de84c05b 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -509,6 +509,22 @@ table.editorbar-panel td.selected { .oe_seo_configuration .oe_seo_suggestion { cursor: pointer; } +.oe_seo_configuration .oe_seo_keyword { + padding: 0.2em 0.4em 0.2em 0.5em; + border-radius: 0.4em; +} +.oe_seo_configuration .keyword-in-title { + background-color: #5cb85c; + color: white; +} +.oe_seo_configuration .keyword-in-description { + background-color: #428bca; + color: white; +} +.oe_seo_configuration .keyword-in-body { + background-color: #5bc0de; + color: white; +} /* ---- ACE EDITOR ---- */ .oe_ace_view_editor { diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index d275faa34fd..20fa98ec96d 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -429,6 +429,10 @@ $icon_close: #E00101 /* ---- SEO TOOLS ---- */ $remove_color: $icon_close +$in_title_color: #5cb85c +$in_description_color: #428bca +$in_body_color: #5bc0de +$highlighted_text_color: #ffffff .oe_seo_configuration .modal-dialog width: 80% @@ -436,6 +440,18 @@ $remove_color: $icon_close color: $remove_color .oe_seo_suggestion cursor: pointer + .oe_seo_keyword + padding: .2em .4em .2em .5em + border-radius: .4em + .keyword-in-title + background-color: $in_title_color + color: $highlighted_text_color + .keyword-in-description + background-color: $in_description_color + color: $highlighted_text_color + .keyword-in-body + background-color: $in_body_color + color: $highlighted_text_color /* ---- ACE EDITOR ---- */ diff --git a/addons/website/static/src/js/website.ace.js b/addons/website/static/src/js/website.ace.js index a39aa747e1c..206813f237b 100644 --- a/addons/website/static/src/js/website.ace.js +++ b/addons/website/static/src/js/website.ace.js @@ -25,9 +25,9 @@ website.EditorBar.include({ events: _.extend({}, website.EditorBar.prototype.events, { - 'click a[data-action=ace]': 'launch', + 'click a[data-action=ace]': 'launchAce', }), - launch: function (e) { + launchAce: function (e) { e.preventDefault(); launch(); }, diff --git a/addons/website/static/src/js/website.seo.js b/addons/website/static/src/js/website.seo.js index 49cc65e1b09..8dae960a7b5 100644 --- a/addons/website/static/src/js/website.seo.js +++ b/addons/website/static/src/js/website.seo.js @@ -6,45 +6,51 @@ website.EditorBar.include({ events: _.extend({}, website.EditorBar.prototype.events, { - 'click a[data-action=promote-current-page]': 'promotePage', + 'click a[data-action=promote-current-page]': 'launchSeo', }), - promotePage: function () { + launchSeo: function () { (new website.seo.Configurator()).appendTo($(document.body)); }, }); website.seo = {}; + function analyzeKeyword(htmlPage, keyword) { + return htmlPage.isInTitle(keyword) ? { + title: 'keyword-in-title', + description: "This keyword is used in the page title", + } : htmlPage.isInDescription(keyword) ? { + title: 'keyword-in-description', + description: "This keyword is used in the page description", + } : htmlPage.isInBody(keyword) ? { + title: 'keyword-in-body', + description: "This keyword is used in the page content." + } : { title: "", description: "" }; + } + website.seo.Suggestion = openerp.Widget.extend({ template: 'website.seo_suggestion', events: { 'click .js_seo_suggestion': 'select', }, - init: function (parent, keyword, htmlPage) { - this.keyword = keyword; - this.htmlPage = htmlPage; - this.type = this.computeType(); + init: function (parent, options) { + this.root = options.root; + this.keyword = options.keyword; + this.htmlPage = options.page; this._super(parent); }, start: function () { - var self = this; - function update () { - self.updateType(); - } - self.htmlPage.on('title-changed', self, update); - self.htmlPage.on('description-changed', self, update); + this.htmlPage.on('title-changed', this, this.renderElement); + this.htmlPage.on('description-changed', this, this.renderElement); }, - computeType: function () { - // cf. http://getbootstrap.com/components/#labels - // default, primary, success, info, warning, danger - return this.htmlPage.isInTitle(this.keyword) ? 'success' - : this.htmlPage.isInDescription(this.keyword) ? 'primary' - : this.htmlPage.isInBody(this.keyword) ? 'info' - : 'default'; + analyze: function () { + return analyzeKeyword(this.htmlPage, this.keyword); }, - updateType: function () { - this.type = this.computeType(); - this.renderElement(); + highlight: function () { + return this.analyze().title; + }, + tooltip: function () { + return this.analyze().description; }, select: function () { this.trigger('selected', this.keyword); @@ -53,9 +59,9 @@ website.seo.SuggestionList = openerp.Widget.extend({ template: 'website.seo_list', - init: function (parent, word, htmlPage) { - this.word = word; - this.htmlPage = htmlPage; + init: function (parent, options) { + this.root = options.root; + this.htmlPage = options.page; this._super(parent); }, start: function () { @@ -67,14 +73,18 @@ function addSuggestions (list) { self.$el.empty(); // TODO Improve algorithm + Ajust based on custom user keywords - var nameRegex = new RegExp(self.companyName, "gi"); - var cleanList = _.map(list, function removeCompanyName (word) { - return word.replace(nameRegex, "").trim(); + var regex = new RegExp(self.root, "gi"); + var cleanList = _.map(list, function (word) { + return word.replace(regex, "").trim(); }); // TODO Order properly ? _.each(_.uniq(cleanList), function (keyword) { if (keyword) { - var suggestion = new website.seo.Suggestion(self, keyword, self.htmlPage); + var suggestion = new website.seo.Suggestion(self, { + root: self.root, + keyword: keyword, + page: self.htmlPage, + }); suggestion.on('selected', self, function (word) { self.trigger('selected', word); }); @@ -82,7 +92,7 @@ } }); } - $.getJSON("http://seo.eu01.aws.af.cm/suggest/"+encodeURIComponent(this.word + " "), addSuggestions); + $.getJSON("http://seo.eu01.aws.af.cm/suggest/"+encodeURIComponent(this.root + " "), addSuggestions); }, }); @@ -92,36 +102,36 @@ 'click a[data-action=remove-keyword]': 'destroy', }, maxWordsPerKeyword: 4, // TODO Check - init: function (parent, keyword, htmlPage) { - this.keyword = keyword; - this.htmlPage = htmlPage; - this.type = this.computeType(); + init: function (parent, options) { + this.keyword = options.word; + this.htmlPage = options.page; this._super(parent); }, start: function () { - var self = this; - function update () { - self.updateType(); - } - self.htmlPage.on('title-changed', self, update); - self.htmlPage.on('description-changed', self, update); - self.suggestionList = new website.seo.SuggestionList(self, this.keyword, this.htmlPage); - self.suggestionList.on('selected', self, function (word) { - self.trigger('selected', word); + this.htmlPage.on('title-changed', this, this.updateLabel); + this.htmlPage.on('description-changed', this, this.updateLabel); + this.suggestionList = new website.seo.SuggestionList(this, { + root: this.keyword, + page: this.htmlPage, + }); + this.suggestionList.on('selected', this, function (word) { + this.trigger('selected', word); }); this.suggestionList.appendTo(this.$('.js_seo_keyword_suggestion')); }, - computeType: function () { - // cf. http://getbootstrap.com/components/#labels - // default, primary, success, info, warning, danger - return this.htmlPage.isInTitle(this.keyword) ? 'success' - : this.htmlPage.isInDescription(this.keyword) ? 'primary' - : this.htmlPage.isInBody(this.keyword) ? 'warning' - : 'default'; + analyze: function () { + return analyzeKeyword(this.htmlPage, this.keyword); }, - updateType: function () { - this.type = this.computeType(); - this.$('span.js_seo_keyword').attr('class', "label label-"+this.type+" js_seo_keyword"); + highlight: function () { + return this.analyze().title; + }, + tooltip: function () { + return this.analyze().description; + }, + updateLabel: function () { + var cssClass = "oe_seo_keyword js_seo_keyword " + this.highlight(); + this.$(".js_seo_keyword").attr('class', cssClass); + this.$(".js_seo_keyword").attr('title', this.tooltip()); }, destroy: function () { this.trigger('removed'); @@ -132,29 +142,44 @@ website.seo.KeywordList = openerp.Widget.extend({ template: 'website.seo_list', maxKeywords: 10, - init: function (parent, htmlPage) { - this.htmlPage = htmlPage; + init: function (parent, options) { + this.htmlPage = options.page; this._super(parent); }, + start: function () { + var self = this; + var existingKeywords = self.htmlPage.keywords(); + if (existingKeywords.length > 0) { + _.each(existingKeywords, function (word) { + self.add.call(self, word); + }); + } else { + var companyName = self.htmlPage.company().toLowerCase(); + self.add(companyName); + } + }, keywords: function () { var result = []; - this.$('span.js_seo_keyword').each(function () { + this.$('.js_seo_keyword').each(function () { result.push($(this).data('keyword')); }); return result; }, - isKeywordListFull: function () { + isFull: function () { return this.keywords().length >= this.maxKeywords; }, - isExistingKeyword: function (word) { + exists: function (word) { return _.contains(this.keywords(), word); }, add: function (candidate) { var self = this; // TODO Refine - var word = candidate ? candidate.replace(/[,;.:<>]+/g, " ").replace(/ +/g, " ").trim() : ""; - if (word && !self.isKeywordListFull() && !self.isExistingKeyword(word)) { - var keyword = new website.seo.Keyword(self, word, this.htmlPage); + var word = candidate ? candidate.replace(/[,;.:<>]+/g, " ").replace(/ +/g, " ").trim().toLowerCase() : ""; + if (word && !self.isFull() && !self.exists(word)) { + var keyword = new website.seo.Keyword(self, { + word: word, + page: this.htmlPage, + }); keyword.on('removed', self, function () { self.trigger('list-not-full'); self.trigger('removed', word); @@ -164,7 +189,7 @@ }); keyword.appendTo(self.$el); } - if (self.isKeywordListFull()) { + if (self.isFull()) { self.trigger('list-full'); } }, @@ -181,8 +206,8 @@ website.seo.ImageList = openerp.Widget.extend({ - init: function (parent, htmlPage) { - this.htmlPage = htmlPage; + init: function (parent, options) { + this.htmlPage = options.page; this._super(parent); }, start: function () { @@ -228,10 +253,11 @@ this.trigger('description-changed', description); }, keywords: function () { - return $('meta[name=keywords]').attr('value').split(","); + var parsed = $('meta[name=keywords]').attr('value').split(","); + return parsed[0] ? parsed: []; }, changeKeywords: function (keywords) { - $('meta[name=keywords]').attr('value', keyword.join(",")); + $('meta[name=keywords]').attr('value', keywords.join(",")); this.trigger('keywords-changed', keywords); }, headers: function (tag) { @@ -299,9 +325,13 @@ $modal.find('input[name=seo_page_title]').val(htmlPage.title()); $modal.find('textarea[name=seo_page_description]').val(htmlPage.description()); self.suggestImprovements(); - self.imageList = new website.seo.ImageList(self, htmlPage); - self.imageList.appendTo($modal.find('.js_seo_image_list')); - self.keywordList = new website.seo.KeywordList(self, htmlPage); + self.imageList = new website.seo.ImageList(self, { page: htmlPage }); + if (htmlPage.images().length === 0) { + $modal.find('.js_image_section').remove() + } else { + self.imageList.appendTo($modal.find('.js_seo_image_list')); + } + self.keywordList = new website.seo.KeywordList(self, { page: htmlPage }); self.keywordList.on('list-full', self, function () { $modal.find('input[name=seo_page_keywords]') .attr('readonly', "readonly") @@ -319,9 +349,9 @@ self.keywordList.add(word); }); self.keywordList.appendTo($modal.find('.js_seo_keywords_list')); - var companyName = htmlPage.company().toLowerCase(); - self.addKeyword(companyName); $modal.modal(); + // Avoid the 'Edit' button in the background boucing needlessly + $modal.on('click', function (e) { e.stopPropagation(); }); }, suggestImprovements: function () { var tips = []; @@ -336,13 +366,13 @@ if (htmlPage.headers('h1').length === 0) { tips.push({ type: 'warning', - message: "You don't have an <h1> tag on your page.", + message: "This page seems to be missing an <h1> tag.", }); } if (htmlPage.headers('h1').length > 1) { tips.push({ type: 'warning', - message: "You have more than one <h1> tag on your page.", + message: "The page contains more than one <h1> tag.", }); } if (tips.length > 0) { @@ -350,7 +380,7 @@ displayTip(tip.message, tip.type); }); } else { - displayTip("Your page makup is appropriate for search engines.", 'success'); + displayTip("The markup on this page is appropriate for search engines.", 'success'); } }, confirmKeyword: function (e) { @@ -389,5 +419,9 @@ self.htmlPage.changeDescription(description); }, 1); }, + destroy: function () { + this.htmlPage.changeKeywords(this.keywordList.keywords()); + this._super(); + }, }); })(); diff --git a/addons/website/static/src/js/website.snippets.js b/addons/website/static/src/js/website.snippets.js index 9acc7f9a9d5..db2545ada29 100644 --- a/addons/website/static/src/js/website.snippets.js +++ b/addons/website/static/src/js/website.snippets.js @@ -426,13 +426,24 @@ var $zone = $(this); var $template = $(zone_template).addClass("oe_vertical"); var nb = 0; + var $lastinsert = false; + var left = 0; + var temp_left = 0; $zone.find('> *:not(.oe_drop_zone):visible').each(function () { var $col = $(this); $template.css('height', ($col.outerHeight() + parseInt($col.css("margin-top")) + parseInt($col.css("margin-bottom")))+'px'); - $col.after($template.clone()); - if (!nb) { - $(this).before($template.clone()); + $lastinsert = $template.clone(); + $(this).after($lastinsert); + + temp_left = $col.position().left; + if (left === temp_left) { + $col.prev(".oe_drop_zone.oe_vertical").remove(); + $col.before($template.clone().css("clear", "left")); } + else if (!nb) { + $col.before($template.clone()); + } + left = temp_left; nb ++; }); if (!nb) { @@ -456,11 +467,11 @@ var count; do { count = 0; - var $zones = $('.oe_drop_zone + .oe_drop_zone'); // no two consecutive zones - count += $zones.length; - $zones.remove(); + // var $zones = $('.oe_drop_zone + .oe_drop_zone'); // no two consecutive zones + // count += $zones.length; + // $zones.remove(); - $zones = $('.oe_drop_zone > .oe_drop_zone').remove(); // no recusrive zones + $zones = $('.oe_drop_zone > .oe_drop_zone:not(.oe_vertical)').remove(); // no recusrive zones count += $zones.length; $zones.remove(); } while (count > 0); @@ -846,7 +857,7 @@ var xy = event['page'+XY]; var begin = current; var beginClass = self.$target.attr("class"); - var regClass = new RegExp("\\s*" + resize[0][begin].replace(/[-]*[0-9]+/, '[0-9-]+'), 'g'); + var regClass = new RegExp("\\s*" + resize[0][begin].replace(/[-]*[0-9]+/, '[-]*[0-9]+'), 'g'); var cursor = $handle.css("cursor")+'-important'; $("body").addClass(cursor); diff --git a/addons/website/static/src/xml/website.seo.xml b/addons/website/static/src/xml/website.seo.xml index 9471733e25c..cc209cf0fe2 100644 --- a/addons/website/static/src/xml/website.seo.xml +++ b/addons/website/static/src/xml/website.seo.xml @@ -26,14 +26,7 @@ -
-
Keywords
-
- -
-
+ Most searched topics related your keywords, ordered by importance:
@@ -53,10 +46,10 @@
-
+
- +

@@ -76,19 +69,16 @@ -
- - +
+
-
+
- - x - &nbsp; - + + x
@@ -99,7 +89,7 @@
  • - +