From 824c098aeffc53383b9d58116c7b87824ac001a8 Mon Sep 17 00:00:00 2001 From: ddm Date: Fri, 20 Sep 2013 14:22:32 +0200 Subject: [PATCH 1/4] [FIX] seo whitespace bzr revid: ddm@openerp.com-20130920122232-58di0ozyax9qy4or --- addons/website/static/src/css/editor.css | 13 +- addons/website/static/src/css/editor.sass | 13 +- addons/website/static/src/js/website.ace.js | 4 +- addons/website/static/src/js/website.seo.js | 150 +++++++++--------- addons/website/static/src/xml/website.seo.xml | 19 +-- 5 files changed, 110 insertions(+), 89 deletions(-) diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 1e6c23baeed..3b6c7055e13 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -506,8 +506,19 @@ table.editorbar-panel td.selected { .oe_seo_configuration .oe_remove { color: #e00101; } -.oe_seo_configuration .oe_seo_suggestion { +.oe_seo_configuration .oe_seo_keyword { cursor: pointer; + padding: 0.2em 0.4em 0.2em 0.5em; + border-radius: 0.4em; +} +.oe_seo_configuration .keyword-in-title { + background-color: #5cb85c; +} +.oe_seo_configuration .keyword-in-description { + background-color: #428bca; +} +.oe_seo_configuration .keyword-in-body { + background-color: #5bc0de; } /* ---- ACE EDITOR ---- */ diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index d275faa34fd..4db40a8f9c2 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -429,13 +429,24 @@ $icon_close: #E00101 /* ---- SEO TOOLS ---- */ $remove_color: $icon_close +$in_title_color: #5cb85c +$in_description_color: #428bca +$in_body_color: #5bc0de .oe_seo_configuration .modal-dialog width: 80% .oe_remove color: $remove_color - .oe_seo_suggestion + .oe_seo_keyword cursor: pointer + padding: .2em .4em .2em .5em + border-radius: .4em + .keyword-in-title + background-color: $in_title_color + .keyword-in-description + background-color: $in_description_color + .keyword-in-body + background-color: $in_body_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..810fa3c737c 100644 --- a/addons/website/static/src/js/website.seo.js +++ b/addons/website/static/src/js/website.seo.js @@ -6,9 +6,9 @@ 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)); }, }); @@ -20,31 +20,21 @@ 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'; - }, - updateType: function () { - this.type = this.computeType(); - this.renderElement(); + highlight: function () { + return this.htmlPage.isInTitle(this.keyword) ? 'keyword-in-title' + : this.htmlPage.isInDescription(this.keyword) ? 'keyword-in-description' + : this.htmlPage.isInBody(this.keyword) ? 'keyword-in-body' + : ""; }, select: function () { this.trigger('selected', this.keyword); @@ -53,9 +43,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 +57,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 +76,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 +86,32 @@ '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'; + highlight: function () { + return this.htmlPage.isInTitle(this.keyword) ? 'keyword-in-title' + : this.htmlPage.isInDescription(this.keyword) ? 'keyword-in-description' + : this.htmlPage.isInBody(this.keyword) ? 'keyword-in-body' + : ""; }, - updateType: function () { - this.type = this.computeType(); - this.$('span.js_seo_keyword').attr('class', "label label-"+this.type+" js_seo_keyword"); + updateLabel: function () { + var cssClass = "oe_seo_keyword js_seo_keyword " + this.highlight(); + this.$(".js_seo_keyword").attr('class', cssClass); }, destroy: function () { this.trigger('removed'); @@ -132,29 +122,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 +169,7 @@ }); keyword.appendTo(self.$el); } - if (self.isKeywordListFull()) { + if (self.isFull()) { self.trigger('list-full'); } }, @@ -181,8 +186,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 +233,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 +305,9 @@ $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 = new website.seo.ImageList(self, { page: htmlPage }); self.imageList.appendTo($modal.find('.js_seo_image_list')); - self.keywordList = new website.seo.KeywordList(self, htmlPage); + 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,8 +325,6 @@ self.keywordList.add(word); }); self.keywordList.appendTo($modal.find('.js_seo_keywords_list')); - var companyName = htmlPage.company().toLowerCase(); - self.addKeyword(companyName); $modal.modal(); }, suggestImprovements: function () { @@ -389,5 +393,9 @@ self.htmlPage.changeDescription(description); }, 1); }, + destroy: function () { + this.htmlPage.changeKeywords(this.keywordList.keywords()); + this._super(); + }, }); })(); diff --git a/addons/website/static/src/xml/website.seo.xml b/addons/website/static/src/xml/website.seo.xml index 9471733e25c..9d5b1700a91 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 to these keywords
  • -
-
-
+ Here are the most searched topics related to these keywords, ordered by importance:
@@ -83,12 +76,10 @@ -
+
- - x - &nbsp; - + + x
@@ -99,7 +90,7 @@
  • - +
  • From 3c6d6e7a4f039e8586dc4f04c0b010827aed4239 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Fri, 20 Sep 2013 15:01:22 +0200 Subject: [PATCH 2/4] [FIX] website snippet: fix resizing bzr revid: chm@openerp.com-20130920130122-1zd3cu27egc1iyxv --- .../website/static/src/js/website.snippets.js | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) 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); From a139fb4d4898fb19448752a50534ecbca86ca17f Mon Sep 17 00:00:00 2001 From: ddm Date: Fri, 20 Sep 2013 15:28:32 +0200 Subject: [PATCH 3/4] [IMP] small SEO usability improvements bzr revid: ddm@openerp.com-20130920132832-zlbbg4qk9t7gt4sb --- addons/website/static/src/css/editor.css | 7 ++- addons/website/static/src/css/editor.sass | 7 ++- addons/website/static/src/js/website.seo.js | 44 ++++++++++++++----- addons/website/static/src/xml/website.seo.xml | 11 +++-- 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 3b6c7055e13..2b5de84c05b 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -506,19 +506,24 @@ table.editorbar-panel td.selected { .oe_seo_configuration .oe_remove { color: #e00101; } -.oe_seo_configuration .oe_seo_keyword { +.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 ---- */ diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 4db40a8f9c2..20fa98ec96d 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -432,21 +432,26 @@ $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% .oe_remove color: $remove_color - .oe_seo_keyword + .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.seo.js b/addons/website/static/src/js/website.seo.js index 810fa3c737c..e38d63c9ef5 100644 --- a/addons/website/static/src/js/website.seo.js +++ b/addons/website/static/src/js/website.seo.js @@ -15,6 +15,19 @@ 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: { @@ -30,11 +43,14 @@ this.htmlPage.on('title-changed', this, this.renderElement); this.htmlPage.on('description-changed', this, this.renderElement); }, + analyze: function () { + return analyzeKeyword(this.htmlPage, this.keyword); + }, highlight: function () { - return this.htmlPage.isInTitle(this.keyword) ? 'keyword-in-title' - : this.htmlPage.isInDescription(this.keyword) ? 'keyword-in-description' - : this.htmlPage.isInBody(this.keyword) ? 'keyword-in-body' - : ""; + return this.analyze().title; + }, + tooltip: function () { + return this.analyze().description; }, select: function () { this.trigger('selected', this.keyword); @@ -103,15 +119,19 @@ }); this.suggestionList.appendTo(this.$('.js_seo_keyword_suggestion')); }, + analyze: function () { + return analyzeKeyword(this.htmlPage, this.keyword); + }, highlight: function () { - return this.htmlPage.isInTitle(this.keyword) ? 'keyword-in-title' - : this.htmlPage.isInDescription(this.keyword) ? 'keyword-in-description' - : this.htmlPage.isInBody(this.keyword) ? 'keyword-in-body' - : ""; + 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'); @@ -326,6 +346,8 @@ }); self.keywordList.appendTo($modal.find('.js_seo_keywords_list')); $modal.modal(); + // Avoid the 'Edit' button in the background boucing needlessly + $modal.on('click', function (e) { e.stopPropagation(); }); }, suggestImprovements: function () { var tips = []; @@ -340,13 +362,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) { @@ -354,7 +376,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) { diff --git a/addons/website/static/src/xml/website.seo.xml b/addons/website/static/src/xml/website.seo.xml index 9d5b1700a91..d77dacd10bc 100644 --- a/addons/website/static/src/xml/website.seo.xml +++ b/addons/website/static/src/xml/website.seo.xml @@ -26,7 +26,7 @@ - Here are the most searched topics related to these keywords, ordered by importance: + Most searched topics related your keywords, ordered by importance:
    @@ -69,16 +69,15 @@ -
    - - +
    +
    - + x
    @@ -90,7 +89,7 @@
  • - +
  • From 8989d0c7d976b3e3765a1b7056460aaf91600e42 Mon Sep 17 00:00:00 2001 From: ddm Date: Fri, 20 Sep 2013 15:36:49 +0200 Subject: [PATCH 4/4] [IMP] Hide SEO image section when unused bzr revid: ddm@openerp.com-20130920133649-akgg8blxmaxy9i5b --- addons/website/static/src/js/website.seo.js | 6 +++++- addons/website/static/src/xml/website.seo.xml | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/addons/website/static/src/js/website.seo.js b/addons/website/static/src/js/website.seo.js index e38d63c9ef5..8dae960a7b5 100644 --- a/addons/website/static/src/js/website.seo.js +++ b/addons/website/static/src/js/website.seo.js @@ -326,7 +326,11 @@ $modal.find('textarea[name=seo_page_description]').val(htmlPage.description()); self.suggestImprovements(); self.imageList = new website.seo.ImageList(self, { page: htmlPage }); - self.imageList.appendTo($modal.find('.js_seo_image_list')); + 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]') diff --git a/addons/website/static/src/xml/website.seo.xml b/addons/website/static/src/xml/website.seo.xml index d77dacd10bc..cc209cf0fe2 100644 --- a/addons/website/static/src/xml/website.seo.xml +++ b/addons/website/static/src/xml/website.seo.xml @@ -46,10 +46,10 @@
    -
    +
    - +