Parcourir la source

Fixed few bugs and tweaked and enhanced stuff

Markus Ochel il y a 13 ans
Parent
commit
716a2c8bc1

+ 1 - 1
admin/controllers/authors.coffee

@@ -117,8 +117,8 @@ class AuthorForm extends Spine.Controller
     e.preventDefault()
     
   deactivate: ->
-    super
     @el.scrollTop(0, 0)
+    super
 
 
 class AuthorList extends Spine.Controller

+ 1 - 1
admin/controllers/blocks.coffee

@@ -101,8 +101,8 @@ class BlockForm extends Spine.Controller
     e.preventDefault()
     
   deactivate: ->
-    super
     @el.scrollTop(0, 0)
+    super
 
 
 class BlockList extends Spine.Controller

+ 13 - 12
admin/controllers/collections.coffee

@@ -1,6 +1,7 @@
 Spine       = require('spine/core')
 # $           = Spine.$
 templates   = require('duality/templates')
+utils       = require('lib/utils')
 
 Collection  = require('models/collection')
 Sponsor     = require('models/sponsor')
@@ -16,6 +17,8 @@ class CollectionForm extends Spine.Controller
     'form':                    'form'
     'select[name=site]':       'formSite'
     'select[name=sponsor_id]': 'formSponsorId'
+    'input[name=name]':        'formName'
+    'input[name=pinned]':      'formPinned'
     '.save-button':            'saveButton'
     '.cancel-button':          'cancelButton'
 
@@ -25,6 +28,7 @@ class CollectionForm extends Spine.Controller
     'click .cancel-button':     'cancel'
     'click .delete-button':     'destroy'
     'change select[name=site]': 'siteChange'
+    'blur input[name=slug]':    'updateSlug'
 
   constructor: ->
     super
@@ -54,6 +58,7 @@ class CollectionForm extends Spine.Controller
     if @editing
       @formSite.val(@item.site)
       @formSponsorId.val(@item.sponsor_id)
+      @formPinned.prop('checked', @item.pinned)
     else
       @formSite.val(@stack.stack.filterBox.siteId)
     @siteChange()
@@ -66,6 +71,11 @@ class CollectionForm extends Spine.Controller
     else
       $siteSelected.html ""
 
+  updateSlug: (e) =>
+    slug = $(e.currentTarget)
+    unless slug.val()
+      slug.val utils.cleanSlug(@formName.val())
+
   save: (e) ->
     e.preventDefault()
     if @editing
@@ -73,18 +83,9 @@ class CollectionForm extends Spine.Controller
     else
       @item = new Collection().fromForm(@form)
 
-    # Convert some boolean properties
-    @item.pinned = Boolean(@item.pinned)
+    # Take care of some boolean checkboxes
+    @item.pinned = @formPinned.is(':checked')
 
-    # Take care of some dates if need be
-    try
-      if @item.updated_at
-        @item.updated_at = new Date(@item.updated_at).toJSON()
-      else
-        @item.updated_at = new Date().toJSON()
-    catch error
-      @showError "Date format is wrong. Use this format: 'Feb 20 2012 6:30 PM'"
-    
     # Save the item and make sure it validates
     if @item.save()
       @back()
@@ -117,8 +118,8 @@ class CollectionForm extends Spine.Controller
     e.preventDefault()
     
   deactivate: ->
-    super
     @el.scrollTop(0, 0)
+    super
 
 
 class CollectionList extends Spine.Controller

+ 1 - 1
admin/controllers/contacts.coffee

@@ -82,8 +82,8 @@ class ContactForm extends Spine.Controller
     e.preventDefault()
     
   deactivate: ->
-    super
     @el.scrollTop(0, 0)
+    super
 
 
 class ContactList extends Spine.Controller

+ 23 - 23
admin/controllers/essays.coffee

@@ -1,6 +1,7 @@
 Spine       = require('spine/core')
-# $           = Spine.$
+$           = Spine.$
 templates   = require('duality/templates')
+utils       = require('lib/utils')
 
 MultiSelectUI = require('controllers/ui/multi-select')
 FileUploadUI  = require('controllers/ui/file-upload')
@@ -22,8 +23,10 @@ class EssayForm extends Spine.Controller
     'select[name=site]':       'formSite'
     'select[name=author_id]':  'formAuthorId'
     'select[name=sponsor_id]': 'formSponsorId'
+    'input[name=title]':       'formTitle'
+    'input[name=published]':   'formPublished'
     '.collections-list':       'collectionsList'
-    '.files-list':             'filesList'
+    '.upload-ui':              'fileUploadContainer'
     '.save-button':            'saveButton'
     '.cancel-button':          'cancelButton'
 
@@ -33,6 +36,7 @@ class EssayForm extends Spine.Controller
     'click .cancel-button':     'cancel'
     'click .delete-button':     'destroy'
     'change select[name=site]': 'siteChange'
+    'blur input[name=slug]':    'updateSlug'
 
   constructor: ->
     super
@@ -65,15 +69,18 @@ class EssayForm extends Spine.Controller
     if @editing
       @formSite.val(@item.site)
       @formSponsorId.val(@item.sponsor_id)
+      @formPublished.prop('checked', @item.published)
     else
       @formSite.val(@stack.stack.filterBox.siteId)
+      @formPublished.prop('checked', true)
     @siteChange()
 
     # Files upload area
-    @fileUpload = new FileUploadUI
+    @fileUploadUI = new FileUploadUI
       docId: @item.id
+      selectedFile: @item.photo
       attachments: @item._attachments
-    @filesList.find('.upload-ui').html @fileUpload.el
+    @fileUploadContainer.html @fileUploadUI.el
 
   siteChange: ->
     $siteSelected = @formSite.parents('.field').find('.site-selected')
@@ -95,11 +102,16 @@ class EssayForm extends Spine.Controller
   
   makeCollectionsList: (site) ->
     collections = Collection.findAllByAttribute('site', site.id)
-    @collectionSelect = new MultiSelectUI
+    @collectionSelectUI = new MultiSelectUI
       items: collections
       selectedItems: (c.id for c in @item.collections)
       valueFields: ['id','slug']
-    @collectionsList.html @collectionSelect.el
+    @collectionsList.html @collectionSelectUI.el
+
+  updateSlug: (e) =>
+    slug = $(e.currentTarget)
+    unless slug.val()
+      slug.val utils.cleanSlug(@formTitle.val())
 
   save: (e) ->
     e.preventDefault()
@@ -108,23 +120,11 @@ class EssayForm extends Spine.Controller
     else
       @item = new Essay().fromForm(@form)
 
-    # Convert some boolean properties
-    @item.published = Boolean(@item.published)
-
-    @item.collections = @collectionSelect.selected()
-
-    # TODO: Take care of files and photo
-    @item._attachments = @fileUpload.attachments
-    @item.photo = null
+    @item.collections = @collectionSelectUI.selected()
+    @item._attachments = @fileUploadUI.attachments
 
-    # Take care of some dates if need be
-    try
-      if @item.published_at
-        @item.published_at = new Date(@item.published_at).toJSON()
-      else
-        @item.published_at = new Date().toJSON()
-    catch error
-      @showError "Date format is wrong. Use this format: 'Feb 20 2012 6:30 PM'"
+    # Take care of some boolean checkboxes
+    @item.published = @formPublished.is(':checked')
     
     # Save the item and make sure it validates
     if @item.save()
@@ -158,8 +158,8 @@ class EssayForm extends Spine.Controller
     e.preventDefault()
     
   deactivate: ->
-    super
     @el.scrollTop(0, 0)
+    super
 
 
 class EssayList extends Spine.Controller

+ 1 - 1
admin/controllers/sites.coffee

@@ -91,8 +91,8 @@ class SiteForm extends Spine.Controller
     e.preventDefault()
     
   deactivate: ->
-    super
     @el.scrollTop(0, 0)
+    super
 
 
 class SiteList extends Spine.Controller

+ 1 - 1
admin/controllers/sponsors.coffee

@@ -91,8 +91,8 @@ class SponsorForm extends Spine.Controller
     e.preventDefault()
     
   deactivate: ->
-    super
     @el.scrollTop(0, 0)
+    super
 
 
 class SponsorList extends Spine.Controller

+ 81 - 19
admin/controllers/ui/file-upload.coffee

@@ -7,9 +7,15 @@ class FileUploadUI extends Spine.Controller
   tag: 'div'
   className: 'ui-file-upload'
   fieldName: 'file_upload'
+  selectedFieldName: 'photo'
   dropzoneText: 'drop or click'
   attachments: {}
   docId: null
+  selectedFile: null
+
+  events:
+    'click ul.files-list > li':   'itemClick'
+    'dblclick ul.files-list > li': 'itemDblClick'
 
   constructor: ->
     super
@@ -17,21 +23,72 @@ class FileUploadUI extends Spine.Controller
 
   render: ->
     @dropzone = $("<div class=\"dropzone\">#{@dropzoneText}</div>")
-    @fileInput = $("<input type=\"file\" name=\"#{@fieldName}\" style=\"display: none;\"/>")
+    @fileInput = $("<input type=\"file\" name=\"#{@fieldName}\" style=\"display: none;\">")
+    @fileSelectedInput = $("<input type=\"hidden\" name=\"#{@selectedFieldName}\">")
     @fileName = $("<div class=\"filename\"/>")
-    @filesList = $("<ul class=\"list\"/>")
-    @el.append @dropzone, @fileInput, @fileName, @filesList
+    @filesList = $("<ul class=\"files-list\"/>")
+    @el.append @dropzone, @fileInput, @fileSelectedInput, @fileName, @filesList
     @setupList()
-    @setupEvents()
+    @setupZoneEvents()
 
   setupList: ->
-    if @docId
-      names = (prop for prop of @attachments)
-      for name in names
-        file = @attachments[name]
-        @filesList.append "<li><img src=\"/file/#{@docId}/#{name}\"></li>"
+    names = (prop for prop of @attachments)
+    for name in names
+      file = @attachments[name]
+      @addToList(name, file.content_type)
+
+    if @selectedFile
+      item = @filesList.find("> li[data-filename='#{@selectedFile}']")
+      @selectItem(item) if item
+    
+  addToList: (name, type, dataURL = null) =>
+    $item = $('<li/>')
+    url = if @docId then "/file/#{@docId}/#{name}" else ''
+    
+    $item.attr('data-filename', name)
+         .attr('data-url', url)
+    if type.match(/image.*/)
+      $img = $('<img>')
+      if dataURL
+        $img.attr('src', dataURL)
+      else if @docId
+        $img.attr('src', url)
+      $item.append $img
+    else
+      $item.append $("<div><em>#{type}</em><br>#{name}</div>")
+
+    @filesList.append $item
+
+  itemClick: (e) ->
+    # Selecting the main photo
+    e.preventDefault()
+    @selectItem(e.currentTarget)
+
+  itemDblClick: (e) ->
+    # Removing an attachment
+    e.preventDefault()
+    @removeItem(e.currentTarget)
+
+  selectItem: (item) =>
+    @resetSelection()
+    if item
+      name = $(item).attr('data-filename')
+      $(item).addClass('selected')
+      @fileName.html "Selected: #{name}"
+      @fileSelectedInput.val(name)
+
+  resetSelection: ->
+    @filesList.children().removeClass('selected')
+    @fileName.html ''
+    @fileSelectedInput.val('')
+
+  removeItem: (item) =>
+    if item
+      name = $(item).attr('data-filename')
+      if confirm "Remove this attachment?\n#{name}"
+        delete @attachments[name]
 
-  setupEvents: ->
+  setupZoneEvents: ->
     @dropzone.on 'dragenter dragover', (e) ->
       e.originalEvent.preventDefault()
       e.originalEvent.stopPropagation()
@@ -40,19 +97,20 @@ class FileUploadUI extends Spine.Controller
     @dropzone.on 'drop', (e) =>
       e.originalEvent.preventDefault()
       e.originalEvent.stopPropagation()
-      @prepareFiles e.originalEvent.dataTransfer.files
+      @addFiles e.originalEvent.dataTransfer.files
 
     @dropzone.on 'click', (e) =>
       e.preventDefault()
       @fileInput.click()
 
     @fileInput.on 'change', (e) =>
-      @prepareFiles e.target.files
+      @addFiles e.target.files
 
-  prepareFiles: (files) =>
+  addFiles: (files) =>
     if files.length
       file = files[0]
-      name = encodeURIComponent(file.name)
+      name = file.name.replace(/[\ \'\"]/g,'-')
+      name = encodeURIComponent(name)
       type = file.type
       reader = new FileReader
       
@@ -61,17 +119,21 @@ class FileUploadUI extends Spine.Controller
         # Since the result is a data URL and already base64 encoded
         # then just remove the first meta info and we get the file
         # data the we need to save into _attachments in CouchDB
-        result = dataURL.replace(/^data:.*;base64,/, "")
+        result = dataURL.replace(/^data:.*;base64,/, '')
         @attachments[name] =
           content_type: type
           data: result
-        @fileName.html "#{name}"
+        @addToList(name, type, dataURL)
+        @fileName.html name
       
-      reader.addEventListener 'loadstart', (e) => return
+      reader.addEventListener 'loadstart', (e) =>
+        @fileName.html "In progress"
 
-      reader.addEventListener 'progress', (e) => return
+      reader.addEventListener 'progress', (e) =>
+        @fileName.html @fileName.html() + "."
       
-      reader.addEventListener 'error', (e) => return
+      reader.addEventListener 'error', (e) =>
+        @fileName.html "Couldn't load the file."
       
       reader.readAsDataURL file
 

+ 2 - 0
admin/kanso.json

@@ -37,6 +37,8 @@
   },
 
   "app": {
+    "dev_host": "admin.kleks.local",
+    "prod_host": "kleks.markuso.com",
     "themes": ["default","blue","green","brown","teal","grey"]
   }
 }

+ 14 - 0
admin/lib/utils.coffee

@@ -0,0 +1,14 @@
+# Bunch of utility functions
+
+settings    = require('settings/root')
+
+exports.cleanCode = (code) ->
+  code.toLowerCase().replace(/[\ \.\'\"\-]/g, '_')
+
+exports.cleanSlug = (slug) ->
+  slug.toLowerCase().replace(/[\ \.\'\"]/g, '-')
+
+exports.cleanContent = (content) ->
+  dev_host = settings.app.dev_host
+  prod_host = settings.app.prod_host
+  content.replace(/\http(s)?:\/\/admin.kleks.local/g, '')

+ 4 - 0
admin/models/author.coffee

@@ -1,6 +1,8 @@
 Spine = require('spine/core')
 require('lib/spine-couch-ajax')
 
+utils = require('lib/utils')
+
 BaseModel = require('models/base')
 
 class Author extends BaseModel
@@ -15,4 +17,6 @@ class Author extends BaseModel
     return 'Email is required' unless @email
     return 'Bio is required' unless @bio
 
+    return false
+
 module.exports = Author

+ 10 - 0
admin/models/block.coffee

@@ -1,6 +1,8 @@
 Spine = require('spine/core')
 require('lib/spine-couch-ajax')
 
+utils = require('lib/utils')
+
 BaseModel = require('models/base')
 
 class Block extends BaseModel
@@ -11,9 +13,17 @@ class Block extends BaseModel
   @queryOn: ['name','code']
     
   validate: ->
+    @code = utils.cleanCode @code
+
     return 'Site is required' unless @site
     return 'Code is required' unless @code
     return 'Name is required' unless @name
     return 'Content is required' unless @content
 
+    # Some content transformation
+    @content = utils.cleanContent @content
+
+    return false
+
+
 module.exports = Block

+ 30 - 3
admin/models/collection.coffee

@@ -1,23 +1,50 @@
 Spine = require('spine/core')
 require('lib/spine-couch-ajax')
 
+utils = require('lib/utils')
+
 BaseModel = require('models/base')
 
 class Collection extends BaseModel
-  @configure "Collection", "site", "slug", "name", "intro", "pinned", "updated_at", "sponsor_id", "sponsor_start", "sponsor_end"
+  @configure "Collection", "site", "slug", "name", "intro", "pinned", "updated_at", "sponsor_id", "sponsor_start", "sponsor_end", "_attachments"
 
   @extend Spine.Model.CouchAjax
 
   @queryOn: ['name','slug']
     
   validate: ->
+    @slug = utils.cleanSlug @slug
+
     return 'Site is required' unless @site
     return 'Slug is required' unless @slug
     return 'Name is required' unless @name
+    
+    # Take care of some dates
+    try
+      if @updated_at
+        @updated_at = new Date(@updated_at).toJSON()
+      else
+        @updated_at = new Date().toJSON()
+    catch error
+      return "Date format is wrong. Use this format: 'Feb 20 2012 6:30 PM'"
+    
+    # Convert some boolean properties
+    @pinned = Boolean(@pinned)
+
+    # Sponsor dates if setting a sponsor
     if @sponsor_id
       return 'Sponsor Start Date is required' unless @sponsor_start
       return 'Sponsor End Date is required' unless @sponsor_end
-      if new Date(@sponsor_start) >= new Date(@sponsor_end)
-        return 'Sponsor Start Date cannot be later the End Date'
+      try
+        if new Date(@sponsor_start).toJSON() >= new Date(@sponsor_end).toJSON()
+          return 'Sponsor Start Date cannot be later than End Date'
+      catch error
+        return "Sponsor date format is wrong. Use this format: 'Feb 20 2012 6:30 PM'"
+
+    # Some content transformation
+    @intro = utils.cleanContent @intro
+
+    return false
+
 
 module.exports = Collection

+ 4 - 0
admin/models/contact.coffee

@@ -1,6 +1,8 @@
 Spine = require('spine/core')
 require('lib/spine-couch-ajax')
 
+utils = require('lib/utils')
+
 BaseModel = require('models/base')
 
 class Contact extends BaseModel
@@ -13,4 +15,6 @@ class Contact extends BaseModel
   validate: ->
     return 'Name is required' unless @name
 
+    return false
+
 module.exports = Contact

+ 31 - 4
admin/models/essay.coffee

@@ -1,6 +1,8 @@
 Spine = require('spine/core')
 require('lib/spine-couch-ajax')
 
+utils = require('lib/utils')
+
 BaseModel = require('models/base')
 
 class Essay extends BaseModel
@@ -17,15 +19,40 @@ class Essay extends BaseModel
   @queryOn: ['title','slug']
     
   validate: ->
-    @updated_at = new Date().toJSON()
-    @published_at = new Date().toJSON() unless @published_at
+    @slug = utils.cleanSlug @slug
+    
     return 'Site is required' unless @site
     return 'Slug is required' unless @slug
     return 'Title is required' unless @title
+
+    # Take care of some dates
+    @updated_at = new Date().toJSON()
+    try
+      if @published_at
+        @published_at = new Date(@published_at).toJSON()
+      else
+        @published_at = new Date().toJSON()
+    catch error
+      return "Date format is wrong. Use this format: 'Feb 20 2012 6:30 PM'"
+
+    # Convert some boolean properties
+    @published = Boolean(@published)
+
+    # Sponsor dates if setting a sponsor
     if @sponsor_id
       return 'Sponsor Start Date is required' unless @sponsor_start
       return 'Sponsor End Date is required' unless @sponsor_end
-      if new Date(@sponsor_start) >= new Date(@sponsor_end)
-        return 'Sponsor Start Date cannot be later the End Date'
+      try
+        if new Date(@sponsor_start).toJSON() >= new Date(@sponsor_end).toJSON()
+          return 'Sponsor Start Date cannot be later than End Date'
+      catch error
+        return "Sponsor date format is wrong. Use this format: 'Feb 20 2012 6:30 PM'"
+
+    # Some content transformation
+    @intro = utils.cleanContent @intro
+    @body = utils.cleanContent @body
+
+    return false
+
 
 module.exports = Essay

+ 4 - 0
admin/models/site.coffee

@@ -1,6 +1,8 @@
 Spine = require('spine/core')
 require('lib/spine-couch-ajax')
 
+utils = require('lib/utils')
+
 BaseModel = require('models/base')
 
 class Site extends BaseModel
@@ -15,4 +17,6 @@ class Site extends BaseModel
     return 'Name is required' unless @name
     return 'Name HTML is required' unless @name_html
 
+    return false
+
 module.exports = Site

+ 9 - 0
admin/models/sponsor.coffee

@@ -1,8 +1,12 @@
 Spine = require('spine/core')
 require('lib/spine-couch-ajax')
 
+utils = require('lib/utils')
+
 BaseModel = require('models/base')
 
+utils = require('lib/utils')
+
 class Sponsor extends BaseModel
   @configure "Sponsor", "format", "name", "link", "label", "content", "note", "contact_id"
   
@@ -14,4 +18,9 @@ class Sponsor extends BaseModel
     @format ?= 'text'
     return 'Name is required' unless @name
 
+    # Some content transformation
+    @content = utils.cleanContent @content
+
+    return false
+
 module.exports = Sponsor

+ 4 - 2
admin/server/validate.coffee

@@ -1,3 +1,5 @@
+utils = require('lib/utils')
+
 exports.validate_doc_update = (newDoc, oldDoc, userCtx) ->
 
   is_admin = if '_admin' in userCtx.roles then true else false
@@ -5,11 +7,11 @@ exports.validate_doc_update = (newDoc, oldDoc, userCtx) ->
   if not is_admin
     throw unauthorized: 'You are not a database admin'
 
-  if newDoc.type is 'essay'    
+  if newDoc.type is 'essay'
     if not newDoc.site
       throw forbidden: 'site is a required field'
 
-    if not newDoc.slug
+    if not utils.cleanSlug newDoc.slug
       throw forbidden: 'slug is a required field'
     
     if not newDoc.title

+ 4 - 4
admin/static/css/common.styl

@@ -111,7 +111,7 @@ select
   padding: 5px 0
   margin: 0
   border: 0
-  border-bottom: 2px dotted #f3f3f3
+  border-bottom: 2px dotted $faintGrey
   border-radius(0)
 
 textarea
@@ -119,7 +119,7 @@ textarea
   min-width: 50%
   height: 120px
   min-height: 60px
-  border-left: 2px dotted #f3f3f3
+  border-left: 2px dotted $faintGrey
   padding-left: 2px
 
 button, .button
@@ -140,10 +140,10 @@ button, .button
   &.plain
     color: $primaryColor
     background: #fff
-    border: 1px solid #f3f3f3
+    border: 1px solid $faintGrey
 
     &:hover
-      background: #fafafa
+      background: lighten($faintGrey, 50%)
 
   &.small
     padding: 0 5px

+ 3 - 2
admin/static/css/mixin.styl

@@ -35,9 +35,10 @@ $zColor         = #E6CBA8
 $a1Color        = #E6B7A8
 
 $primaryColor   = $defaultColor
-$lightGrey      = #c4c4c4
+$lightGrey      = #b0b0b0
+$faintGrey      = #f0f0f0
 $linkColor      = darken($primaryColor, 20%)
-$textColor      = #848484
+$textColor      = #969696
 
 $normalFont     = 300
 $boldFont       = 400

+ 36 - 22
admin/static/css/theme.styl

@@ -15,7 +15,7 @@ $navbarWidth  = 180px
   font-size: 1.65em
   line-height: 1em
   font-weight: $normalFont
-  color: $lightGrey
+  color: #C4C4C4
   letter-spacing: -0.07em
   word-spacing: 0em
   white-space: nowrap
@@ -51,7 +51,7 @@ span.label
     font-weight: $normalFont
     padding: 5px 40px 5px 10px
     margin: 0
-    border: 1px solid #f3f3f3
+    border: 1px solid $faintGrey
     outline: none
 
   .clear-filter
@@ -74,7 +74,7 @@ span.label
 
   .selected-site
     padding: 10px 10px
-    border: 1px solid #f3f3f3
+    border: 1px solid $faintGrey
     border-top: 0
     line-height: 1em
     cursor: pointer
@@ -86,7 +86,7 @@ span.label
       arrow('top', 10px, $lightGrey)
 
     &:hover
-      background: #fafafa
+      background: lighten($faintGrey, 50%)
 
   ul.site-selector
     display: none
@@ -94,7 +94,7 @@ span.label
     margin: 0
     padding: 0
     background: #fff
-    border: 1px solid #f3f3f3
+    border: 1px solid $faintGrey
     border-top: 0
     max-height: 500px
     overflow: auto
@@ -105,7 +105,7 @@ span.label
       cursor: pointer
 
       &:hover
-        background: #fafafa
+        background: lighten($faintGrey, 50%)
 
       &.selected
         opacity: 0.5
@@ -176,7 +176,7 @@ span.label
     overflow: hidden
 
     > .panel
-      background: #f3f3f3
+      background: $faintGrey
       overflow: auto
 
       .content
@@ -200,10 +200,12 @@ span.label
           position: fixed
           width: 26%
           margin-top: -21px
+          margin-left: -5px
           padding-top: 20px
           padding-bottom: 10px
           border-bottom: 1px solid #fff
           background: #F3F3F3
+          box-shadow: 0 5px 0 rgba(0,0,0,0.05)
           z-index: 10
 
           button, .button
@@ -242,13 +244,13 @@ span.label
       list-style-type: none
       margin: 0
       padding: 0
-      border-top: 1px dotted #f3f3f3
+      border-top: 1px dotted $faintGrey
       clearfix()
 
       > li
         font-size: 1.5em
         line-height: 1.5em
-        border-bottom: 1px dotted #f3f3f3
+        border-bottom: 1px dotted $faintGrey
         padding: 0.5em 0
         clearfix()
 
@@ -291,7 +293,7 @@ span.label
 
       .heading
         color: $primaryColor
-        margin: 1em 0
+        margin: 1em 0 0.4em 0
      
       .field
         position: relative
@@ -336,7 +338,7 @@ span.label
           padding-left: 10px
           background: #fff
         
-        &.inline
+        .inline
           
           label
             display: inline-block
@@ -382,12 +384,12 @@ ul.ui-multi-select
     margin: 0 12px 12px 0
     max-width: 100%
     background: #fff
-    border: 1px solid #f3f3f3
+    border: 1px solid $faintGrey
     ellipsis()
     cursor: pointer
 
     &:hover
-      background: #fafafa
+      background: lighten($faintGrey, 50%)
 
     &.selected
       color: $linkColor
@@ -399,31 +401,43 @@ ul.ui-multi-select
   
   .dropzone
     padding: 20px
-    border: 4px dashed #fafafa
-    color: #fafafa
+    border: 4px dashed #fff
+    color: #fff
     font-size: 2em
     text-align: center
     cursor: default
 
   .filename
-    background: #fafafa
+    background: #fff
     padding: 0 5px
     margin-top: -6px
     line-height: 1.7em
     text-align: center
     overflow: hidden
 
-  ul.list
+  ul.files-list
     list-style-type: none
-    margin: 0
+    margin: 20px 0
     padding: 0
 
     li
       position: relative
       float: left
-      width: 49%
-      margin-right: 1%
+      width: 46%
+      margin-right: 4%
       overflow: hidden
+      -webkit-user-select: none
+      cursor: pointer
+
+      &.selected
+        border-color: $primaryColor
+        outline: 4px solid rgba($primaryColor, 0.5)
+
+      > img
+        width: 100%
 
-      img
-        width: 100%
+      > div
+        border: 1px solid #e3e3e3
+        em
+          font-weight: $boldFont
+          font-size: 0.8em

+ 2 - 2
admin/templates/collection-form.html

@@ -22,7 +22,7 @@
     </div>
     <div class="field required">
       <label>Slug</label>
-      <input type="text" name="slug" value="{{slug}}" placeholder="All lowercase and hyphens but and NO spaces">
+      <input type="text" name="slug" value="{{slug}}" placeholder="All lowercase and hyphens but NO spaces">
     </div>
     <div class="field">
       <label>Intro MD/HTML</label>
@@ -31,7 +31,7 @@
     <div class="field">
       <div class="field-left">
         <label>Pinned</label>
-        <input type="checkbox" name="pinned" {{#if pinned}}checked{{/if}}>
+        <input type="checkbox" name="pinned">
       </div>
       <div class="field-right">
         <label>Updated</label>

+ 3 - 3
admin/templates/essay-form.html

@@ -27,7 +27,7 @@
     <div class="field">
       <div class="field-left required">
         <label>Slug</label>
-        <input type="text" name="slug" value="{{slug}}" placeholder="All lowercase and hyphens but and NO spaces">
+        <input type="text" name="slug" value="{{slug}}" placeholder="All lowercase and hyphens but NO spaces">
       </div>
       <div class="field-right required">
         <label>Author</label>
@@ -45,7 +45,7 @@
     <div class="field">
       <div class="field-left">
         <label>Published</label>
-        <input type="checkbox" name="published" {{#if published}}checked{{/if}}>
+        <input type="checkbox" name="published">
       </div>
       <div class="field-right">
         <label>Published At</label>
@@ -64,7 +64,7 @@
     <div class="top-spacer"></div>
 
     <h3 class="heading">Files</h3>
-    <div class="field files-list">
+    <div class="field">
       <div class="upload-ui"></div>
     </div>
 

+ 6 - 6
site/server/lists.coffee

@@ -66,10 +66,10 @@ exports.collection = (head, req) ->
 
   if sponsor
     # Check for strat/end dates of sponsorship
-    start = new Date(collection.sponsor_start)
-    end = new Date(collection.sponsor_end)
+    sponsor_start = new Date(collection.sponsor_start)
+    sponsor_end = new Date(collection.sponsor_end)
     now = new Date()
-    if start <= now and end >= now
+    if sponsor_start <= now and sponsor_end >= now
       # let continue on
       sponsor.text_format = sponsor.format is 'text'
       sponsor.image_format = sponsor.format is 'image'
@@ -180,10 +180,10 @@ exports.essay = (head, req) ->
 
   if sponsor
     # Check for strat/end dates of sponsorship
-    start = new Date(essay.sponsor_start)
-    end = new Date(essay.sponsor_end)
+    sponsor_start = new Date(essay.sponsor_start)
+    sponsor_end = new Date(essay.sponsor_end)
     now = new Date()
-    if start <= now and end >= now
+    if sponsor_start <= now and sponsor_end >= now
       # let continue on
       sponsor.text_format = sponsor.format is 'text'
       sponsor.image_format = sponsor.format is 'image'

+ 5 - 2
site/static/css/theme.styl

@@ -12,7 +12,7 @@
   font-size: 5.25em
   line-height: 0.8em
   font-weight: $normalFont
-  color: $lightGrey
+  color: #C4C4C4
   letter-spacing: -0.07em
   word-spacing: 0em
   white-space: nowrap
@@ -98,6 +98,9 @@
 
 
 article
+  
+  a
+    font-weight: 400
 
   > .intro
     margin-bottom: 2em
@@ -112,7 +115,7 @@ article
     max-width: $normalFontpx
     margin: 0 0 1em 1em
     text-align: center
-    background: #f9f9f9
+    background: lighten($faintGrey, 3%)
     content-box()
 
     .label