[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 {
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 {

View File

@ -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 ---- */

View File

@ -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();
},

View File

@ -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 &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) {
tips.push({
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) {
@ -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();
},
});
})();

View File

@ -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);

View File

@ -26,14 +26,7 @@
</div>
</div>
</div>
<dl class="dl-horizontal">
<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>
<span class="text-muted">Most searched topics related your keywords, ordered by importance:</span>
<!-- filled in JS -->
</section>
<section>
@ -53,10 +46,10 @@
</div>
</div>
</section>
<section>
<section class="js_image_section">
<h3 class="page-header">3. Describe your images</h3>
<div class="row js_seo_image_list">
<!-- filled in JS -->
<!-- filled in JS -->
</div>
</section>
<hr/>
@ -76,19 +69,16 @@
</t>
<t t-name="website.seo_tip">
<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"/>
<div t-attf-class="alert alert-#{ widget.type }">
<t t-raw="widget.message"/>
</div>
</t>
<t t-name="website.seo_keyword">
<dl class="dl-horizontal js_seo_keyword">
<dl class="dl-horizontal">
<dt>
<span t-attf-class="label label-#{ widget.type } js_seo_keyword" t-att-data-keyword="widget.keyword">
<a href="#" class="oe_remove" data-action="remove-keyword">x</a>
&amp;nbsp;
<t t-raw="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> <t t-raw="widget.keyword"/>
</span>
</dt>
<dd class="js_seo_keyword_suggestion">
@ -99,7 +89,7 @@
<t t-name="website.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"/>
</span>
</li>