cortex-reaver 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/bin/cortex_reaver +9 -2
  2. data/lib/cortex_reaver.rb +61 -14
  3. data/lib/cortex_reaver/config.rb +6 -1
  4. data/lib/cortex_reaver/controller/journal.rb +3 -1
  5. data/lib/cortex_reaver/controller/main.rb +10 -9
  6. data/lib/cortex_reaver/controller/page.rb +4 -2
  7. data/lib/cortex_reaver/controller/photograph.rb +3 -1
  8. data/lib/cortex_reaver/controller/project.rb +1 -1
  9. data/lib/cortex_reaver/controller/tag.rb +1 -1
  10. data/lib/cortex_reaver/helper/canonical.rb +1 -1
  11. data/lib/cortex_reaver/helper/crud.rb +7 -15
  12. data/lib/cortex_reaver/helper/navigation.rb +16 -0
  13. data/lib/cortex_reaver/helper/pages.rb +62 -0
  14. data/lib/cortex_reaver/helper/tags.rb +2 -3
  15. data/lib/cortex_reaver/migrations/009_mysql.rb +8 -4
  16. data/lib/cortex_reaver/migrations/010_pageparents.rb +17 -0
  17. data/lib/cortex_reaver/model/page.rb +68 -7
  18. data/lib/cortex_reaver/plugin.rb +4 -0
  19. data/lib/cortex_reaver/public/css/main.css +4 -1
  20. data/lib/cortex_reaver/public/images/tag.gif +0 -0
  21. data/lib/cortex_reaver/public/js/autocompletefb.js +125 -0
  22. data/lib/cortex_reaver/snippets/ramaze/dispatcher/file.rb +1 -1
  23. data/lib/cortex_reaver/support/canonical.rb +42 -38
  24. data/lib/cortex_reaver/support/renderer.rb +10 -72
  25. data/lib/cortex_reaver/support/sequenceable.rb +1 -1
  26. data/lib/cortex_reaver/support/tags.rb +17 -6
  27. data/lib/cortex_reaver/version.rb +2 -2
  28. data/lib/cortex_reaver/view/journals/journal.rhtml +4 -3
  29. data/lib/cortex_reaver/view/pages/form.rhtml +1 -0
  30. data/lib/cortex_reaver/view/pages/list.rhtml +3 -3
  31. data/lib/cortex_reaver/view/pages/show.rhtml +2 -8
  32. data/lib/cortex_reaver/view/photographs/show.rhtml +1 -1
  33. data/lib/cortex_reaver/view/projects/show.rhtml +3 -2
  34. data/lib/cortex_reaver/view/tags/list.rhtml +4 -3
  35. data/lib/cortex_reaver/view/tags/show.rhtml +2 -2
  36. data/lib/cortex_reaver/view/text_layout.rhtml +12 -9
  37. data/lib/cortex_reaver/view/users/list.rhtml +2 -2
  38. 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=\"autobox\" value=\"#{attr_h(tags_on(model, false))}\" />"
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
- if db.is_a? Sequel::MySQL::Database
11
- # Use InnoDB storage for everything
12
- db.tables.each do |table|
13
- db << "ALTER TABLE `#{table.to_s}` ENGINE = InnoDB;"
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 :comments, :class => 'CortexReaver::Comment'
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 you
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 self.get(id)
46
- self[:name => id] || self[id]
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/' + name
105
+ '/pages/atom/' + id.to_s
59
106
  end
60
107
 
61
108
  def url
62
- '/' + name
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(
@@ -0,0 +1,4 @@
1
+ module CortexReaver
2
+ module Plugin
3
+ end
4
+ end
@@ -256,9 +256,12 @@ th {
256
256
  font-size: 75%;
257
257
  }
258
258
 
259
- .tags {
259
+ ul.tags {
260
260
  margin: 0px;
261
261
  padding: 0px;
262
+ background: url(/images/tag.gif) no-repeat;
263
+ background-position: 0px 3px;
264
+ padding-left: 12px;
262
265
  }
263
266
 
264
267
  .tags li {
@@ -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
+
@@ -25,7 +25,7 @@ module Ramaze
25
25
  end
26
26
 
27
27
  if ::File.directory?(joined)
28
- Dir[joined/"{#{INDICES.join(',')}}"].first || joined
28
+ Dir[::File.join(joined, "{#{INDICES.join(',')}}")].first || joined
29
29
  else
30
30
  joined
31
31
  end
@@ -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, id = nil)
29
+ # with opts[:id]
30
+ def self.canonicalize(string, opts={})
26
31
  # Lower case, remove special chars, and replace with hyphens.
27
- proper = string.downcase.gsub(/[^a-z0-9_]/, '-').squeeze('-')[0..250].sub(/-$/, '')
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
- if proper != filter(:id => id).map(canonical_name_attr).first
38
- # We don't already have this name.
39
-
40
- similar = []
42
+ # Get similar names from the class
43
+ similar = similar_canonical_names(proper, opts)
41
44
 
42
- if filter(canonical_name_attr => proper).limit(1).count > 0
43
- similar << proper
44
- # This name already exists, and it's not ours!
45
- similar += filter(canonical_name_attr.like(/^#{proper}\-[0-9]+$/)).map(canonical_name_attr)
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
- # Check for reserved names
49
- reserved_canonical_names.each do |name|
50
- if name =~ /^#{proper}(-\d+)?$/
51
- similar << name
52
- end
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
- # Compute suffix
69
- unless suffices.empty?
70
- i = 1
71
- while suffices.include? i
72
- i += 1
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