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.
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