Skip to content

Commit

Permalink
[Closes #678] List Portlet
Browse files Browse the repository at this point in the history
List portlets find and return content blocks. The following can be configured:

* Find all of a given content type and display as a list.
* Can limit results
* Can order results (uses AJAX)
* Can sort/reverse sort results
* Can chose list or table views
* Provide a way to custom the view for a given list portlet via creating an application specific version.
* Only works for html.erb files. Format is: app/views/portlets/list/#{name_of_portlet}/_#{view_as}.html.erb
* Name links if content is addressable.

Other Fixes:

* Fix portlet generator so it generates SimpleForm fields.
* New portlets have a prompt for developers to add a suitable description.
  • Loading branch information
peakpg committed Jan 28, 2014
1 parent f3403d4 commit 29cde5b
Show file tree
Hide file tree
Showing 25 changed files with 246 additions and 7 deletions.
1 change: 1 addition & 0 deletions app/assets/javascripts/cms/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//= require jquery.taglist
//= require jquery.ui.all
//= require cms/core_library
//= require cms/content_types
//= require cms/attachment_manager
//= require cms/form_builder
//= require cms/sitemap
Expand Down
13 changes: 13 additions & 0 deletions app/assets/javascripts/cms/content_types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Adds AJAX behavior primarily for List Portlet

jQuery(function($){
var select_tag = $('*[data-role="content_type_selector"]');
var order_field = $('*[data-role="order-fields"]');
if(select_tag.exists()){
select_tag.on('change', function(){
var selected_option = $( this ).val();
console.log("Changed to", selected_option );
order_field.load( '/cms/content_types.js .load > .select', { "content_type": selected_option } )
});
}
});
14 changes: 14 additions & 0 deletions app/controllers/cms/content_types_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require_dependency "cms/application_controller"

module Cms
class ContentTypesController < ApplicationController

def index
content_type = ContentType.named(params[:content_type]).first
@attributes = content_type.orderable_attributes.sort()
respond_to do |format|
format.js { render :layout => false }
end
end
end
end
4 changes: 4 additions & 0 deletions app/helpers/cms/content_types_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Cms
module ContentTypesHelper
end
end
9 changes: 9 additions & 0 deletions app/helpers/cms/path_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ module Cms
# From app, should be cms.xyz_path
module PathHelper

# @return A link if content is addressable, name otherwise.
# Link_to_if would call content.path even if it doesn't respond to '
def link_to_addressable_content(name, content)
if content.respond_to? :path
link_to name, content.path
else
name
end
end
# Returns the relative path to the given attachment.
# Content editors will see exact specific version path, while other users will see the 'public' url for the path.
def attachment_path_for(attachment)
Expand Down
6 changes: 6 additions & 0 deletions app/models/cms/content_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ def self.find_by_key(key)
klass.content_type
end

# Returns a list of column names and values for this content type which are allowed be orderable.
def orderable_attributes
attribute_names = model_class.new.attribute_names
attribute_names -= ["id", "version", "lock_version", "created_by_id", "updated_by_id"]
end

# @deprecated
def save!
ActiveSupport::Deprecation.warn "Cms::ContentType#save! should no longer be called. Content Types do not need to be registered in the database."
Expand Down
5 changes: 5 additions & 0 deletions app/portlets/helpers/cms/list_portlet_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
##
# All methods from this helper will be available in the render.html.erb for ListPortlet
module ListPortletHelper

end
40 changes: 40 additions & 0 deletions app/portlets/list_portlet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class ListPortlet < Cms::Portlet

description "Find and display content blocks."
enable_template_editor false

def render
query = current_content_type.model_class
limit = self.limit
unless limit.blank?
query = query.limit(limit.to_i)
end
direction = self.reverse_order.blank? || self.reverse_order == "0" ? 'asc' : 'desc'
unless self.order.blank?
query = query.order("#{self.order} #{direction}")
end
@content_blocks = query.all.to_a
end


# This is far less flexible than prepending additional view paths, but it suffices for now.
def view_as_full_path
if File.exists?(expected_view_path())
"portlets/list/#{self.name.parameterize('_')}/_#{self.view_as}"
else
"portlets/list/_#{self.view_as}"
end
end

def expected_view_path
File.join(Rails.root, 'app', 'views', 'portlets', 'list', self.name.parameterize('_'), "_#{self.view_as}.html.erb")
end

def view_as_path
"portlets/list/#{self.name.parameterize('_')}/_#{self.view_as}.html.erb"
end

def current_content_type
Cms::ContentType.named(self.content_type).first
end
end
1 change: 1 addition & 0 deletions app/views/cms/content_block/_sidebar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@
<%= link_to "Delete", engine_aware_path(@block), class: "btn btn-small btn-danger confirm_with_title http_delete", title: "Are you sure you want to delete this content?" %>
</div>
</div>
<%= yield :sidebar_after if content_for(:sidebar_after)%>
5 changes: 5 additions & 0 deletions app/views/cms/content_types/_order_field.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<%= f.input :order,
collection: collection,
label_method: :humanize,
include_blank: false,
wrapper_html: {'data-role' => "order-fields", class: 'load'} %>
3 changes: 3 additions & 0 deletions app/views/cms/content_types/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= simple_form_for ListPortlet.new(order: "name") do |f| %>
<%= render partial: 'cms/content_types/order_field', locals: { f: f, collection: @attributes} %>
<% end %>
33 changes: 33 additions & 0 deletions app/views/portlets/list/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<%
# Setup that would otherwise be done in Controller#new / #edit
if @block.content_type.blank?
@block.content_type = Cms::HtmlBlock.name
end
@orderable_attributes = @block.current_content_type.orderable_attributes

views = {
list: "List",
table: "Table",
}
%>
<% if @block.persisted? %>
<% content_for :sidebar_after do %>
<div class='sidebar-block'>
<h4 class="gray">Custom View</h4>

<p>Create/edit the following file: app/views/<%= @block.view_as_path %></p>
</div>
<% end %>
<% end %>
<%= f.input :name, placeholder: 'Name', label: false, input_html: {class: 'input-block-level input-xxlarge'} %>
<%= f.input :content_type,
collection: Cms::ContentType.available,
label_method: :display_name,
value_method: :name,
include_blank: false,
input_html: {'data-role' => "content_type_selector"} %>
<%= f.input :limit, hint: 'Show at most this many items.', placeholder: 'Displays all items if left blank.' %>
<%= render partial: 'cms/content_types/order_field', locals: {f: f, collection: @orderable_attributes} %>
<%= f.input :reverse_order, as: :boolean %>
<%= f.input :view_as, collection: views.invert, include_blank: false %>
<%= f.input :template, as: :template_editor %>
6 changes: 6 additions & 0 deletions app/views/portlets/list/_list.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<h1 class="cms-list-portlet"><%= @portlet.name %></h1>
<ul class="cms-list-portlet" data-view-as="list">
<% collection.each do |content| %>
<li><%= link_to_addressable_content(content.name, content) %></li>
<% end %>
</ul>
13 changes: 13 additions & 0 deletions app/views/portlets/list/_table.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<h1 class="cms-list-portlet"><%= @portlet.name %></h1>
<table class="cms-list-portlet" data-view-as="table">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<% collection.each do |content| %>
<tr>
<td><%= link_to_addressable_content(content.name, content) %></td>
</tr>
<% end %>
</table>
1 change: 1 addition & 0 deletions app/views/portlets/list/render.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= render file: @portlet.view_as_full_path, locals: {collection: @content_blocks} %>
13 changes: 9 additions & 4 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
skip: [:sessions],
path: :users,
class_name: 'Cms::PersistentUser',
controllers: { passwords: 'cms/passwords' },
controllers: {passwords: 'cms/passwords'},
module: :devise

devise_scope :cms_user do
get '/login' => "sessions#new", :as => 'login'
get '/login' => "sessions#new", :as => :new_cms_user_session
post '/login' => "sessions#create", :as => :cms_user_session
get '/login' => "sessions#new", :as => :new_cms_user_session
post '/login' => "sessions#create", :as => :cms_user_session
get '/logout' => "sessions#destroy", :as => 'logout'

end
Expand All @@ -39,6 +39,11 @@
end
resources :links

resources :content_types, only: [] do
collection do
post :index
end
end
resources :pages do
member do
put :archive
Expand Down Expand Up @@ -116,7 +121,7 @@
resources :redirects
resources :page_partials, :controller => 'dynamic_views'
resources :page_templates, :controller => 'dynamic_views'
resources :page_routes, except: :show do
resources :page_routes, except: :show do
resources :conditions, :controller => "page_route_conditions"
resources :requirements, :controller => "page_route_requirements"
end
Expand Down
20 changes: 20 additions & 0 deletions doc/release_notes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# v4.0.0.beta

* List Portlet [#678] - A convienant way to find content without custom coding.

## List Portlet

This portlet is provides a configurable option to find and list content via configuration, similar to a greatly simplified Drupal views. The intent is to handle easy cases of finding a few content items without needing to create a full portlet. The following configuration options are supported:

1. Content Type - User can select any available content type to list.
2. Limit - Restrict the max results to some number (i.e. 5). Can be left blank to find all.
3. Order - The results list can be ordered based on fields specific to each content type.
4. Reverse order - Change the sorting order (asc to desc).
5. View As - Results can be shown as a list or a table. Views can be customized as well.

### Customizable Views

Each list portlet can also have its own specific view that overrides the default list or table view. Developers can add a new file in a specific location, based on the name of the portlet. This view will then be used when showing the portlet. The exact path for each portlet is displayed in the sidebar. If using a table view, this will almost always need to be overriden since there is no way to configure which columns to show in the portlet.



# v4.0.0.alpha

Try it! gem install browsercms --pre
Expand Down
21 changes: 21 additions & 0 deletions features/generators/portlets.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@cli
Feature:
Developers working in BrowserCMS projects should be able to generate portlets.

Background:
Given a BrowserCMS project named "petstore" exists
And I cd into the project "petstore"

Scenario: Generate a portlet
When I run `rails g cms:portlet Events body`
Then the file "app/portlets/events_portlet.rb" should contain:
"""
description "TODO: Provide a suitable description for this portlet."
"""
And the file "app/views/portlets/events/_form.html.erb" should contain:
"""
<%= f.input :name %>
<%= f.input :body %>
<%= f.input :template, as: :template_editor %>
"""

6 changes: 3 additions & 3 deletions lib/generators/cms/portlet/templates/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<%%= f.cms_text_field :name %>
<%%= f.input :name %>
<% for attribute in attributes -%>
<%%= f.cms_text_field :<%= attribute.name %> %>
<%%= f.input :<%= attribute.name %> %>
<% end -%>
<%%= f.cms_template_editor :template %>
<%%= f.input :template, as: :template_editor %>
2 changes: 2 additions & 0 deletions lib/generators/cms/portlet/templates/portlet.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
class <%= class_name %>Portlet < Cms::Portlet

description "TODO: Provide a suitable description for this portlet."

# Mark this as 'true' to allow the portlet's template to be editable via the CMS admin UI.
enable_template_editor false

Expand Down
7 changes: 7 additions & 0 deletions test/controllers/cms/content_types_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require "test_helper"

describe ContentTypesController do
# it "must be a real test" do
# flunk "Need real tests"
# end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<h1 call>My special list:</h1>
<ul class="cms-list-portlet" data-view-as="list">
<% collection.each do |content| %>
<li><%= content.name %></li>
<% end %>
</ul>
9 changes: 9 additions & 0 deletions test/helpers/cms/content_types_helper_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require "test_helper"

describe ContentTypesHelper do

it "must be a real test" do
flunk "Need real tests"
end

end
6 changes: 6 additions & 0 deletions test/unit/models/content_type_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ class ContentTypeTest < ActiveSupport::TestCase
assert_equal Cms::HtmlBlock, Cms::ContentType.find_by_key('html_block').model_class
end

test ".orderable_attributes" do
orderable_columns = Cms::HtmlBlock.content_type.orderable_attributes
assert orderable_columns.include?("name")
refute orderable_columns.include?("id")
end

test "#content_block_type" do
assert_equal "html_blocks", Cms::HtmlBlock.content_type.content_block_type
assert_equal "dummy/products", Dummy::Product.content_type.content_block_type
Expand Down
9 changes: 9 additions & 0 deletions test/unit/portlets/list_portlet_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require File.join(File.dirname(__FILE__), '/../../test_helper')

class ListTest < ActiveSupport::TestCase

test "Should be able to create new instance of a portlet" do
assert ListPortlet.create!(:name => "New Portlet")
end

end

0 comments on commit 29cde5b

Please sign in to comment.