cortex-reaver 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/cortex_reaver +9 -2
- data/lib/cortex_reaver.rb +61 -14
- data/lib/cortex_reaver/config.rb +6 -1
- data/lib/cortex_reaver/controller/journal.rb +3 -1
- data/lib/cortex_reaver/controller/main.rb +10 -9
- data/lib/cortex_reaver/controller/page.rb +4 -2
- data/lib/cortex_reaver/controller/photograph.rb +3 -1
- data/lib/cortex_reaver/controller/project.rb +1 -1
- data/lib/cortex_reaver/controller/tag.rb +1 -1
- data/lib/cortex_reaver/helper/canonical.rb +1 -1
- data/lib/cortex_reaver/helper/crud.rb +7 -15
- data/lib/cortex_reaver/helper/navigation.rb +16 -0
- data/lib/cortex_reaver/helper/pages.rb +62 -0
- data/lib/cortex_reaver/helper/tags.rb +2 -3
- data/lib/cortex_reaver/migrations/009_mysql.rb +8 -4
- data/lib/cortex_reaver/migrations/010_pageparents.rb +17 -0
- data/lib/cortex_reaver/model/page.rb +68 -7
- data/lib/cortex_reaver/plugin.rb +4 -0
- data/lib/cortex_reaver/public/css/main.css +4 -1
- data/lib/cortex_reaver/public/images/tag.gif +0 -0
- data/lib/cortex_reaver/public/js/autocompletefb.js +125 -0
- data/lib/cortex_reaver/snippets/ramaze/dispatcher/file.rb +1 -1
- data/lib/cortex_reaver/support/canonical.rb +42 -38
- data/lib/cortex_reaver/support/renderer.rb +10 -72
- data/lib/cortex_reaver/support/sequenceable.rb +1 -1
- data/lib/cortex_reaver/support/tags.rb +17 -6
- data/lib/cortex_reaver/version.rb +2 -2
- data/lib/cortex_reaver/view/journals/journal.rhtml +4 -3
- data/lib/cortex_reaver/view/pages/form.rhtml +1 -0
- data/lib/cortex_reaver/view/pages/list.rhtml +3 -3
- data/lib/cortex_reaver/view/pages/show.rhtml +2 -8
- data/lib/cortex_reaver/view/photographs/show.rhtml +1 -1
- data/lib/cortex_reaver/view/projects/show.rhtml +3 -2
- data/lib/cortex_reaver/view/tags/list.rhtml +4 -3
- data/lib/cortex_reaver/view/tags/show.rhtml +2 -2
- data/lib/cortex_reaver/view/text_layout.rhtml +12 -9
- data/lib/cortex_reaver/view/users/list.rhtml +2 -2
- metadata +11 -6
@@ -7,15 +7,13 @@ module Ramaze
|
|
7
7
|
title = opts[:title] || name.to_s.titleize
|
8
8
|
|
9
9
|
s = "<p><label for=\"#{name}\">#{title}</label>"
|
10
|
-
s << "<input name=\"#{name}\" id=\"#{name}\" type=\"text\" class=\"
|
10
|
+
s << "<input name=\"#{name}\" id=\"#{name}\" type=\"text\" class=\"acfb-input\" value=\"#{attr_h(tags_on(model, false))}\" />"
|
11
11
|
s << "</p>"
|
12
12
|
|
13
13
|
s << <<EOF
|
14
14
|
|
15
15
|
<script type="text/javascript">
|
16
16
|
/* <![CDATA[ */
|
17
|
-
|
18
|
-
|
19
17
|
/* ]]> */
|
20
18
|
</script>
|
21
19
|
EOF
|
@@ -25,6 +23,7 @@ EOF
|
|
25
23
|
def tags_on(model, html=true)
|
26
24
|
begin
|
27
25
|
if html
|
26
|
+
#t = '<img src="/images/tag.gif" alt="Tags" />'
|
28
27
|
t = '<ul class="tags">'
|
29
28
|
model.tags.each do |tag|
|
30
29
|
t << "<li><a href=\"#{tag.url}\">#{tag.title}</a></li>"
|
@@ -7,11 +7,15 @@ module CortexReaver
|
|
7
7
|
|
8
8
|
def up
|
9
9
|
db = CortexReaver.db
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
db
|
10
|
+
begin
|
11
|
+
if db.is_a? Sequel::MySQL::Database
|
12
|
+
# Use InnoDB storage for everything
|
13
|
+
db.tables.each do |table|
|
14
|
+
db << "ALTER TABLE `#{table.to_s}` ENGINE = InnoDB;"
|
15
|
+
end
|
14
16
|
end
|
17
|
+
rescue NameError
|
18
|
+
# Not mysql!
|
15
19
|
end
|
16
20
|
end
|
17
21
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CortexReaver
|
2
|
+
class PageparentsSchema < Sequel::Migration
|
3
|
+
def down
|
4
|
+
alter_table :pages do
|
5
|
+
drop_index :page_id
|
6
|
+
drop_column :page_id
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def up
|
11
|
+
alter_table :pages do
|
12
|
+
add_foreign_key :page_id, :pages, :key => :id
|
13
|
+
add_index :page_id
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -9,10 +9,15 @@ module CortexReaver
|
|
9
9
|
include CortexReaver::Model::Tags
|
10
10
|
include CortexReaver::Model::Sequenceable
|
11
11
|
|
12
|
+
belongs_to :page, :class => 'CortexReaver::Page'
|
13
|
+
has_many :pages, :class => 'CortexReaver::Page'
|
12
14
|
belongs_to :user, :class => 'CortexReaver::User'
|
13
|
-
has_many
|
15
|
+
has_many :comments, :class => 'CortexReaver::Comment'
|
14
16
|
many_to_many :tags, :class => 'CortexReaver::Tag'
|
15
17
|
|
18
|
+
# Top-level pages.
|
19
|
+
subset :top, :page_id => nil
|
20
|
+
|
16
21
|
validates do
|
17
22
|
uniqueness_of :name
|
18
23
|
presence_of :name
|
@@ -35,15 +40,57 @@ module CortexReaver
|
|
35
40
|
|
36
41
|
# Also reserve everything in the public directory, as a courtesy.
|
37
42
|
#
|
38
|
-
# I can't stop you from shooting yourself in the foot, but this will help
|
39
|
-
# aim higher. :)
|
43
|
+
# I can't stop you from shooting yourself in the foot, but this will help
|
44
|
+
# you aim higher. :)
|
40
45
|
self.reserved_canonical_names += Dir.entries(CortexReaver.config[:public_root]) - ['..', '.']
|
41
46
|
|
42
47
|
# Use standard cached renderer
|
43
48
|
render :body
|
44
49
|
|
45
|
-
def
|
46
|
-
|
50
|
+
def render(text)
|
51
|
+
bluecloth(
|
52
|
+
macro(
|
53
|
+
erubis_filter(
|
54
|
+
syntax_highlight(
|
55
|
+
text
|
56
|
+
)
|
57
|
+
)
|
58
|
+
), false, false
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Canonicalize only in the context of our parent's namespace.
|
63
|
+
# Arguments:
|
64
|
+
# - A proper canonical name to check for conflicts with
|
65
|
+
# - :id => An optional id to ignore conflicts with
|
66
|
+
# - :page_id => A parent page_id to use as the namespace for conflict checking.
|
67
|
+
def self.similar_canonical_names(proper, opts={})
|
68
|
+
id = opts[:id]
|
69
|
+
page_id = opts[:page_id]
|
70
|
+
|
71
|
+
similar = []
|
72
|
+
# Get parent page id for the current context.
|
73
|
+
if filter(canonical_name_attr => proper, :page_id => page_id).exclude(:id => id).limit(1).count > 0
|
74
|
+
# This name already exists, and it's not ours.
|
75
|
+
similar << proper
|
76
|
+
similar += filter(canonical_name_attr.like(/^#{proper}\-[0-9]+$/)).filter(:page_id => page_id).map(canonical_name_attr)
|
77
|
+
end
|
78
|
+
similar
|
79
|
+
end
|
80
|
+
|
81
|
+
# get('foo', 'bar', 'baz')
|
82
|
+
# get('foo/bar/baz')
|
83
|
+
def self.get(ids)
|
84
|
+
unless ids.is_a? Array
|
85
|
+
ids = ids.split('/')
|
86
|
+
end
|
87
|
+
|
88
|
+
# Look up ids by nested names.
|
89
|
+
ids.inject(nil) do |page, name|
|
90
|
+
puts "Searching for #{name} in #{page.inspect}"
|
91
|
+
parent_id = page ? page.id : nil
|
92
|
+
self[:page_id => parent_id, :name => name]
|
93
|
+
end
|
47
94
|
end
|
48
95
|
|
49
96
|
def self.url
|
@@ -55,17 +102,31 @@ module CortexReaver
|
|
55
102
|
end
|
56
103
|
|
57
104
|
def atom_url
|
58
|
-
'/pages/atom/' +
|
105
|
+
'/pages/atom/' + id.to_s
|
59
106
|
end
|
60
107
|
|
61
108
|
def url
|
62
|
-
|
109
|
+
if page
|
110
|
+
page.url + '/' + name
|
111
|
+
else
|
112
|
+
'/' + name
|
113
|
+
end
|
63
114
|
end
|
64
115
|
|
65
116
|
def to_s
|
66
117
|
title || name
|
67
118
|
end
|
68
119
|
|
120
|
+
# Returns true if this page is located underneath another page.
|
121
|
+
# within?(self) => true.
|
122
|
+
def within?(other)
|
123
|
+
if parent = page
|
124
|
+
self == other or parent.within?(other)
|
125
|
+
else
|
126
|
+
self == other
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
69
130
|
# Create a default page if none exists.
|
70
131
|
if table_exists? and Page.count == 0
|
71
132
|
Page.new(
|
Binary file
|
@@ -0,0 +1,125 @@
|
|
1
|
+
/*
|
2
|
+
* jQuery plugin: autoCompletefb(AutoComplete Facebook)
|
3
|
+
* @requires jQuery v1.2.2 or later
|
4
|
+
* using plugin:jquery.autocomplete.js
|
5
|
+
*
|
6
|
+
* Credits:
|
7
|
+
* - Idea: Facebook
|
8
|
+
* - Guillermo Rauch: Original MooTools script
|
9
|
+
* - InteRiders <http://interiders.com/>
|
10
|
+
*
|
11
|
+
* Modified by Michael 'manveru' Fellinger <m.fellinger@gmail.com>
|
12
|
+
*
|
13
|
+
* Copyright (c) 2008 Widi Harsojo <wharsojo@gmail.com>, http://wharsojo.wordpress.com/
|
14
|
+
* Dual licensed under the MIT and GPL licenses:
|
15
|
+
* http://www.opensource.org/licenses/mit-license.php
|
16
|
+
* http://www.gnu.org/licenses/gpl.html
|
17
|
+
*/
|
18
|
+
|
19
|
+
jQuery.fn.autoCompletefb = function(options) {
|
20
|
+
var tmp = this;
|
21
|
+
var root = tmp[0];
|
22
|
+
if(!root){ return; } // fail fast if this is called on empty jquery collection
|
23
|
+
|
24
|
+
var settings = {
|
25
|
+
ul : tmp,
|
26
|
+
urlLookup : [""],
|
27
|
+
foundClass : ".acfb-data",
|
28
|
+
inputClass : ".acfb-input",
|
29
|
+
removeImg : "/theme/light/media/delete.gif"
|
30
|
+
}
|
31
|
+
|
32
|
+
if(options){ jQuery.extend(settings, options); }
|
33
|
+
|
34
|
+
var acfb = {
|
35
|
+
params : settings,
|
36
|
+
getData : function(){
|
37
|
+
var result = '';
|
38
|
+
$(settings.foundClass, tmp).each(
|
39
|
+
function(i) {
|
40
|
+
if(i > 0){ result += ','; }
|
41
|
+
result += $('span', this).html();
|
42
|
+
result += $('input', this).html();
|
43
|
+
});
|
44
|
+
return result;
|
45
|
+
},
|
46
|
+
clearData : function(){
|
47
|
+
$(settings.foundClass, tmp).remove();
|
48
|
+
$(settings.inputClass, tmp).focus();
|
49
|
+
return tmp.acfb;
|
50
|
+
},
|
51
|
+
removeFind : function(o, d){
|
52
|
+
var without = [];
|
53
|
+
|
54
|
+
for(i in root.acfb_data){
|
55
|
+
var val = root.acfb_data[i];
|
56
|
+
if(val !== d){ without.push(val); }
|
57
|
+
}
|
58
|
+
|
59
|
+
root.acfb_data = without;
|
60
|
+
acfb.updateStore([]);
|
61
|
+
|
62
|
+
$(o).unbind('click').parent().remove();
|
63
|
+
$(settings.inputClass, tmp).focus();
|
64
|
+
return tmp.acfb;
|
65
|
+
},
|
66
|
+
updateStore : function(d){
|
67
|
+
for(i in d){ root.acfb_data.push(d[i]); }
|
68
|
+
var input = $('.acfb-store', root);
|
69
|
+
input.val(root.acfb_data.join(","));
|
70
|
+
},
|
71
|
+
addLi : function(data, value){
|
72
|
+
if(value.length == 0){ return; }
|
73
|
+
for(i in root.acfb_data){ if(root.acfb_data[i] == value){ return } }
|
74
|
+
acfb.updateStore(data);
|
75
|
+
|
76
|
+
var klass = settings.foundClass.replace(/\./,'');
|
77
|
+
var li_tag = '<li class="' + klass + '"><span>' + data + '</span> <img class="p" src="' + settings.removeImg + '"/></li>';
|
78
|
+
var li = $(settings.inputClass, tmp).before(li_tag);
|
79
|
+
|
80
|
+
$('.p', li[0].previousSibling).click(function(){
|
81
|
+
acfb.removeFind(this, data[0]);
|
82
|
+
});
|
83
|
+
},
|
84
|
+
addLis : function(arr){
|
85
|
+
for(i in arr){ acfb.addLi([arr[i]], arr[i]); }
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
// using an array so we can easily join
|
90
|
+
root.acfb_data = [];
|
91
|
+
|
92
|
+
// add hidden input tag in the ul
|
93
|
+
var orig_input = $(settings.inputClass, tmp)[0];
|
94
|
+
var input_name = orig_input.name;
|
95
|
+
var input_tag = '<input type="hidden" class="acfb-store" name="' + input_name + '" />';
|
96
|
+
var input = $(settings.inputClass, tmp).before(input_tag)[0];
|
97
|
+
|
98
|
+
var orig_values = orig_input.value.split(",");
|
99
|
+
acfb.addLis(orig_values);
|
100
|
+
|
101
|
+
// remove name from original input tag so it won't show up in the request and
|
102
|
+
// reset the value so it is ready to take further input
|
103
|
+
$(orig_input).removeAttr("name");
|
104
|
+
orig_input.value = '';
|
105
|
+
|
106
|
+
// add an add button... enter doesn't seem to work?
|
107
|
+
var add_tag = '<span class="acfb-add">+</a>'
|
108
|
+
$(settings.inputClass, tmp).after(add_tag);
|
109
|
+
var add = $('.acfb-add', tmp);
|
110
|
+
add.click(function(){
|
111
|
+
var words = orig_input.value.split(/,+/);
|
112
|
+
acfb.addLis(words);
|
113
|
+
$(settings.inputClass, tmp).val('').focus();
|
114
|
+
return false;
|
115
|
+
});
|
116
|
+
|
117
|
+
// $(settings.foundClass + " img.p").click(function(){ acfb.removeFind(this); });
|
118
|
+
$(settings.inputClass, tmp).autocomplete(settings.urlLookup);
|
119
|
+
$(settings.inputClass, tmp).result(function(ev, data, value) {
|
120
|
+
acfb.addLi(data, value);
|
121
|
+
$(settings.inputClass, tmp).val('').focus();
|
122
|
+
});
|
123
|
+
return acfb;
|
124
|
+
}
|
125
|
+
|
@@ -9,6 +9,11 @@ module CortexReaver
|
|
9
9
|
# The attribute we infer the canonical name from, if not set.
|
10
10
|
CANONICAL_INFERENCE_ATTR = :title
|
11
11
|
|
12
|
+
# Lower case, remove special chars, and replace with hyphens.
|
13
|
+
def self.formalize(string)
|
14
|
+
string.downcase.gsub(/[^a-z0-9_]/, '-').squeeze('-')[0..250].sub(/-$/, '')
|
15
|
+
end
|
16
|
+
|
12
17
|
def self.included(base)
|
13
18
|
base.class_eval do
|
14
19
|
# Canonical names which cannot be reserved.
|
@@ -21,10 +26,10 @@ module CortexReaver
|
|
21
26
|
end
|
22
27
|
|
23
28
|
# Canonicalize a string. Optionally, ignore conflicts with the record
|
24
|
-
# with id
|
25
|
-
def self.canonicalize(string,
|
29
|
+
# with opts[:id]
|
30
|
+
def self.canonicalize(string, opts={})
|
26
31
|
# Lower case, remove special chars, and replace with hyphens.
|
27
|
-
proper =
|
32
|
+
proper = Canonical.formalize string
|
28
33
|
|
29
34
|
# If proper is blank, just return it at this point.
|
30
35
|
if proper.blank?
|
@@ -34,47 +39,32 @@ module CortexReaver
|
|
34
39
|
# Numeric suffix to append
|
35
40
|
suffix = nil
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
similar = []
|
42
|
+
# Get similar names from the class
|
43
|
+
similar = similar_canonical_names(proper, opts)
|
41
44
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
similar
|
45
|
+
# Get reserved names from the class
|
46
|
+
reserved_canonical_names.each do |name|
|
47
|
+
if name =~ /^#{proper}(-\d+)?$/
|
48
|
+
similar << name
|
46
49
|
end
|
50
|
+
end
|
47
51
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
# Find possible conflicting names from actions on this model's controller.
|
56
|
-
# if self.respond_to? :url and controller = Ramaze::Controller.at(self.url)
|
57
|
-
# similar += controller.action_methods.select do |action|
|
58
|
-
# action =~ /^#{proper}(-\d+)?$/
|
59
|
-
# end
|
60
|
-
# end
|
61
|
-
|
62
|
-
# Extract numeric suffices
|
63
|
-
suffices = {}
|
64
|
-
similar.each do |name|
|
65
|
-
suffices[name[/\d$/].to_i] = true
|
66
|
-
end
|
52
|
+
# Extract numeric suffices
|
53
|
+
suffices = {}
|
54
|
+
similar.each do |name|
|
55
|
+
suffices[name[/\d$/].to_i] = true
|
56
|
+
end
|
67
57
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
suffix = i
|
58
|
+
# Compute suffix
|
59
|
+
unless suffices.empty?
|
60
|
+
i = 1
|
61
|
+
while suffices.include? i
|
62
|
+
i += 1
|
75
63
|
end
|
64
|
+
suffix = i
|
76
65
|
end
|
77
|
-
|
66
|
+
|
67
|
+
# Apply suffix
|
78
68
|
if suffix
|
79
69
|
proper + '-' + suffix.to_s
|
80
70
|
else
|
@@ -100,6 +90,20 @@ module CortexReaver
|
|
100
90
|
@canonical_name_attr || CANONICAL_NAME_ATTR
|
101
91
|
end
|
102
92
|
end
|
93
|
+
|
94
|
+
# Canonicalize only in the context of our parent's namespace.
|
95
|
+
# Takes a proper canonical name to check for conflicts with, and an optional
|
96
|
+
# id to ignore conflicts with
|
97
|
+
def self.similar_canonical_names(proper, opts={})
|
98
|
+
id = opts[:id]
|
99
|
+
similar = []
|
100
|
+
if filter(canonical_name_attr => proper).exclude(:id => id).limit(1).count > 0
|
101
|
+
# This name already exists, and it's not ours.
|
102
|
+
similar << proper
|
103
|
+
similar += filter(canonical_name_attr.like(/^#{proper}\-[0-9]+$/)).map(canonical_name_attr)
|
104
|
+
end
|
105
|
+
similar
|
106
|
+
end
|
103
107
|
end
|
104
108
|
end
|
105
109
|
end
|