[MERGE] Sync with main website-al branch

bzr revid: tde@openerp.com-20130920135710-diowg05s6cmoxa05
This commit is contained in:
Thibault Delavallée 2013-09-20 15:57:10 +02:00
commit 835bad4386
6 changed files with 170 additions and 103 deletions

View File

@ -509,6 +509,22 @@ table.editorbar-panel td.selected {
.oe_seo_configuration .oe_seo_suggestion { .oe_seo_configuration .oe_seo_suggestion {
cursor: pointer; 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 ---- */ /* ---- ACE EDITOR ---- */
.oe_ace_view_editor { .oe_ace_view_editor {

View File

@ -429,6 +429,10 @@ $icon_close: #E00101
/* ---- SEO TOOLS ---- */ /* ---- SEO TOOLS ---- */
$remove_color: $icon_close $remove_color: $icon_close
$in_title_color: #5cb85c
$in_description_color: #428bca
$in_body_color: #5bc0de
$highlighted_text_color: #ffffff
.oe_seo_configuration .oe_seo_configuration
.modal-dialog .modal-dialog
width: 80% width: 80%
@ -436,6 +440,18 @@ $remove_color: $icon_close
color: $remove_color color: $remove_color
.oe_seo_suggestion .oe_seo_suggestion
cursor: pointer 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 ---- */ /* ---- ACE EDITOR ---- */

View File

@ -25,9 +25,9 @@
website.EditorBar.include({ website.EditorBar.include({
events: _.extend({}, website.EditorBar.prototype.events, { 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(); e.preventDefault();
launch(); launch();
}, },

View File

@ -6,45 +6,51 @@
website.EditorBar.include({ website.EditorBar.include({
events: _.extend({}, website.EditorBar.prototype.events, { 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)); (new website.seo.Configurator()).appendTo($(document.body));
}, },
}); });
website.seo = {}; 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({ website.seo.Suggestion = openerp.Widget.extend({
template: 'website.seo_suggestion', template: 'website.seo_suggestion',
events: { events: {
'click .js_seo_suggestion': 'select', 'click .js_seo_suggestion': 'select',
}, },
init: function (parent, keyword, htmlPage) { init: function (parent, options) {
this.keyword = keyword; this.root = options.root;
this.htmlPage = htmlPage; this.keyword = options.keyword;
this.type = this.computeType(); this.htmlPage = options.page;
this._super(parent); this._super(parent);
}, },
start: function () { start: function () {
var self = this; this.htmlPage.on('title-changed', this, this.renderElement);
function update () { this.htmlPage.on('description-changed', this, this.renderElement);
self.updateType();
}
self.htmlPage.on('title-changed', self, update);
self.htmlPage.on('description-changed', self, update);
}, },
computeType: function () { analyze: function () {
// cf. http://getbootstrap.com/components/#labels return analyzeKeyword(this.htmlPage, this.keyword);
// 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 () { highlight: function () {
this.type = this.computeType(); return this.analyze().title;
this.renderElement(); },
tooltip: function () {
return this.analyze().description;
}, },
select: function () { select: function () {
this.trigger('selected', this.keyword); this.trigger('selected', this.keyword);
@ -53,9 +59,9 @@
website.seo.SuggestionList = openerp.Widget.extend({ website.seo.SuggestionList = openerp.Widget.extend({
template: 'website.seo_list', template: 'website.seo_list',
init: function (parent, word, htmlPage) { init: function (parent, options) {
this.word = word; this.root = options.root;
this.htmlPage = htmlPage; this.htmlPage = options.page;
this._super(parent); this._super(parent);
}, },
start: function () { start: function () {
@ -67,14 +73,18 @@
function addSuggestions (list) { function addSuggestions (list) {
self.$el.empty(); self.$el.empty();
// TODO Improve algorithm + Ajust based on custom user keywords // TODO Improve algorithm + Ajust based on custom user keywords
var nameRegex = new RegExp(self.companyName, "gi"); var regex = new RegExp(self.root, "gi");
var cleanList = _.map(list, function removeCompanyName (word) { var cleanList = _.map(list, function (word) {
return word.replace(nameRegex, "").trim(); return word.replace(regex, "").trim();
}); });
// TODO Order properly ? // TODO Order properly ?
_.each(_.uniq(cleanList), function (keyword) { _.each(_.uniq(cleanList), function (keyword) {
if (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) { suggestion.on('selected', self, function (word) {
self.trigger('selected', 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', 'click a[data-action=remove-keyword]': 'destroy',
}, },
maxWordsPerKeyword: 4, // TODO Check maxWordsPerKeyword: 4, // TODO Check
init: function (parent, keyword, htmlPage) { init: function (parent, options) {
this.keyword = keyword; this.keyword = options.word;
this.htmlPage = htmlPage; this.htmlPage = options.page;
this.type = this.computeType();
this._super(parent); this._super(parent);
}, },
start: function () { start: function () {
var self = this; this.htmlPage.on('title-changed', this, this.updateLabel);
function update () { this.htmlPage.on('description-changed', this, this.updateLabel);
self.updateType(); this.suggestionList = new website.seo.SuggestionList(this, {
} root: this.keyword,
self.htmlPage.on('title-changed', self, update); page: this.htmlPage,
self.htmlPage.on('description-changed', self, update); });
self.suggestionList = new website.seo.SuggestionList(self, this.keyword, this.htmlPage); this.suggestionList.on('selected', this, function (word) {
self.suggestionList.on('selected', self, function (word) { this.trigger('selected', word);
self.trigger('selected', word);
}); });
this.suggestionList.appendTo(this.$('.js_seo_keyword_suggestion')); this.suggestionList.appendTo(this.$('.js_seo_keyword_suggestion'));
}, },
computeType: function () { analyze: function () {
// cf. http://getbootstrap.com/components/#labels return analyzeKeyword(this.htmlPage, this.keyword);
// 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';
}, },
updateType: function () { highlight: function () {
this.type = this.computeType(); return this.analyze().title;
this.$('span.js_seo_keyword').attr('class', "label label-"+this.type+" js_seo_keyword"); },
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 () { destroy: function () {
this.trigger('removed'); this.trigger('removed');
@ -132,29 +142,44 @@
website.seo.KeywordList = openerp.Widget.extend({ website.seo.KeywordList = openerp.Widget.extend({
template: 'website.seo_list', template: 'website.seo_list',
maxKeywords: 10, maxKeywords: 10,
init: function (parent, htmlPage) { init: function (parent, options) {
this.htmlPage = htmlPage; this.htmlPage = options.page;
this._super(parent); 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 () { keywords: function () {
var result = []; var result = [];
this.$('span.js_seo_keyword').each(function () { this.$('.js_seo_keyword').each(function () {
result.push($(this).data('keyword')); result.push($(this).data('keyword'));
}); });
return result; return result;
}, },
isKeywordListFull: function () { isFull: function () {
return this.keywords().length >= this.maxKeywords; return this.keywords().length >= this.maxKeywords;
}, },
isExistingKeyword: function (word) { exists: function (word) {
return _.contains(this.keywords(), word); return _.contains(this.keywords(), word);
}, },
add: function (candidate) { add: function (candidate) {
var self = this; var self = this;
// TODO Refine // TODO Refine
var word = candidate ? candidate.replace(/[,;.:<>]+/g, " ").replace(/ +/g, " ").trim() : ""; var word = candidate ? candidate.replace(/[,;.:<>]+/g, " ").replace(/ +/g, " ").trim().toLowerCase() : "";
if (word && !self.isKeywordListFull() && !self.isExistingKeyword(word)) { if (word && !self.isFull() && !self.exists(word)) {
var keyword = new website.seo.Keyword(self, word, this.htmlPage); var keyword = new website.seo.Keyword(self, {
word: word,
page: this.htmlPage,
});
keyword.on('removed', self, function () { keyword.on('removed', self, function () {
self.trigger('list-not-full'); self.trigger('list-not-full');
self.trigger('removed', word); self.trigger('removed', word);
@ -164,7 +189,7 @@
}); });
keyword.appendTo(self.$el); keyword.appendTo(self.$el);
} }
if (self.isKeywordListFull()) { if (self.isFull()) {
self.trigger('list-full'); self.trigger('list-full');
} }
}, },
@ -181,8 +206,8 @@
website.seo.ImageList = openerp.Widget.extend({ website.seo.ImageList = openerp.Widget.extend({
init: function (parent, htmlPage) { init: function (parent, options) {
this.htmlPage = htmlPage; this.htmlPage = options.page;
this._super(parent); this._super(parent);
}, },
start: function () { start: function () {
@ -228,10 +253,11 @@
this.trigger('description-changed', description); this.trigger('description-changed', description);
}, },
keywords: function () { keywords: function () {
return $('meta[name=keywords]').attr('value').split(","); var parsed = $('meta[name=keywords]').attr('value').split(",");
return parsed[0] ? parsed: [];
}, },
changeKeywords: function (keywords) { changeKeywords: function (keywords) {
$('meta[name=keywords]').attr('value', keyword.join(",")); $('meta[name=keywords]').attr('value', keywords.join(","));
this.trigger('keywords-changed', keywords); this.trigger('keywords-changed', keywords);
}, },
headers: function (tag) { headers: function (tag) {
@ -299,9 +325,13 @@
$modal.find('input[name=seo_page_title]').val(htmlPage.title()); $modal.find('input[name=seo_page_title]').val(htmlPage.title());
$modal.find('textarea[name=seo_page_description]').val(htmlPage.description()); $modal.find('textarea[name=seo_page_description]').val(htmlPage.description());
self.suggestImprovements(); 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')); if (htmlPage.images().length === 0) {
self.keywordList = new website.seo.KeywordList(self, htmlPage); $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 () { self.keywordList.on('list-full', self, function () {
$modal.find('input[name=seo_page_keywords]') $modal.find('input[name=seo_page_keywords]')
.attr('readonly', "readonly") .attr('readonly', "readonly")
@ -319,9 +349,9 @@
self.keywordList.add(word); self.keywordList.add(word);
}); });
self.keywordList.appendTo($modal.find('.js_seo_keywords_list')); self.keywordList.appendTo($modal.find('.js_seo_keywords_list'));
var companyName = htmlPage.company().toLowerCase();
self.addKeyword(companyName);
$modal.modal(); $modal.modal();
// Avoid the 'Edit' button in the background boucing needlessly
$modal.on('click', function (e) { e.stopPropagation(); });
}, },
suggestImprovements: function () { suggestImprovements: function () {
var tips = []; var tips = [];
@ -336,13 +366,13 @@
if (htmlPage.headers('h1').length === 0) { if (htmlPage.headers('h1').length === 0) {
tips.push({ tips.push({
type: 'warning', type: 'warning',
message: "You don't have an &lt;h1&gt; tag on your page.", message: "This page seems to be missing an &lt;h1&gt; tag.",
}); });
} }
if (htmlPage.headers('h1').length > 1) { if (htmlPage.headers('h1').length > 1) {
tips.push({ tips.push({
type: 'warning', type: 'warning',
message: "You have more than one &lt;h1&gt; tag on your page.", message: "The page contains more than one &lt;h1&gt; tag.",
}); });
} }
if (tips.length > 0) { if (tips.length > 0) {
@ -350,7 +380,7 @@
displayTip(tip.message, tip.type); displayTip(tip.message, tip.type);
}); });
} else { } 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) { confirmKeyword: function (e) {
@ -389,5 +419,9 @@
self.htmlPage.changeDescription(description); self.htmlPage.changeDescription(description);
}, 1); }, 1);
}, },
destroy: function () {
this.htmlPage.changeKeywords(this.keywordList.keywords());
this._super();
},
}); });
})(); })();

View File

@ -426,13 +426,24 @@
var $zone = $(this); var $zone = $(this);
var $template = $(zone_template).addClass("oe_vertical"); var $template = $(zone_template).addClass("oe_vertical");
var nb = 0; var nb = 0;
var $lastinsert = false;
var left = 0;
var temp_left = 0;
$zone.find('> *:not(.oe_drop_zone):visible').each(function () { $zone.find('> *:not(.oe_drop_zone):visible').each(function () {
var $col = $(this); var $col = $(this);
$template.css('height', ($col.outerHeight() + parseInt($col.css("margin-top")) + parseInt($col.css("margin-bottom")))+'px'); $template.css('height', ($col.outerHeight() + parseInt($col.css("margin-top")) + parseInt($col.css("margin-bottom")))+'px');
$col.after($template.clone()); $lastinsert = $template.clone();
if (!nb) { $(this).after($lastinsert);
$(this).before($template.clone());
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 ++; nb ++;
}); });
if (!nb) { if (!nb) {
@ -456,11 +467,11 @@
var count; var count;
do { do {
count = 0; count = 0;
var $zones = $('.oe_drop_zone + .oe_drop_zone'); // no two consecutive zones // var $zones = $('.oe_drop_zone + .oe_drop_zone'); // no two consecutive zones
count += $zones.length; // count += $zones.length;
$zones.remove(); // $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; count += $zones.length;
$zones.remove(); $zones.remove();
} while (count > 0); } while (count > 0);
@ -846,7 +857,7 @@
var xy = event['page'+XY]; var xy = event['page'+XY];
var begin = current; var begin = current;
var beginClass = self.$target.attr("class"); 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'; var cursor = $handle.css("cursor")+'-important';
$("body").addClass(cursor); $("body").addClass(cursor);

View File

@ -26,14 +26,7 @@
</div> </div>
</div> </div>
</div> </div>
<dl class="dl-horizontal"> <span class="text-muted">Most searched topics related your keywords, ordered by importance:</span>
<dt><span class="label label-warning">Keywords</span></dt>
<dd>
<ul class="list-inline">
<li><span class="label label-default">Most searched topics related to these keywords</span></li>
</ul>
</dd>
</dl>
<!-- filled in JS --> <!-- filled in JS -->
</section> </section>
<section> <section>
@ -53,10 +46,10 @@
</div> </div>
</div> </div>
</section> </section>
<section> <section class="js_image_section">
<h3 class="page-header">3. Describe your images</h3> <h3 class="page-header">3. Describe your images</h3>
<div class="row js_seo_image_list"> <div class="row js_seo_image_list">
<!-- filled in JS --> <!-- filled in JS -->
</div> </div>
</section> </section>
<hr/> <hr/>
@ -76,19 +69,16 @@
</t> </t>
<t t-name="website.seo_tip"> <t t-name="website.seo_tip">
<div t-attf-class="alert alert-#{widget.type}"> <div t-attf-class="alert alert-#{ widget.type }">
<button title="Dismiss" type="button" class="close" data-action="close" data-dismiss="alert" aria-hidden="true">×</button> <t t-raw="widget.message"/>
<t t-raw="widget.message"/>
</div> </div>
</t> </t>
<t t-name="website.seo_keyword"> <t t-name="website.seo_keyword">
<dl class="dl-horizontal js_seo_keyword"> <dl class="dl-horizontal">
<dt> <dt>
<span t-attf-class="label label-#{ widget.type } js_seo_keyword" t-att-data-keyword="widget.keyword"> <span t-attf-title="#{ widget.tooltip() }" t-attf-class="oe_seo_keyword #{ widget.highlight() } js_seo_keyword" t-att-data-keyword="widget.keyword">
<a href="#" class="oe_remove" data-action="remove-keyword">x</a> <a href="#" class="oe_remove" data-action="remove-keyword">x</a> <t t-raw="widget.keyword"/>
&amp;nbsp;
<t t-raw="widget.keyword"/>
</span> </span>
</dt> </dt>
<dd class="js_seo_keyword_suggestion"> <dd class="js_seo_keyword_suggestion">
@ -99,7 +89,7 @@
<t t-name="website.seo_suggestion"> <t t-name="website.seo_suggestion">
<li class="oe_seo_suggestion"> <li class="oe_seo_suggestion">
<span t-attf-class="label label-#{ widget.type } js_seo_suggestion" t-att-data-keyword="widget.keyword"> <span t-attf-title="#{ widget.tooltip() }" t-attf-class="oe_seo_keyword #{ widget.highlight() } js_seo_suggestion" t-att-data-keyword="widget.keyword">
<t t-raw="widget.keyword"/> <t t-raw="widget.keyword"/>
</span> </span>
</li> </li>