Explorar o código

commit inicial

edgar %!s(int64=8) %!d(string=hai) anos
achega
2afa02198c

+ 27 - 0
LICENSE

@@ -0,0 +1,27 @@
+Odoo Proprietary License v1.0
+
+This software and associated files (the "Software") may only be used (executed,
+modified, executed after modifications) if you have purchased a valid license
+from the authors, typically via Odoo Apps, or if you have received a written
+agreement from the authors of the Software (see the COPYRIGHT file).
+
+You may develop Odoo modules that use the Software as a library (typically
+by depending on it, importing it and using its resources), but without copying
+any source code or material from the Software. You may distribute those
+modules under the license of your choice, provided that this license is
+compatible with the terms of the Odoo Proprietary License (For example:
+LGPL, MIT, or proprietary licenses similar to this one).
+
+It is forbidden to publish, distribute, sublicense, or sell copies of the Software
+or modified copies of the Software.
+
+The above copyright notice and this permission notice must be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.

+ 1 - 0
__init__.py

@@ -0,0 +1 @@
+import controllers

BIN=BIN
__init__.pyc


+ 16 - 0
__openerp__.py

@@ -0,0 +1,16 @@
+{
+    'name':'Latest Posts Snippet',
+    'description':'Latest Posts Snippet',
+    'category': 'Website',
+    'version':'1.1',
+    'author':'Odoo S.A.',
+    'data': [
+        'views/assets.xml',
+        'views/s_latest_posts.xml',
+        'views/options.xml',
+    ],
+    'depends': ['website_less', 'website_blog'],
+    'images': [
+        'static/description/icon.png',
+    ],
+}

+ 1 - 0
controllers/__init__.py

@@ -0,0 +1 @@
+import main

BIN=BIN
controllers/__init__.pyc


+ 14 - 0
controllers/main.py

@@ -0,0 +1,14 @@
+import werkzeug
+from openerp.addons.web import http
+from openerp.addons.web.http import request
+
+class snippet_latest_posts_controller(http.Controller):
+
+    # @http.route(['/snippet_latest_posts/fetch'], type='json', auth='public', website=True)
+    # def fetch_latest_posts(self, fields, domain, limit=None, order='create_date desc', context={}):
+    #     return request.env['blog.post'].search_read(domain, fields, limit=limit, order=order, context=context)
+
+    @http.route(['/snippet_latest_posts/render'], type='json', auth='public', website=True)
+    def render_latest_posts(self, template, domain, limit=None, order='create_date desc'):
+        posts = request.env['blog.post'].search(domain, limit=limit, order=order)
+        return request.registry['ir.ui.view'].render(request.cr, request.uid, template, {'posts': posts}, context=request.context)

BIN=BIN
controllers/main.pyc


+ 34 - 0
doc/index.rst

@@ -0,0 +1,34 @@
+You need to install the *Less CSS* compiler to run this snippet
+
+* on Linux, use your distribution's package manager to install nodejs and npm.
+   * In debian wheezy and Ubuntu 13.10 and before you need to install nodejs manually:
+
+       .. code-block:: console
+
+           $ wget -qO- https://deb.nodesource.com/setup | bash -
+           $ apt-get install -y nodejs
+
+   * In later debian (>jessie) and ubuntu (>14.04) you may need to add a symlink as npm packages call ``node`` but debian calls the binary ``nodejs``
+
+       .. code-block:: console
+
+           $ apt-get install -y npm
+           $ sudo ln -s /usr/bin/nodejs /usr/bin/node
+
+   * Once npm is installed, use it to install less and less-plugin-clean-css:
+
+       .. code-block:: console
+
+           $ sudo npm install -g less less-plugin-clean-css
+
+* on OS X, install nodejs via your preferred package manager (`homebrew <http://brew.sh/>`_, `macports <https://www.macports.org/>`_) then install less and less-plugin-clean-css:
+
+   .. code-block:: console
+
+       $ sudo npm install -g less less-plugin-clean-css
+
+* on Windows, `install nodejs <http://nodejs.org/download/>`_, reboot (to update the `PATH`) and install less and less-plugin-clean-css:
+
+   .. code-block:: ps1
+
+       C:\> npm install -g less less-plugin-clean-css

BIN=BIN
static/description/icon.png


+ 18 - 0
static/description/index.html

@@ -0,0 +1,18 @@
+
+
+<div class="container">
+  
+
+  <div class="row text-left" style="margin-top: 2em;">
+    <div class="col-sm-5">
+      <p>Present your blog posts in magazine-style layouts. <br/>
+      This snippet is great for highlighting blog content on your homepage.
+    </p>
+    </div>
+    <div class="col-sm-7">
+      <img src="screen_1.jpg" class="img-responsive" alt="">
+    </div>
+  </div>
+
+
+</div>

BIN=BIN
static/description/screen_1.jpg


BIN=BIN
static/src/img/s_latest_posts.jpg


BIN=BIN
static/src/img/s_latest_posts_big_picture.jpg


BIN=BIN
static/src/img/thumb.jpg


+ 107 - 0
static/src/js/s_latest_posts_editor.js

@@ -0,0 +1,107 @@
+(function(){
+  'use strict';
+  var website = openerp.website;
+  var session_editor = new openerp.Session();
+  var _t = openerp._t;
+
+  
+  // js_get_posts
+  website.snippet.options.js_get_posts = website.snippet.Option.extend({
+    drop_and_build_snippet: function(){
+      var self = this;
+      if (!self.$target.data('snippet-view')) {
+        this.$target.data("snippet-view", new website.snippet.animationRegistry.js_get_posts(this.$target));
+      }
+    },
+    clean_for_save:function(){
+      this.$target.empty();
+    }
+  }),
+
+  // js_get_posts limit
+  website.snippet.options.js_get_posts_limit = website.snippet.Option.extend({
+    start:function(){
+      var self = this;
+      setTimeout(function(){
+        var ul = self.$overlay.find(".snippet-option-js_get_posts_limit > ul");
+        if (self.$target.attr("data-posts_limit")) {
+          var limit = self.$target.attr("data-posts_limit");
+          ul.find('li[data-posts_limit="' + limit + '"]').addClass("active");
+        } else {
+          ul.find('li[data-posts_limit="3"]').addClass("active");
+        }
+      },100)
+    },
+    posts_limit:function(type, value, $li){
+      var self = this;
+      if(type != "click"){return}
+      value = parseInt(value);
+      this.$target.attr("data-posts_limit",value)
+                  .data("posts_limit",value)
+                  .data('snippet-view').redrow(true);
+      setTimeout(function(){
+        $li.parent().find("li").removeClass("active");
+        $li.addClass("active");
+      },100); 
+    }
+  }),
+
+  // js_get_posts Select Blog
+  website.snippet.options.js_get_posts_selectBlog = website.snippet.Option.extend({
+    start: function() {
+      this._super();
+      var self      = this;
+      var model     =  session_editor.model('blog.blog');
+      var blogsList = [];
+
+      model
+      .call('search_read', 
+      [
+        [],
+        ['name','id']// attributes to get
+      ],  
+      {} )
+      .then(function(blogs){
+        self.createBlogsList(blogs)// start printing posts...
+      }) 
+      .fail(function (e) {
+        // No data
+        var title = _t("Oops, Huston we have a problem"),
+            msg   = $("<div contenteditable='false' class='message error text-center'><h3>"+ title +"</h3><code>"+ e.data.message + "</code></div>" );
+        self.$target.append(msg)
+        return;
+      });
+    },
+
+    createBlogsList: function(blogs){
+      var self = this;
+      var ul = null;
+
+      setTimeout(function(){
+        ul = self.$overlay.find(".snippet-option-js_get_posts_selectBlog > ul");
+        $(blogs).each(function(){
+          var blog = $(this);
+          var li = $('<li data-filter_by_blog_id="' + blog[0].id + '"><a>' + blog[0].name + '</a></li>');
+          ul.append(li);
+        });
+        if (self.$target.attr("data-filter_by_blog_id")) {
+          var id = self.$target.attr("data-filter_by_blog_id");
+          ul.find("li[data-filter_by_blog_id=" + id  + "]").addClass("active");
+        }
+      },100)
+    },
+
+    filter_by_blog_id:function(type, value, $li){
+      var self = this;
+      if(type == "click"){
+        $li.parent().find("li").removeClass("active");
+        $li.addClass("active");
+        value = parseInt(value);
+        self.$target.attr("data-filter_by_blog_id",value)
+                    .data("filter_by_blog_id",value)
+                    .data('snippet-view').redrow(true); 
+      }
+    }
+  })
+
+})();

+ 131 - 0
static/src/js/s_latest_posts_frontend.js

@@ -0,0 +1,131 @@
+(function(){
+  'use strict';
+
+  var website = openerp.website;
+
+  website.snippet.animationRegistry.js_get_posts = website.snippet.Animation.extend({
+    selector : ".js_get_posts",
+
+    start: function(){
+      this.redrow();
+    },
+    stop: function(){
+      this.clean();
+    },
+
+    redrow: function(debug){
+      this.clean(debug);
+      this.build(debug);
+    },
+
+    clean:function(debug){
+      this.$target.empty();
+    },
+
+    build: function(debug){
+      var self     = this,
+          limit    = self.$target.data("posts_limit"),
+          blog_id  = self.$target.data("filter_by_blog_id"),
+          template = self.$target.data("template"),
+          loading  = self.$target.data("loading");
+
+      // prevent user's editing
+      self.$target.attr("contenteditable","False");
+
+      // if no data, then use defaults values
+      if(!limit)limit = 3;
+      if(!template) template = 'snippet_latest_posts.media_list_template';
+      
+      // create the domain
+      var domain = [['website_published', '=', true]]
+      if (blog_id) {domain.push(['blog_id', '=', parseInt(blog_id)]); }
+
+      // call posts
+      openerp.jsonRpc('/snippet_latest_posts/render', 'call', {
+        'template': template,
+        'domain': domain,
+        'limit': limit,
+      }).then(function(posts) {
+        if (loading && loading == true) {
+          // perfrorm an intro animation
+          self.loading(posts, debug);
+        } else {
+          // just print the posts
+          $(posts).appendTo(self.$target);
+        }
+      })
+      .fail(function(e) {
+        // debug in js console
+        return;
+      });
+    },
+
+    loading: function(posts, debug){
+      var self = this,
+          $posts = $(posts);
+
+      if(!$posts.first().find(".loading_container") && !$posts.first().is(".loading_container")){
+        console.log("loading_container dont exist??")
+        if(debug) {
+          console.info("No '.loading_container' defined \n Please, add a 'loading_container' class to the element that must be filled by the loading bar");
+        }
+      } else if(!$posts.first().is(".thumb") && !$posts.first().find(".thumb")) {
+        console.log("thumb dont exist??")
+        if(debug) {
+          console.info("No '.thumb' defined \n Please, add a 'thumb' class to your thumbnail div");
+        }
+      }
+      else {
+        $posts.each(function(){
+          var $post     = $(this),
+              $load_c   = $post.find(".loading_container"),
+              $thumb    = $post.find(".thumb"),
+              $progress =  $('<div class="progress js-loading"><div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:0;" /></div>')
+
+          // prevent precessing empty post 
+          if ($post.html() == undefined) {return;}
+
+          // if can't find loading container or thumb inside the post, then they are the post itself
+          if($load_c.length == 0) { $load_c = $post }
+          if($thumb.length == 0)  { $thumb  = $post }
+
+          $post.addClass("js-loading");
+          $progress.appendTo($load_c);
+          $post.appendTo(self.$target);
+
+          var bg = $thumb.css('background-image').replace('url(','').replace(')',''),
+              loaded = false;
+
+          $progress.find(".progress-bar").css("width","50%").attr("aria-valuenow","50"); 
+
+          var dummyImg = $('<img/>').attr('src', bg)
+            .load(function() {
+              // The post's background image is loaded, let's perform a gracefull intro animation
+              $progress.find(".progress-bar").find(".progress-bar").css("width","100%").attr("aria-valuenow","100");
+              setTimeout(function() {
+                self.showPost($post, $progress);
+              },500); 
+              $(this).remove();
+              loaded = true;
+            });
+
+          // Show the post after 5sec without wait for thumb loading
+          setTimeout(function() {
+            if(loaded == false) {
+              dummyImg.remove();
+              self.showPost($post, $progress)
+            }
+          },5000); 
+
+        })
+      }
+    },
+
+    showPost: function($post, $progress){
+      $post.removeClass("js-loading");
+      $progress.fadeOut(500);
+    }
+
+  })
+
+})();

+ 24 - 0
static/src/less/js_get_posts.less

@@ -0,0 +1,24 @@
+.js_get_posts{
+    min-height: 100px;
+    overflow: auto;
+}
+
+.progress.js-loading{
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  border-radius: 0;
+
+  .progress-bar{
+    margin: 0;
+    left: 10%;
+    width: 80%;
+    top: 45%;
+    height: 10px;
+    position: absolute;
+    background: #DBDBDB;
+    .box-shadow(none);
+  }
+}

+ 31 - 0
static/src/less/s_latest_posts.less

@@ -0,0 +1,31 @@
+
+// Default Style ================================
+// ==============================================
+
+
+.s_latest_posts {
+  .media.media_list_template{
+    .pull-left {
+      height : 200px;
+      width  : 100%;
+      a, .media-object {
+        height : 100%;
+        width  : 100%;
+        position: relative;
+        background-size: cover;
+        background-position: center;
+      }
+      @media (min-width: 768px){
+        height : 100px;
+        width  : 100px;
+      }
+    }
+    .media, .media-body {
+      overflow: visible;
+      @media (min-width: 768px){
+        overflow: hidden;
+      }
+    }
+  }
+}
+

+ 114 - 0
static/src/less/s_latest_posts_big_picture.less

@@ -0,0 +1,114 @@
+
+// =============================
+// s_latest_posts_big_picture    
+// =============================
+
+@s_latest_posts_big_picture-figure_bg: @brand-primary;
+
+
+//  ====== hooks(hook) =====================================
+//  ===========================================================
+//  Use them to inject NEW rules or overwrite the old ones.  
+//  This method is more maintenable than classic css overwrite
+//  'couse it works also if the snippet's structure will change. 
+
+//  How to: 
+//  in your theme, just call the backdoor like a normal 
+//  CSS class adding parethesis at the end ".backdoor-name()" 
+//  (this will prevent the creation of a standard CSS class).
+//  Add your rules inside. Your style will take the priority.
+//  Done. 
+
+//  .s_latest_posts-figure(){
+//    background: red;
+//    min-height: 200px;
+//  }
+
+.s_latest_posts-js_get_posts-hook(){};
+  .s_latest_posts-figure-hook(){};
+
+
+
+
+
+.s_latest_posts_big_picture {
+
+  > div {
+    clear: both;
+
+    .content {
+      opacity:1;
+      position: relative;
+      height: 100%;
+    }
+
+    figure {
+      position: relative;
+      float: left;
+      overflow: hidden;
+      margin: 0;
+      padding: 0;
+      margin: 10px 1%;
+      width: 98%;
+      height: 250px;
+      background: @s_latest_posts_big_picture-figure_bg;
+      text-align: center;
+      cursor: pointer;
+      .transition(all 300ms);
+      @media (min-width:768px) {
+        width: 48%;
+      }
+
+      &.js-loading{
+        background: #E7E7E7!important;
+      }
+
+      .thumb {
+        position: relative;
+        display: block;
+        height: auto;
+        width: 100%;
+        height: 100%;
+        background-size: cover!important;
+        background-position: 50%;
+        opacity: 0.8;
+      }
+      figcaption, figcaption > a {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        padding: 2em;
+        color: #fff;
+        text-transform: uppercase;
+        font-size: 1.25em;
+        .backface-visibility(hidden);
+        &:after, &:before{ pointer-events: none;}
+
+        > a{
+          z-index: 1000;
+          text-indent: 200%;
+          white-space: nowrap;
+          font-size: 0;
+          opacity: 0;
+        }
+        h2 {
+          margin: 0;
+          font-size: 1.5em;
+          font-weight: 300;
+          span {font-weight: 800}
+        }
+        p{
+          margin: 0;
+          margin-top: 1em;
+          letter-spacing: 1px;
+          font-size: 68.5%;
+        }
+      }   
+      .s_latest_posts-figure-hook;
+    } // global figure
+    .s_latest_posts-js_get_posts-hook;
+  }
+}
+

+ 25 - 0
views/assets.xml

@@ -0,0 +1,25 @@
+<openerp>
+  <data>
+    
+    <template id="assets_frontend_less" name="Latest Posts LESS assets" inherit_id="website_less.option_bootstrap_less">
+      <xpath expr="//link[last()]" position="after">
+        <link href="/snippet_latest_posts/static/src/less/js_get_posts.less" rel="stylesheet" type="text/less"/>
+        <link href="/snippet_latest_posts/static/src/less/s_latest_posts.less" rel="stylesheet" type="text/less"/>
+        <link href="/snippet_latest_posts/static/src/less/s_latest_posts_big_picture.less" rel="stylesheet" type="text/less"/>
+      </xpath>
+    </template>
+
+    <template id="assets_frontend_js" name="Latest Posts JS frontend assets" inherit_id="website.assets_frontend">
+      <xpath expr="//script[last()]" position="after">
+        <script src="/snippet_latest_posts/static/src/js/s_latest_posts_frontend.js" rel="stylesheet" type="text/less"/>
+      </xpath>
+    </template>
+
+    <template id="assets_editor" name="Latest Posts JS editor assets" inherit_id="website.assets_editor">
+      <xpath expr="." position="inside">
+        <script src="/snippet_latest_posts/static/src/js/s_latest_posts_editor.js" rel="stylesheet" type="text/less"/>
+      </xpath>
+    </template>
+
+  </data>
+</openerp>

+ 51 - 0
views/options.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+  <data>
+
+    <template id="s_latest_posts_place_in_tab" inherit_id="website_less.snippets">
+      <xpath expr="//div[@id='snippet_feature']" position="inside">
+        <t t-snippet="snippet_latest_posts.s_latest_posts" t-thumbnail="/snippet_latest_posts/static/src/img/s_latest_posts.jpg"/>
+        <t t-snippet="snippet_latest_posts.s_latest_posts_big_picture" t-thumbnail="/snippet_latest_posts/static/src/img/s_latest_posts_big_picture.jpg"/>
+      </xpath>
+    </template>
+
+    <!-- Snippet's Options -->
+    <template id="s_latest_posts_option" inherit_id="website_less.snippet_options">
+      <xpath expr="//div[@data-js='blog-style']" position="before">
+
+        <div data-js='js_get_posts' data-selector=".js_get_posts" />
+        
+        <div data-js='js_get_posts_selectBlog' data-selector=".js_get_posts">
+          <li class="dropdown-submenu">
+            <a tabindex="-1" href="#">Limitado por Blogs</a>
+            <ul class="dropdown-menu">
+              <li data-filter_by_blog_id="0"><a>Todos los Blogs</a></li> 
+              <!-- the blogs' list will be generated in js -->
+            </ul>
+          </li>
+        </div>
+
+        <div data-js='js_get_posts_limit' data-selector=".js_get_posts">
+          <li class="dropdown-submenu">
+            <a tabindex="-1" href="#">Limitar por Cantidad...</a>
+            <ul class="dropdown-menu">
+              <li data-posts_limit="1"><a>1 post</a></li> 
+              <li data-posts_limit="2"><a>2 posts</a></li>  
+              <li data-posts_limit="3"><a>3 posts</a></li>
+              <li data-posts_limit="4"><a>4 posts</a></li>
+              <li data-posts_limit="5"><a>5 posts</a></li>
+              <li data-posts_limit="6"><a>6 posts</a></li>
+              <li data-posts_limit="7"><a>7 posts</a></li>
+              <li data-posts_limit="8"><a>8 posts</a></li>
+              <li data-posts_limit="9"><a>9 posts</a></li>
+              <li data-posts_limit="10"><a>10 posts</a></li>
+            </ul>
+          </li>
+        </div>
+
+      </xpath>         
+    </template>
+
+
+</data>
+</openerp>

+ 83 - 0
views/s_latest_posts.xml

@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+  <data>
+
+    <!-- ======== LATEST POSTS  ========  -->
+
+    <!-- Snippet's Body -->
+    <template id="s_latest_posts" name="Latest Posts List">
+      <section class="s_latest_posts">
+        <div class="container">
+          <h2 class="text-center">Ultimas Publicaciones</h2>
+          <div class="js_get_posts"  data-loading="true" data-template="snippet_latest_posts.media_list_template" />
+          <hr/>
+        </div>
+      </section>
+    </template>
+    
+    <!-- Latest posts's Template -->
+    <template id="media_list_template">
+      <t t-if="not posts">
+        <t t-call="snippet_latest_posts.no_blog_posts"/>
+      </t>
+      <t t-foreach="posts" t-as="p">
+        <div class="post media media_list_template">
+          <div class="pull-left">
+            <a t-att-href="'/blog/%s/post/%s' % (p.blog_id.id, p.id)">
+              <div class="media-object loading_container thumb" t-att-style="'background-image: url(%s)' % p.background_image"/>
+            </a>
+          </div>
+          <div class="media-body">
+            <h4 class="media-heading" t-field="p.name"/>
+            <p t-field="p.subtitle"/>
+            <a class="btn btn-default" t-att-href="'/blog/%s/post/%s' % (p.blog_id.id, p.id)">Ver Más</a>
+          </div>
+        </div>
+      </t>
+    </template>
+    
+    <!-- No post here, template -->
+    <template id="no_blog_posts">
+      <div class="container readable">
+        <div class="alert alert-warning alert-dismissible text-center" role="alert">
+          <button type="button" class="close" data-dismiss="alert" aria-label="Close">
+            <span aria-hidden="true">&#215;</span>
+          </button>
+          <h3>NO hay publicaciones aqui...</h3>
+          <p>Asegurate que tus publicaciones esten publicadas</p>
+        </div>
+      </div>
+    </template> 
+
+
+
+    <!-- ======== LATEST POSTS BIG PICTURES  ========  -->
+
+    <!-- Snippet's Body -->
+    <template id="s_latest_posts_big_picture" name="Latest Posts Big Images">
+      <section class="s_latest_posts_big_picture">
+        <h2 class="text-center sans-serif">Ultimas publicaciones</h2>
+        <div class="js_get_posts" data-loading="true" data-template="snippet_latest_posts.s_latest_posts_big_picture_template" />
+      </section>
+    </template>
+    
+    <template id="s_latest_posts_big_picture_template">
+      <t t-if="not posts">
+        <t t-call="snippet_latest_posts.no_blog_posts"/>
+      </t>
+      <t t-foreach="posts" t-as="p">
+        <figure class="loading_container col-sm-6">
+          <div class="content">
+            <div class="thumb" t-att-style="'background-image: url(%s)' % p.background_image" />
+            <figcaption>
+              <h2 t-field="p.name" />
+              <p t-field="p.subtitle"/>
+              <a t-att-href="'/blog/%s/post/%s' % (p.blog_id.id, p.id)">Ver Más</a>
+            </figcaption>
+          </div>
+        </figure>
+      </t>
+    </template>
+
+  </data>
+</openerp>