Преглед изворни кода

Ability to propagate collection sponsor to its docs and also append default ad unit

Markus Ochel пре 12 година
родитељ
комит
80bf658678

+ 2 - 1
TASKS.todo

@@ -3,9 +3,10 @@ New Admin Features:
  ✔ Arbitrary site redirects @done (13-01-05 20:22)
  ☐ Offline and crash recovery support
  ☐ Image insertion into content
- ☐ List sorting options
+ ✔ List sorting options @done (13-02-16 21:14)
  ☐ Option to remove dates from bottom of pages
  ☐ Create a layout selection feature
+ ✔ Sponsor `include_default_ad_unit` flag option @done (13-02-16 21:22)
 
  --- ✄ -----------------------
 

+ 3 - 0
admin/controllers/collections.coffee

@@ -19,6 +19,7 @@ class CollectionForm extends Spine.Controller
     'form':                    'form'
     'select[name=site]':       'formSite'
     'select[name=sponsor_id]': 'formSponsorId'
+    'input[name=sponsor_propagate]': 'formSponsorPropagate'
     'input[name=name]':        'formName'
     'input[name=pinned]':      'formPinned'
     'input[name=hidden]':      'formHidden'
@@ -76,6 +77,7 @@ class CollectionForm extends Spine.Controller
     if @editing or @copying
       @formSite.val(@item.site)
       @formSponsorId.val(@item.sponsor_id)
+      @formSponsorPropagate.prop('checked', @item.sponsor_propagate)
       @formPinned.prop('checked', @item.pinned)
       @formHidden.prop('checked', @item.hidden)
     else
@@ -119,6 +121,7 @@ class CollectionForm extends Spine.Controller
     @item._attachments = @fileUploadUI.attachments
 
     # Take care of some boolean checkboxes
+    @item.sponsor_propagate = @formSponsorPropagate.is(':checked')
     @item.pinned = @formPinned.is(':checked')
     @item.hidden = @formHidden.is(':checked')
 

+ 4 - 0
admin/controllers/sponsors.coffee

@@ -18,6 +18,7 @@ class SponsorForm extends Spine.Controller
     'select[name=contact_id]': 'formContactId'
     'select[name=format]':     'formFormat'
     'input[name=show_label]':  'formShowLabel'
+    'input[name=include_default_ad_unit]':  'formIncludeDefaultAdUnit'
     'textarea[name=content]':  'formContent'
     '.upload-ui':              'fileUploadContainer'
     '.save-button':            'saveButton'
@@ -70,8 +71,10 @@ class SponsorForm extends Spine.Controller
       @formContactId.val(@item.contact_id)
       @formFormat.val(@item.format)
       @formShowLabel.prop('checked', @item.show_label)
+      @formIncludeDefaultAdUnit.prop('checked', @item.include_default_ad_unit)
     else
       @formShowLabel.prop('checked', true)
+      @formIncludeDefaultAdUnit.prop('checked', true)
 
     # Files upload area
     @fileUploadUI = new FileUploadUI
@@ -99,6 +102,7 @@ class SponsorForm extends Spine.Controller
     
     # Take care of some boolean checkboxes
     @item.show_label = @formShowLabel.is(':checked')
+    @item.include_default_ad_unit = @formIncludeDefaultAdUnit.is(':checked')
 
     # Save the item and make sure it validates
     if @item.save()

+ 2 - 1
admin/models/collection.coffee

@@ -7,7 +7,7 @@ moment = require('lib/moment')
 BaseModel = require('models/base')
 
 class Collection extends BaseModel
-  @configure "Collection", "site", "slug", "name", "intro", "photo", "pinned", "hidden", "updated_at", "sponsor_id", "sponsor_start", "sponsor_end", "sponsors_history", "_attachments"
+  @configure "Collection", "site", "slug", "name", "intro", "photo", "pinned", "hidden", "updated_at", "sponsor_id", "sponsor_start", "sponsor_end", "sponsor_propagate", "sponsors_history", "_attachments"
 
   @extend @CouchAjax
 
@@ -40,6 +40,7 @@ class Collection extends BaseModel
     # Convert some boolean properties
     @pinned = Boolean(@pinned)
     @hidden = Boolean(@hidden)
+    @sponsor_propagate = Boolean(@sponsor_propagate)
 
     # Sponsor dates if setting a sponsor
     if @sponsor_id

+ 2 - 1
admin/models/sponsor.coffee

@@ -8,7 +8,7 @@ BaseModel = require('models/base')
 utils = require('lib/utils')
 
 class Sponsor extends BaseModel
-  @configure "Sponsor", "format", "name", "link", "label", "show_label", "content", "image", "note", "contact_id", "_attachments"
+  @configure "Sponsor", "format", "name", "link", "label", "show_label", "content", "include_default_ad_unit", "image", "note", "contact_id", "_attachments"
   
   @extend @CouchAjax
 
@@ -20,6 +20,7 @@ class Sponsor extends BaseModel
 
     # Convert some boolean properties
     @show_label = Boolean(@show_label)
+    @include_default_ad_unit = Boolean(@include_default_ad_unit)
 
     # Some content transformation
     @content = utils.cleanContent @content

+ 3 - 0
admin/templates/collection-form.html

@@ -74,6 +74,9 @@
           <option value="{{id}}">{{name}}</option>
           {{/each}}
         </select>
+        <br class="clearfix">
+        <label>Propagate</label>
+        <input type="checkbox" name="sponsor_propagate">
       </div>
       <div class="field-right">
         <label>Start</label>

+ 1 - 0
admin/templates/sponsor-form.html

@@ -33,6 +33,7 @@
     <div class="field">
       <label>Content</label>
       <textarea class="default" name="content" placeholder="Type text or paste embed code of an image or video">{{content}}</textarea>
+      <input type="checkbox" name="include_default_ad_unit" style="display: inline; margin: 7px 10px 7px 0;"> <small>Include default ad unit (if available for site)</small>
     </div>
     <div class="field">
       <label>Note</label>

+ 30 - 1
site/lib/app.coffee

@@ -3,6 +3,9 @@ $       = require('jquery')
 moment  = require('lib/moment')
 require('lib/fastclick')
 
+handlebars = require('handlebars')
+settings  = require('settings/root')
+
 exports.initialize = (config) ->
   # Use the fastclick module for touch devices.
   # Add a class of `needsclick` of the original click
@@ -151,6 +154,7 @@ setupNavMenus = ->
     collectionId = $collectionNav.attr('data-id')
     collectionSlug = $collectionNav.attr('data-slug')
     if collectionSlug
+      # Grab the docs for the collection from DB
       $collectionNav.show()
       $.ajax
         type: 'GET'
@@ -159,13 +163,37 @@ setupNavMenus = ->
         success: (data) ->
           if data
             data = JSON.parse(data)
+            collectionObject = null
+            collectionSponsor = null
+            
             for row in data.rows
               doc = row.doc
-              collectionDocs.push(doc)
+              collectionDocs.push(doc) if doc.type in settings.app.content_types
+              collectionObject ?= doc if doc.type is 'collection'
+              collectionSponsor ?= doc if doc.type is 'sponsor'
+
+            # Loop through docs within collection
+            for doc in collectionDocs
               url = "/#{doc.type}/#{doc.slug}"
               selectedClass = if window.location.pathname is url then 'active' else ''
               $collectionNavList.append "<li><a href=\"#{url}\" class=\"#{selectedClass}\" data-id=\"#{doc._id}\">#{doc.title}</a></li>"
             setupCollectionDocsNav(collectionDocs, $collectionNavList)
+
+            # Setup collcetion sponsor inheritance if needed
+            $loadSponsorSection = $('div[data-action="load-sponsor"]')
+            if collectionSponsor and $loadSponsorSection
+              collectionSponsor.text_format = collectionSponsor.format is 'text'
+              collectionSponsor.image_format = collectionSponsor.format is 'image'
+              collectionSponsor.video_format = collectionSponsor.format is 'video'
+              collectionSponsor.embed_format = collectionSponsor.format is 'embed'
+              collectionSponsor.for_type = collectionObject?.type
+              collectionSponsor.for_type_tc = collectionObject?.type_tc
+              $html = $(handlebars.templates['partials/sponsor.html']({sponsor: collectionSponsor}, {}))
+              $html.insertAfter($loadSponsorSection)
+              if collectionSponsor.include_default_ad_unit
+                $loadSponsorSection.prependTo($html).removeClass('hide')
+              else
+                $loadSponsorSection.remove()
     else
       # Remove the doc nav prev and next arrows
       $('.doc-nav').remove()
@@ -228,6 +256,7 @@ setupCollectionDocsNav = (docs, $collectionNavList) ->
         else if e.which is 39
           $docNavNext.click()
 
+
 setupSmoothScrolling = ->
   smoothScroll = (hash) ->
     $target = $(hash)

+ 29 - 1
site/server/lists.coffee

@@ -39,6 +39,7 @@ exports.home = (head, req) ->
     on_dev: utils.isDev(req)
     area: 'home'
     site: site
+    type: 'home'
     title: "#{site.name}"
     content: templates.render "home.html", req,
       collections: collections
@@ -109,6 +110,9 @@ exports.collection = (head, req) ->
       sponsor.embed_format = sponsor.format is 'embed'
       sponsor.for_type = collection.type
       sponsor.for_type_tc = collection.type_tc
+      # Let's also pass the site's default ad unit if asked for it
+      if site and site.default_ad_unit and site.default_ad_enabled and sponsor.include_default_ad_unit
+        sponsor.content = site.default_ad_unit + sponsor.content
     else
       # let's remove the sponsor
       sponsor = null
@@ -127,6 +131,7 @@ exports.collection = (head, req) ->
       on_dev: utils.isDev(req)
       area: 'collection'
       site: site
+      type: 'collection'
       title: collection.name
       content: templates.render 'collection.html', req,
         collection: collection
@@ -190,6 +195,7 @@ exports.docs = (head, req) ->
     on_dev: utils.isDev(req)
     area: 'docs'
     site: site
+    type: 'docs'
     title: 'Docs List'
     content: templates.render 'docs.html', req,
       docs: docs
@@ -263,6 +269,8 @@ exports.doc = (head, req) ->
     doc.fresh = utils.isItFresh(doc.updated_at)
     return doc
 
+  collection = collections?[0] # primary one
+
   if sponsor
     # Check for strat/end dates of sponsorship
     sponsor_start = moment.utc(doc.sponsor_start)
@@ -276,10 +284,29 @@ exports.doc = (head, req) ->
       sponsor.embed_format = sponsor.format is 'embed'
       sponsor.for_type = doc.type
       sponsor.for_type_tc = doc.type_tc
+      # Let's also pass the site's default ad unit if asked for it
+      if site and site.default_ad_unit and site.default_ad_enabled and sponsor.include_default_ad_unit
+        sponsor.content = site.default_ad_unit + sponsor.content
     else
       # let's remove the sponsor
       sponsor = null
 
+  # Let's use the collection's sponsor if there was no doc sponsor
+  # and the sponsor was setup to propogate to entire collection's docs
+  if not sponsor and collection and collection.sponsor_id and collection.sponsor_propagate
+    # Check for strat/end dates of sponsorship
+    sponsor_start = moment.utc(collection.sponsor_start)
+    sponsor_end = moment.utc(collection.sponsor_end)
+    now = moment.utc()
+    if sponsor_start.diff(now) <= 0 and sponsor_end.diff(now) >= 0
+      sponsor =
+        load_on_client: true
+        collection_id: collection._id
+        sponsor_id: collection.sponsor_id
+      # Let's also pass the site's default ad unit in case we like to use it
+      if site and site.default_ad_unit and site.default_ad_enabled
+        sponsor.default_ad_unit = site.default_ad_unit
+
   if doc
     if not sponsor and site and site.default_ad_unit and site.default_ad_enabled
       # In this case create a new sponsor object and use
@@ -294,11 +321,12 @@ exports.doc = (head, req) ->
       on_dev: utils.isDev(req)
       area: 'doc'
       site: site
+      type: doc.type
       title: doc.title
       content: templates.render 'doc.html', req,
         doc: doc
         collections: collections
-        collection: collections?[0] # primary one
+        collection: collection
         author: author
         sponsor: sponsor
         blocks: blocks

+ 24 - 2
site/server/rewrites.coffee

@@ -39,17 +39,39 @@ module.exports = [
   }
 
   # Collection JSON view - list of docs ONLY
+  # {
+  #   from: '/render/:site/json/collection/:slug',
+  #   to: '_view/docs_by_collection',
+  #   query: {
+  #     startkey: [':site', ':slug', 'doc', {}],
+  #     endkey: [':site', ':slug', 'doc'],
+  #     descending: 'true',
+  #     include_docs: 'true'
+  #   }
+  # }
+
+  # Collection JSON view - all related rows 
   {
     from: '/render/:site/json/collection/:slug',
     to: '_view/docs_by_collection',
     query: {
-      startkey: [':site', ':slug', 'doc', {}],
-      endkey: [':site', ':slug', 'doc'],
+      startkey: [':site', ':slug', {}],
+      endkey: [':site', ':slug'],
       descending: 'true',
       include_docs: 'true'
     }
   }
 
+  # Collection's Sponsor JSON view - sponsor ONLY
+  # {
+  #   from: '/render/:site/json/collection-sponsor/:slug',
+  #   to: '_view/docs_by_collection',
+  #   query: {
+  #     key: [':site', ':slug', 'sponsor', {}],
+  #     include_docs: 'true'
+  #   }
+  # }
+
   # Search JSON endpoint
   {
     from: '/render/:site/json/search',

+ 1 - 0
site/static/css/responsive.styl

@@ -219,6 +219,7 @@
       right: 0
       margin: 10px
       padding: 10px
+      z-index: 10
 
 
 // Heights

+ 1 - 1
site/templates/base.html

@@ -44,7 +44,7 @@
   {{#if site.css}}<style>{{{site.css}}}</style>{{/if}}
 
 </head>
-<body class="theme-{{site.theme}} layout-{{site.layout}} area-{{area}} type-{{doc.type}}">
+<body class="theme-{{site.theme}} layout-{{site.layout}} area-{{area}} type-{{type}}">
 
   <div class="container">
     <header>

+ 2 - 0
site/templates/collection.html

@@ -9,9 +9,11 @@
   </section>
   {{/if}}
 
+  {{#if collection.intro_html}}
   <section class="intro">
     {{{collection.intro_html}}}
   </section>
+  {{/if}}
   
   {{{include "partials/sponsor.html"}}}
 

+ 41 - 35
site/templates/partials/sponsor.html

@@ -1,42 +1,48 @@
-  {{#if sponsor}}
-  <section class="sponsor {{sponsor.for_type}}-sponsor">
-    {{#if sponsor.show_label}}
-    <div class="label">
-      {{#if sponsor.label}}
-        {{{sponsor.label}}}
-      {{else}}
-        Supported By
+{{#if sponsor}}
+
+  {{#if sponsor.load_on_client}}
+    <div class="hide embed" data-action="load-sponsor">{{{sponsor.default_ad_unit}}}</div>
+  {{else}}
+    <section class="sponsor {{sponsor.for_type}}-sponsor">
+      {{#if sponsor.show_label}}
+      <div class="label">
+        {{#if sponsor.label}}
+          {{{sponsor.label}}}
+        {{else}}
+          Supported By
+        {{/if}}
+      </div>
+      {{/if}}
+
+      {{#if sponsor.text_format}}
+      <div class="name">
+        {{#if sponsor.link}}
+          <a href="{{sponsor.link}}" target="_blank" rel="nofollow">{{{sponsor.name}}}</a>
+        {{else}}
+          {{{sponsor.name}}}
+        {{/if}}
+      </div>
+      <div class="content">{{{sponsor.content}}}</div>
       {{/if}}
-    </div>
-    {{/if}}
 
-    {{#if sponsor.text_format}}
-    <div class="name">
-      {{#if sponsor.link}}
-        <a href="{{sponsor.link}}">{{{sponsor.name}}}</a>
-      {{else}}
-        {{{sponsor.name}}}
+      {{#if sponsor.image_format}}
+      <div class="image">
+        {{#if sponsor.link}}
+          <a href="{{sponsor.link}}" target="_blank" rel="nofollow"><img src="{{#if sponsor.image}}{{sponsor.image}}{{else}}{{sponsor.content}}{{/if}}"></a>
+        {{else}}
+          <img src="{{#if sponsor.image}}{{sponsor.image}}{{else}}{{sponsor.content}}{{/if}}">
+        {{/if}}
+      </div>
       {{/if}}
-    </div>
-    <div class="content">{{{sponsor.content}}}</div>
-    {{/if}}
 
-    {{#if sponsor.image_format}}
-    <div class="image">
-      {{#if sponsor.link}}
-        <a href="{{sponsor.link}}" target="_blank"><img src="{{#if sponsor.image}}{{sponsor.image}}{{else}}{{sponsor.content}}{{/if}}"></a>
-      {{else}}
-        <img src="{{#if sponsor.image}}{{sponsor.image}}{{else}}{{sponsor.content}}{{/if}}">
+      {{#if sponsor.video_format}}
+      <div class="video">{{{sponsor.content}}}</div>
       {{/if}}
-    </div>
-    {{/if}}
 
-    {{#if sponsor.video_format}}
-    <div class="video">{{{sponsor.content}}}</div>
-    {{/if}}
+      {{#if sponsor.embed_format}}
+      <div class="embed">{{{sponsor.content}}}</div>
+      {{/if}}
+    </section>
+  {{/if}}
 
-    {{#if sponsor.embed_format}}
-    <div class="embed">{{{sponsor.content}}}</div>
-    {{/if}}
-  </section>
-  {{/if}}
+{{/if}}