summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhukl <contact@smyck.org>2009-06-13 15:42:13 +0200
committerhukl <contact@smyck.org>2009-06-13 15:42:13 +0200
commitedd450502f74dcbe8175dfddee8b50d14424a390 (patch)
treec5b8c1b9b4ba57151cc72a6235e10807916ddeb4
parent4b60f3637c63c40f7b542bcb89cc48f0a6315e0b (diff)
added js search widget with clickable results that will take the user to the brand new "show" version of the page. so the user doesn't create new revisions and locks all the time when he / she only wants to take a look at it.
-rw-r--r--app/controllers/admin_controller.rb2
-rw-r--r--app/controllers/nodes_controller.rb8
-rw-r--r--app/views/layouts/admin.html.erb29
-rw-r--r--app/views/nodes/edit.html.erb7
-rw-r--r--app/views/nodes/show.html.erb40
-rw-r--r--doc/cccms_model.grafflebin723967 -> 412161 bytes
-rw-r--r--public/javascripts/admin_interface.js7
-rw-r--r--public/javascripts/admin_search.js62
-rwxr-xr-xpublic/javascripts/jquery.hotkeys.js244
-rw-r--r--public/stylesheets/admin.css64
10 files changed, 394 insertions, 69 deletions
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
index 8d7d2ea..517506f 100644
--- a/app/controllers/admin_controller.rb
+++ b/app/controllers/admin_controller.rb
@@ -19,7 +19,7 @@ class AdminController < ApplicationController
19 format.html 19 format.html
20 format.js do 20 format.js do
21 render( :json => @results.map do |node| 21 render( :json => @results.map do |node|
22 {:id => node.id, :title => node.title} 22 {:id => node.id, :title => node.title, :edit_path => node_path(node)}
23 end 23 end
24 ) 24 )
25 25
diff --git a/app/controllers/nodes_controller.rb b/app/controllers/nodes_controller.rb
index 1c7278c..09d0c52 100644
--- a/app/controllers/nodes_controller.rb
+++ b/app/controllers/nodes_controller.rb
@@ -40,14 +40,6 @@ class NodesController < ApplicationController
40 40
41 def show 41 def show
42 @page = Node.find(params[:id]).pages.last 42 @page = Node.find(params[:id]).pages.last
43
44 if @page
45 template = @page.valid_template
46 render(
47 :file => template,
48 :layout => "application"
49 )
50 end
51 end 43 end
52 44
53 def edit 45 def edit
diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb
index fea6b5a..1c985d9 100644
--- a/app/views/layouts/admin.html.erb
+++ b/app/views/layouts/admin.html.erb
@@ -11,6 +11,7 @@
11 <%= javascript_include_tag 'tiny_mce/tiny_mce.js' %> 11 <%= javascript_include_tag 'tiny_mce/tiny_mce.js' %>
12 <%= javascript_include_tag 'admin_search.js' %> 12 <%= javascript_include_tag 'admin_search.js' %>
13 <%= javascript_include_tag 'admin_interface.js' %> 13 <%= javascript_include_tag 'admin_interface.js' %>
14 <%= javascript_include_tag 'jquery.hotkeys' %>
14 15
15 <script type="text/javascript"> 16 <script type="text/javascript">
16 tinyMCE.init({ 17 tinyMCE.init({
@@ -34,17 +35,14 @@
34 <body> 35 <body>
35 <div id="wrapper"> 36 <div id="wrapper">
36 <div id="navigation"> 37 <div id="navigation">
37 <%= render :partial => 'admin/menu' if current_user %> 38 <div id="main_navigation">
39 <%= render :partial => 'admin/menu' if current_user %>
40 </div>
41 <div id="sub_navigation">
42 <%= yield :subnavigation %>
43 </div>
38 </div> 44 </div>
39 <div id="subnavigation"> 45 <div style="clear: both;"></div>
40 <%= yield :subnavigation %>
41 </div>
42 <div id="admin_search">
43 <% form_tag admin_search_path do %>
44 <%= text_field_tag :search_term %>
45 <% end %>
46 </div>
47 <div style="clear: both"></div>
48 <div id="flash"> 46 <div id="flash">
49 <%= flash[:notice] %> 47 <%= flash[:notice] %>
50 </div> 48 </div>
@@ -54,5 +52,16 @@
54 52
55 <div id="results"></div> 53 <div id="results"></div>
56 </div> 54 </div>
55
56 <div id="search_widget">
57 <div>
58 <% form_tag admin_search_path do %>
59 <span>Search: </span><%= text_field_tag :search_term %>
60 <% end %>
61 </div>
62 <div id="search_results" style="display: none">
63
64 </div>
65 </div>
57 </body> 66 </body>
58</html> 67</html>
diff --git a/app/views/nodes/edit.html.erb b/app/views/nodes/edit.html.erb
index e957b5d..f37a24b 100644
--- a/app/views/nodes/edit.html.erb
+++ b/app/views/nodes/edit.html.erb
@@ -1,6 +1,7 @@
1<% content_for :subnavigation do %> 1<% content_for :subnavigation do %>
2 <%= link_to 'metadata', '#', :id => 'button', :class => "unselected" %> 2 <%= link_to 'metadata', '#', :id => 'button', :class => "unselected" %>
3 <%= link_to 'Preview', @node %> 3 <%= link_to 'Show', @node %>
4 <%= link_to 'Preview', preview_page_path(@draft) %>
4 <%= link_to 'Publish', publish_node_path, :method => :put, :confirm => "Publish this draft?" %> 5 <%= link_to 'Publish', publish_node_path, :method => :put, :confirm => "Publish this draft?" %>
5 <%= link_to 'Revisions', revision_path(params[:id]) %> 6 <%= link_to 'Revisions', revision_path(params[:id]) %>
6<% end %> 7<% end %>
@@ -36,6 +37,10 @@
36 37
37 <table id="content"> 38 <table id="content">
38 <tr> 39 <tr>
40 <th class="description"></th>
41 <th class="content"></th>
42 </tr>
43 <tr>
39 <td class="description">Title</td> 44 <td class="description">Title</td>
40 <td><%= d.text_field :title %></td> 45 <td><%= d.text_field :title %></td>
41 </tr> 46 </tr>
diff --git a/app/views/nodes/show.html.erb b/app/views/nodes/show.html.erb
index bbbefe9..8aaedc5 100644
--- a/app/views/nodes/show.html.erb
+++ b/app/views/nodes/show.html.erb
@@ -1,9 +1,31 @@
1<h1>Node</h1> 1<% content_for :subnavigation do %>
2<p> 2 <%= link_to 'Edit', edit_node_path(@node), :id => 'button', :class => "unselected" %>
3There is no draft to preview. Click <%= link_to 'edit', edit_node_path %> to 3 <%= link_to 'Preview', preview_page_path(@page) %>
4create one or view the currently 4 <%= link_to 'Revisions', revision_path(params[:id]) %>
5<%= link_to_path 'published version', @node.unique_path %>. 5<% end %>
6</p> 6
7<p> 7
8 View the revisions of this node 8<div id="page_editor" class="show_node">
9</p> \ No newline at end of file 9 <table id="content">
10 <tr>
11 <th class="description"></th>
12 <th class="content"></th>
13 </tr>
14 <tr>
15 <td class="description">Title</td>
16 <td><%= @page.title %></td>
17 </tr>
18 <tr>
19 <td class="description">Abstract</td>
20 <td><%= @page.abstract %></td>
21 </tr>
22 <tr>
23 <td class="description">Body</td>
24 <td><%= @page.body %></td>
25 </tr>
26 <tr>
27 <td></td>
28 <td class="right"></td>
29 </tr>
30 </table>
31</div> \ No newline at end of file
diff --git a/doc/cccms_model.graffle b/doc/cccms_model.graffle
index 86d3ab2..dd7b325 100644
--- a/doc/cccms_model.graffle
+++ b/doc/cccms_model.graffle
Binary files differ
diff --git a/public/javascripts/admin_interface.js b/public/javascripts/admin_interface.js
index 5fe91a2..f890afb 100644
--- a/public/javascripts/admin_interface.js
+++ b/public/javascripts/admin_interface.js
@@ -1,5 +1,10 @@
1$(document).ready(function () { 1$(document).ready(function () {
2 admin_search.initialize(); 2 $("#search_widget").hide();
3
4 $(document).bind("keydown", 'Alt+f', function(){
5 admin_search.display_toggle();
6 return false;
7 });
3 8
4 $("#metadata").attr("style", "display: none;"); 9 $("#metadata").attr("style", "display: none;");
5 10
diff --git a/public/javascripts/admin_search.js b/public/javascripts/admin_search.js
index d645cca..ba52bb5 100644
--- a/public/javascripts/admin_search.js
+++ b/public/javascripts/admin_search.js
@@ -1,28 +1,40 @@
1admin_search = { 1admin_search = {
2 2
3 initialize : function() { 3 display_toggle : function() {
4 $("#search_term").bind("keyup", function() { 4 if ($('#search_widget').css("display") != "none") {
5 if ($(this).attr("value")) { 5 $('#search_widget').fadeOut();
6 $.ajax({
7 type: "GET",
8 url: "/admin/search",
9 data: "search_term=" + $(this).attr("value"),
10 dataType: "json",
11 success : function(results) {
12 admin_search.show_results(results);
13 }
14 });
15 }
16 else {
17 $('#results').empty();
18 }
19 });
20 },
21
22 show_results : function(results) {
23 $('#results').empty();
24 for (result in results) {
25 $('#results').append("<p>" + results[result].title + "</p>");
26 } 6 }
27 } 7 else {
28} \ No newline at end of file 8 $('#search_widget').fadeIn();
9 $('#search_term').attr("value", "");
10 $('#search_term').focus();
11 }
12
13 $("#search_term").bind("keyup", function() {
14 if ($(this).attr("value")) {
15 $.ajax({
16 type: "GET",
17 url: "/admin/search",
18 data: "search_term=" + $(this).attr("value"),
19 dataType: "json",
20 success : function(results) {
21 admin_search.show_results(results);
22 }
23 });
24 }
25 else {
26 $('#search_results').slideUp();
27 $('#search_results').empty();
28 }
29 });
30 },
31
32 show_results : function(results) {
33 $('#search_results').empty();
34 for (result in results) {
35 $('#search_results').append("<p><a href='"+ results[result].edit_path + "'>" + results[result].title + "</a></p>");
36 }
37 $('#search_results').slideDown();
38 }
39
40 } \ No newline at end of file
diff --git a/public/javascripts/jquery.hotkeys.js b/public/javascripts/jquery.hotkeys.js
new file mode 100755
index 0000000..9a8f2de
--- /dev/null
+++ b/public/javascripts/jquery.hotkeys.js
@@ -0,0 +1,244 @@
1/*
2(c) Copyrights 2007 - 2008
3
4Original idea by by Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
5
6jQuery Plugin by Tzury Bar Yochay
7tzury.by@gmail.com
8http://evalinux.wordpress.com
9http://facebook.com/profile.php?id=513676303
10
11Project's sites:
12http://code.google.com/p/js-hotkeys/
13http://github.com/tzuryby/hotkeys/tree/master
14
15License: same as jQuery license.
16
17USAGE:
18 // simple usage
19 $(document).bind('keydown', 'Ctrl+c', function(){ alert('copy anyone?');});
20
21 // special options such as disableInIput
22 $(document).bind('keydown', {combi:'Ctrl+x', disableInInput: true} , function() {});
23
24Note:
25 This plugin wraps the following jQuery methods: $.fn.find, $.fn.bind and $.fn.unbind
26*/
27
28(function (jQuery){
29 // keep reference to the original $.fn.bind, $.fn.unbind and $.fn.find
30 jQuery.fn.__bind__ = jQuery.fn.bind;
31 jQuery.fn.__unbind__ = jQuery.fn.unbind;
32 jQuery.fn.__find__ = jQuery.fn.find;
33
34 var hotkeys = {
35 version: '0.7.9',
36 override: /keypress|keydown|keyup/g,
37 triggersMap: {},
38
39 specialKeys: { 27: 'esc', 9: 'tab', 32:'space', 13: 'return', 8:'backspace', 145: 'scroll',
40 20: 'capslock', 144: 'numlock', 19:'pause', 45:'insert', 36:'home', 46:'del',
41 35:'end', 33: 'pageup', 34:'pagedown', 37:'left', 38:'up', 39:'right',40:'down',
42 109: '-',
43 112:'f1',113:'f2', 114:'f3', 115:'f4', 116:'f5', 117:'f6', 118:'f7', 119:'f8',
44 120:'f9', 121:'f10', 122:'f11', 123:'f12', 191: '/'},
45
46 shiftNums: { "`":"~", "1":"!", "2":"@", "3":"#", "4":"$", "5":"%", "6":"^", "7":"&",
47 "8":"*", "9":"(", "0":")", "-":"_", "=":"+", ";":":", "'":"\"", ",":"<",
48 ".":">", "/":"?", "\\":"|" },
49
50 newTrigger: function (type, combi, callback) {
51 // i.e. {'keyup': {'ctrl': {cb: callback, disableInInput: false}}}
52 var result = {};
53 result[type] = {};
54 result[type][combi] = {cb: callback, disableInInput: false};
55 return result;
56 }
57 };
58 // add firefox num pad char codes
59 //if (jQuery.browser.mozilla){
60 // add num pad char codes
61 hotkeys.specialKeys = jQuery.extend(hotkeys.specialKeys, { 96: '0', 97:'1', 98: '2', 99:
62 '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9', 106: '*',
63 107: '+', 109: '-', 110: '.', 111 : '/'
64 });
65 //}
66
67 // a wrapper around of $.fn.find
68 // see more at: http://groups.google.com/group/jquery-en/browse_thread/thread/18f9825e8d22f18d
69 jQuery.fn.find = function( selector ) {
70 this.query = selector;
71 return jQuery.fn.__find__.apply(this, arguments);
72 };
73
74 jQuery.fn.unbind = function (type, combi, fn){
75 if (jQuery.isFunction(combi)){
76 fn = combi;
77 combi = null;
78 }
79 if (combi && typeof combi === 'string'){
80 var selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
81 var hkTypes = type.split(' ');
82 for (var x=0; x<hkTypes.length; x++){
83 delete hotkeys.triggersMap[selectorId][hkTypes[x]][combi];
84 }
85 }
86 // call jQuery original unbind
87 return this.__unbind__(type, fn);
88 };
89
90 jQuery.fn.bind = function(type, data, fn){
91 // grab keyup,keydown,keypress
92 var handle = type.match(hotkeys.override);
93
94 if (jQuery.isFunction(data) || !handle){
95 // call jQuery.bind only
96 return this.__bind__(type, data, fn);
97 }
98 else{
99 // split the job
100 var result = null,
101 // pass the rest to the original $.fn.bind
102 pass2jq = jQuery.trim(type.replace(hotkeys.override, ''));
103
104 // see if there are other types, pass them to the original $.fn.bind
105 if (pass2jq){
106 result = this.__bind__(pass2jq, data, fn);
107 }
108
109 if (typeof data === "string"){
110 data = {'combi': data};
111 }
112 if(data.combi){
113 for (var x=0; x < handle.length; x++){
114 var eventType = handle[x];
115 var combi = data.combi.toLowerCase(),
116 trigger = hotkeys.newTrigger(eventType, combi, fn),
117 selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
118
119 //trigger[eventType][combi].propagate = data.propagate;
120 trigger[eventType][combi].disableInInput = data.disableInInput;
121
122 // first time selector is bounded
123 if (!hotkeys.triggersMap[selectorId]) {
124 hotkeys.triggersMap[selectorId] = trigger;
125 }
126 // first time selector is bounded with this type
127 else if (!hotkeys.triggersMap[selectorId][eventType]) {
128 hotkeys.triggersMap[selectorId][eventType] = trigger[eventType];
129 }
130 // make trigger point as array so more than one handler can be bound
131 var mapPoint = hotkeys.triggersMap[selectorId][eventType][combi];
132 if (!mapPoint){
133 hotkeys.triggersMap[selectorId][eventType][combi] = [trigger[eventType][combi]];
134 }
135 else if (mapPoint.constructor !== Array){
136 hotkeys.triggersMap[selectorId][eventType][combi] = [mapPoint];
137 }
138 else {
139 hotkeys.triggersMap[selectorId][eventType][combi][mapPoint.length] = trigger[eventType][combi];
140 }
141
142 // add attribute and call $.event.add per matched element
143 this.each(function(){
144 // jQuery wrapper for the current element
145 var jqElem = jQuery(this);
146
147 // element already associated with another collection
148 if (jqElem.attr('hkId') && jqElem.attr('hkId') !== selectorId){
149 selectorId = jqElem.attr('hkId') + ";" + selectorId;
150 }
151 jqElem.attr('hkId', selectorId);
152 });
153 result = this.__bind__(handle.join(' '), data, hotkeys.handler)
154 }
155 }
156 return result;
157 }
158 };
159 // work-around for opera and safari where (sometimes) the target is the element which was last
160 // clicked with the mouse and not the document event it would make sense to get the document
161 hotkeys.findElement = function (elem){
162 if (!jQuery(elem).attr('hkId')){
163 if (jQuery.browser.opera || jQuery.browser.safari){
164 while (!jQuery(elem).attr('hkId') && elem.parentNode){
165 elem = elem.parentNode;
166 }
167 }
168 }
169 return elem;
170 };
171 // the event handler
172 hotkeys.handler = function(event) {
173 var target = hotkeys.findElement(event.currentTarget),
174 jTarget = jQuery(target),
175 ids = jTarget.attr('hkId');
176
177 if(ids){
178 ids = ids.split(';');
179 var code = event.which,
180 type = event.type,
181 special = hotkeys.specialKeys[code],
182 // prevent f5 overlapping with 't' (or f4 with 's', etc.)
183 character = !special && String.fromCharCode(code).toLowerCase(),
184 shift = event.shiftKey,
185 ctrl = event.ctrlKey,
186 // patch for jquery 1.2.5 && 1.2.6 see more at:
187 // http://groups.google.com/group/jquery-en/browse_thread/thread/83e10b3bb1f1c32b
188 alt = event.altKey || event.originalEvent.altKey,
189 mapPoint = null;
190
191 for (var x=0; x < ids.length; x++){
192 if (hotkeys.triggersMap[ids[x]][type]){
193 mapPoint = hotkeys.triggersMap[ids[x]][type];
194 break;
195 }
196 }
197
198 //find by: id.type.combi.options
199 if (mapPoint){
200 var trigger;
201 // event type is associated with the hkId
202 if(!shift && !ctrl && !alt) { // No Modifiers
203 trigger = mapPoint[special] || (character && mapPoint[character]);
204 }
205 else{
206 // check combinations (alt|ctrl|shift+anything)
207 var modif = '';
208 if(alt) modif +='alt+';
209 if(ctrl) modif+= 'ctrl+';
210 if(shift) modif += 'shift+';
211 // modifiers + special keys or modifiers + character or modifiers + shift character or just shift character
212 trigger = mapPoint[modif+special];
213 if (!trigger){
214 if (character){
215 trigger = mapPoint[modif+character]
216 || mapPoint[modif+hotkeys.shiftNums[character]]
217 // '$' can be triggered as 'Shift+4' or 'Shift+$' or just '$'
218 || (modif === 'shift+' && mapPoint[hotkeys.shiftNums[character]]);
219 }
220 }
221 }
222 if (trigger){
223 var result = false;
224 for (var x=0; x < trigger.length; x++){
225 if(trigger[x].disableInInput){
226 // double check event.currentTarget and event.target
227 var elem = jQuery(event.target);
228 if (jTarget.is("input") || jTarget.is("textarea") || jTarget.is("select")
229 || elem.is("input") || elem.is("textarea") || elem.is("select")) {
230 return true;
231 }
232 }
233 // call the registered callback function
234 result = result || trigger[x].cb.apply(this, [event]);
235 }
236 return result;
237 }
238 }
239 }
240 };
241 // place it under window so it can be extended and overridden by others
242 window.hotkeys = hotkeys;
243 return jQuery;
244})(jQuery);
diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css
index e077918..faebbea 100644
--- a/public/stylesheets/admin.css
+++ b/public/stylesheets/admin.css
@@ -3,6 +3,7 @@
3body { 3body {
4 font-family: Helvetica, Arial, sans-serif; 4 font-family: Helvetica, Arial, sans-serif;
5 font-size: 12px; 5 font-size: 12px;
6 margin: 0px;
6} 7}
7 8
8a { 9a {
@@ -64,33 +65,38 @@ div.pagination span.current, div.pagination a:hover {
64 text-align: right; 65 text-align: right;
65} 66}
66 67
67#navigation, #subnavigation { 68#navigation {
68 margin-left: 0px; 69 position: relative;
70 margin-top: 10px;
71}
72
73#navigation div {
69 float: left; 74 float: left;
70} 75}
71 76
77
72#navigation a:hover { 78#navigation a:hover {
73 color: #ffffff; 79 color: #ffffff;
74 background-color: #000000; 80 background-color: #000000;
75} 81}
76 82
77#navigation a, #subnavigation a { 83#main_navigation a, #sub_navigation a {
78 letter-spacing: 1px; 84 letter-spacing: 1px;
79 padding-left: 5px; 85 padding-left: 5px;
80 padding-right: 5px; 86 padding-right: 5px;
81 text-transform: lowercase; 87 text-transform: lowercase;
82} 88}
83 89
84#subnavigation a { 90#sub_navigation a {
85 color: #969696; 91 color: #969696;
86} 92}
87 93
88#subnavigation a:hover { 94#sub_navigation a:hover {
89 color: #ffffff; 95 color: #ffffff;
90 background-color: #ff9600; 96 background-color: #ff9600;
91} 97}
92 98
93#subnavigation a.selected { 99#sub_navigation a.selected {
94 color: #ffffff; 100 color: #ffffff;
95 background-color: #ff9600; 101 background-color: #ff9600;
96} 102}
@@ -100,9 +106,6 @@ div.pagination span.current, div.pagination a:hover {
100 background-color: #000000; 106 background-color: #000000;
101} 107}
102 108
103#admin_search {
104}
105
106/* Nodes */ 109/* Nodes */
107 110
108table#node_table { 111table#node_table {
@@ -211,12 +214,10 @@ input[type=text]#page_title {
211} 214}
212 215
213input[type=text]#tag_list { 216input[type=text]#tag_list {
214 width: 690px;
215 padding: 5px; 217 padding: 5px;
216} 218}
217 219
218input[type=text]#node_slug { 220input[type=text]#node_slug {
219 width: 690px;
220 padding: 5px; 221 padding: 5px;
221} 222}
222 223
@@ -235,14 +236,14 @@ input[type=radio] {
235} 236}
236 237
237textarea#page_abstract { 238textarea#page_abstract {
238 height: 150px;
239 width: 690px; 239 width: 690px;
240 height: 150px;
240 padding: 5px; 241 padding: 5px;
241} 242}
242 243
243#page_editor textarea#page_body { 244#page_editor textarea#page_body {
244 height: 600px;
245 width: 700px; 245 width: 700px;
246 height: 600px;
246} 247}
247 248
248#page_editor #metadata, #page_editor #content { 249#page_editor #metadata, #page_editor #content {
@@ -303,4 +304,39 @@ div#draft_list table tr:hover {
303table tr.header { 304table tr.header {
304 height: 20px; 305 height: 20px;
305 text-align: left; 306 text-align: left;
306} \ No newline at end of file 307}
308
309#search_widget {
310 position: absolute;
311 top: 20px;
312 left: 400px;
313 width: 300px;
314 border: 1px solid #000000;
315 -webkit-box-shadow: 3px 3px 5px #b1b1b1;
316 -moz-box-shadow: 10px 10px 5px #888, 10px 10px 30px rgba(0,0,0,0.4);
317 background-color: #ffffff;
318 padding: 3px;
319 text-align: center;
320}
321
322#search_widget span {
323 font-size: 18px;
324}
325
326#search_widget input {
327 width: 210px;
328 font-size: 18px;
329}
330
331#search_widget #search_results {
332 padding: 4px;
333 text-align: left;
334}
335
336table#content th.description {
337 width: 100px;
338}
339
340table#content th.content {
341 width: 690px;
342}