YurtCMS 0.2.0

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/README ADDED
@@ -0,0 +1,18 @@
1
+ == Welcome to Yurt CMS
2
+
3
+ Yurt CMS is a content management system designed for web developers who create
4
+ mostly static web sites. In building this software, I tried to realize the
5
+ following key features:
6
+
7
+ * No Database: Content for mostly static sites should reside in editable
8
+ files, not databases.
9
+ * Version Control System Friendly: The web site should be easy to store
10
+ in a revision control system.
11
+ * Two-step deployment: Starting a new website should be as easy as running a
12
+ single command in the console and updating your webserver configuration.
13
+ * One-step go-live: Yurt CMS websites can be deployed simply by rsync'ing or
14
+ copying from the preview webroot to the live webroot.
15
+ * Web-based *and* console-based admin interfaces: Work where you're most
16
+ comfortable!
17
+
18
+
data/bin/yurt ADDED
@@ -0,0 +1,282 @@
1
+ #! /usr/bin/env ruby
2
+
3
+
4
+ # == Synopsis
5
+ #
6
+ # A simple commandline tool for basic site management
7
+ #
8
+ # == Usage
9
+ #
10
+ # yurt newsite foldername [port] <- creates a new directory structure for a
11
+ # Yurt CMS site, with a preconfigured
12
+ # apache conf file
13
+ # yurt newfile path/to/file <- creates a new content file
14
+ # yurt update [path/to/file] <- updates yurt infrastructure to latest
15
+ # version. path/to/file is optional if you're
16
+ # in the top level directory of your yurt
17
+ # install
18
+ # yurt newpartial path/to/file <- creates a new partial content file
19
+ # yurt generate path/to/file <- generates html from supplied content file
20
+ # yurt delete path/to/file <- deletes content file and html files
21
+ #
22
+ # == Author
23
+ #
24
+ # Robert Hahn <yurt_at_roberthahn_dot_ca>
25
+ #
26
+ # == Copyright
27
+ #
28
+ # Copyright (c)2006 Robert Hahn. Licensed under the same terms as the Camping framework
29
+ #
30
+
31
+ require File.dirname( __FILE__ ) + "/../lib/yurtcms"
32
+ require 'rdoc/usage'
33
+ require 'rbconfig.rb'
34
+ include Config
35
+
36
+ # this is needed to support the generate() function
37
+ class Hash
38
+ def method_missing(methID)
39
+ self[methID.id2name]
40
+ end
41
+ end
42
+
43
+
44
+
45
+ def newsite yurt_root, port
46
+ # create directory
47
+ Dir.mkdir yurt_root
48
+
49
+ # copy template from CONFIG["datadir"]/yurt/dir_template.zip
50
+ source_files = Dir.glob( File.dirname( __FILE__ ) + "/../site/**/*" )
51
+
52
+ source_files.each do |sf|
53
+ local_f = [ yurt_root, sf.gsub( /^.*?site\//, "" ) ].join( "/" )
54
+ case File.stat( sf ).ftype
55
+ when "directory"
56
+ Dir.mkdir local_f
57
+ File.chmod( 0777, local_f )
58
+ when "file"
59
+ source = File.read( sf )
60
+
61
+ # if the file we read in is yurt.conf, we need to modify it to point to the web root.
62
+ if File.basename( sf ) == "yurt.conf"
63
+ source.gsub!( /<%= port %>/, port )
64
+ source.gsub!( /<%= yurt_root %>/, yurt_root )
65
+ local_f.gsub!( /yurt\.conf/, File.basename( yurt_root ) + ".conf" )
66
+ end
67
+
68
+ writefile( source,local_f )
69
+ end
70
+ end
71
+
72
+ # write out a preferences file
73
+
74
+ writefile( "yurt_root: " + yurt_root, yurt_root + "/yurt.prefs" )
75
+ end
76
+
77
+ def update yurt_root
78
+
79
+ # copy template from CONFIG["datadir"]/yurt/dir_template.zip
80
+ source_files = Dir.glob( File.dirname( __FILE__ ) + "/../site/yurt/**/*" )
81
+
82
+ source_files.each do |sf|
83
+ local_f = [ yurt_root, sf.gsub( /^.*?site\//, "" ) ].join( "/" )
84
+ case File.stat( sf ).ftype
85
+ when "directory"
86
+ begin
87
+ Dir.mkdir local_f
88
+ rescue SystemCallError => e
89
+ unless e.errno == Errno::EEXIST::Errno
90
+ exit
91
+ end
92
+ end
93
+ File.chmod( 0777, local_f )
94
+ when "file"
95
+ source = File.read( sf )
96
+ writefile( source,local_f )
97
+ end
98
+ end
99
+ end
100
+
101
+ def writefile sf_str, df
102
+ File.open( df, "w" ) do |dest|
103
+ dest.puts sf_str
104
+ end
105
+ File.chmod( 0777, df )
106
+ end
107
+
108
+ def normalize_path path
109
+ # This wasn't something that could easily be done, as I realized there was
110
+ # a number of situations that could legitimately occur. The following comment
111
+ # block will describe the logic required:
112
+ #
113
+ # 1. Is the supplied #{ path } absolute (begins with "/")?
114
+ #
115
+ # - if 1., then test 2.
116
+ # - if not 1., compute final path by concatenating cwd to #{ path }, call it
117
+ # full_path, then test 2.
118
+
119
+ if path[0,1] != "/"
120
+ # it's a relative path
121
+ path = [ Dir.getwd, path ].join( "/" )
122
+ end
123
+
124
+ # 2. Can I chdir to full_path?
125
+ #
126
+ # - If 2., getwd, then use it to locate yurt.prefs, then test 3.
127
+ # - If not 2, raise an error (destination not valid)
128
+
129
+ begin
130
+ Dir.chdir( File.dirname( path ) )
131
+ path = [ Dir.getwd, File.basename( path ) ].join( "/" )
132
+ rescue Exception => e
133
+ puts e.message
134
+ exit 1
135
+ end
136
+
137
+ # The following yield statement allows for implementation-specific tests to
138
+ # validate the path
139
+ yield path if block_given?
140
+
141
+ return path
142
+ end
143
+
144
+ def find_yurt_pref full_path
145
+ fp = full_path
146
+ yurt_root = nil
147
+ if File.exists? [ fp, "yurt.prefs" ].join( "/" )
148
+ yurt_root = fp
149
+ else
150
+ puts fp
151
+ while fp.include? "/content"
152
+ fp = fp.gsub( /content\/.*?$/, "" )
153
+ if File.exists? [ fp, "yurt.prefs" ].join( "/" )
154
+ yurt_root = fp
155
+ break
156
+ end
157
+ end
158
+ end
159
+
160
+ return yurt_root
161
+ end
162
+
163
+ def newfile path
164
+ # create file
165
+ File.open( path, "w" ) do |f|
166
+ f.puts "---"
167
+ f.puts "template: template.html"
168
+ f.puts "title: enter page title"
169
+ f.puts "description: enter page meta description content"
170
+ f.puts "keywords: enter keywords here"
171
+ f.puts "content: |-"
172
+ f.puts " Enter your content here. Please ensure that you begin each new line with two spaces while in this area."
173
+ end
174
+ File.chmod( 0777, path )
175
+ end
176
+
177
+ def newpartial path
178
+ # create file
179
+ File.open( path, "w" ) do |f|
180
+ f.puts "---"
181
+ f.puts "partial: yes"
182
+ f.puts "content: |-"
183
+ f.puts " Enter your content here. Please ensure that you begin each new line with two spaces while in this area."
184
+ end
185
+ File.chmod( 0777, path )
186
+ end
187
+
188
+ def get_yurt_path_pieces path
189
+ path = normalize_path path do |p|
190
+ # Is the computed path in #{ yurt_root }/content, and does a yurt.prefs file exist?
191
+ unless ( p.include? "/content" ) && find_yurt_pref( p )
192
+ puts "Not a valid yurt CMS web path - the file must be in /content, and a yurt.prefs file must be present."
193
+ exit 1
194
+ end
195
+ # are we about to generate a file?
196
+ #unless File.exists?( p ) && File.ftype( p ) == "file"
197
+ # puts "Not a valid yurt CMS file. If you need to generate a file, please check your path."
198
+ # exit 1
199
+ #end
200
+ end
201
+
202
+ yurt_root = find_yurt_pref( path )
203
+
204
+ file_to_generate = path
205
+ file_to_generate[ yurt_root + "content/" ] = ''
206
+ return [ yurt_root, file_to_generate ]
207
+ end
208
+
209
+ def generate yurt_root, file_to_generate
210
+ y = YurtCMS.new( yurt_root )
211
+
212
+ y.write_all_files( y.get_file_metadata( file_to_generate ) )
213
+ end
214
+
215
+ def delete yurt_root, file_to_generate
216
+ y = YurtCMS.new( yurt_root )
217
+ y.delete( file_to_generate )
218
+ end
219
+
220
+ def mkdir yurt_root, file_to_generate
221
+ y = YurtCMS.new( yurt_root )
222
+ y.make_new_dir( File.dirname( file_to_generate ), File.basename( file_to_generate ) )
223
+ end
224
+
225
+ case
226
+ when ARGV[0] == "newsite"
227
+ # check for and collect folder name
228
+ yurt_root = normalize_path ARGV[ 1 ]
229
+
230
+ port = "9878"
231
+ port = ARGV[ 2 ] if ARGV[ 2 ] && ARGV[ 2 ].match( /^\d*$/ )
232
+
233
+ newsite( yurt_root, port )
234
+ when ARGV[0] == "update"
235
+ if ARGV[ 1 ]
236
+ yurt_root = find_yurt_pref ARGV[ 1 ]
237
+ else
238
+ yurt_root = find_yurt_pref Dir.getwd
239
+ end
240
+ update yurt_root
241
+ when ARGV[0] == "newfile"
242
+ # check for a path/to/file
243
+ path = normalize_path ARGV[ 1 ] do |p|
244
+ # Is the computed path in #{ yurt_root }/content, and does a yurt.prefs file exist?
245
+ unless ( p.include? "/content" ) && find_yurt_pref( p )
246
+ puts "Not a valid yurt CMS web path - the file must be in /content, and a yurt.prefs file must be present."
247
+ exit 1
248
+ end
249
+ end
250
+
251
+ newfile( path )
252
+ when ARGV[0] == "newpartial"
253
+ # check for a path/to/file
254
+ path = normalize_path ARGV[ 1 ] do |p|
255
+ # Is the computed path in #{ yurt_root }/content, and does a yurt.prefs file exist?
256
+ unless ( p.include? "/content" ) && find_yurt_pref( p )
257
+ puts "Not a valid yurt CMS web path - the file must be in /content, and a yurt.prefs file must be present."
258
+ exit 1
259
+ end
260
+ end
261
+
262
+ newpartial( path )
263
+ when ARGV[0] == "generate"
264
+ # check for a path/to/file
265
+ yurt_root, file_to_generate = get_yurt_path_pieces( ARGV[1] )
266
+
267
+ generate( yurt_root, file_to_generate )
268
+ when ARGV[0] == "mkdir"
269
+ # check for a path/to/file
270
+ yurt_root, file_to_generate = get_yurt_path_pieces( ARGV[1] )
271
+
272
+ mkdir( yurt_root, file_to_generate )
273
+ when ARGV[0] == "delete"
274
+ # check for a path/to/file
275
+ yurt_root, file_to_generate = get_yurt_path_pieces( ARGV[1] )
276
+
277
+ delete( yurt_root, file_to_generate )
278
+ else
279
+ RDoc::usage
280
+ end
281
+
282
+
data/lib/yurtcms.rb ADDED
@@ -0,0 +1,175 @@
1
+
2
+ require 'yaml'
3
+
4
+ class YurtCMS
5
+ attr_reader :data_store, :web_root, :includes, :metatags, :partial, :ext
6
+ def initialize yurt_root
7
+ require 'rubygems'
8
+ require_gem 'BlueCloth'
9
+
10
+ @yurt_root = sanitize_path( yurt_root )
11
+ @data_store = [ @yurt_root, "content/" ].join
12
+ @web_root = [ @yurt_root, "htdocs/" ].join
13
+ @includes = [ @web_root, "includes/content/" ].join
14
+ @metatags = ".metatags"
15
+ @partial = ".partial"
16
+ @ext = ".html"
17
+ end
18
+
19
+ def merge path, file
20
+ [ path, '/', file ].join.gsub( /\/\/*/, "/" )
21
+ end
22
+
23
+ def path_to_orig_content path
24
+ [ @data_store, path ].join
25
+ end
26
+
27
+ def path_to_metatags path
28
+ [ @includes, path, @metatags, @ext ].join
29
+ end
30
+
31
+ def path_to_content path
32
+ [ @includes, path, @ext ].join
33
+ end
34
+
35
+ def path_to_placeholder path
36
+ [ @web_root, path, @ext].join
37
+ end
38
+
39
+ def get_dir_listing path
40
+ @contents = Hash.new
41
+ Dir.chdir( path_to_orig_content( path ) ) do
42
+ Dir.foreach(".") do |entry|
43
+ @contents[entry] = [ File.ftype(entry) ] unless ( entry =~ /^\./ or entry =~ /~$/ )
44
+ end
45
+ end
46
+
47
+ @contents
48
+ end
49
+
50
+ def sanitize_path path
51
+ if path == ""
52
+ "/"
53
+ else
54
+ path.gsub!( /^\//, '' )
55
+ path.gsub!( /\/$/, '' )
56
+ [ '/', path, '/' ].join.gsub( /\/\/*/,"/" )
57
+ end
58
+ end
59
+
60
+ def sanitize_filename f
61
+ f.gsub!(/^\//, "")
62
+ f.gsub!(/[^\w\.\-]/, "_")
63
+
64
+ f
65
+ end
66
+
67
+ def make_new_dir path, name
68
+ p = merge( path, sanitize_filename( name ) )
69
+
70
+ Dir.mkdir( [ @data_store, p ].join )
71
+ Dir.mkdir( [ @includes, p ].join )
72
+ Dir.mkdir( [ @web_root, p ].join )
73
+
74
+ File.chmod( 0777, [ @data_store, p ].join )
75
+ File.chmod( 0777, [ @includes, p ].join )
76
+ File.chmod( 0777, [ @web_root, p ].join )
77
+ end
78
+
79
+ def make_content_as_yaml input
80
+ content = Hash.new
81
+ if input.partial == "y"
82
+ content[ "partial" ] = input.partial
83
+ else
84
+ content[ "title" ] = input.title
85
+ content[ "description" ] = input.description
86
+ content[ "keywords" ] = input.keywords
87
+ content[ "template" ] = input.template
88
+ end
89
+ content[ "content" ] = input.content
90
+
91
+ content.to_yaml
92
+ end
93
+
94
+ def make_metatags input
95
+ t = [ '<!--#set var="TITLE" value="', input.title, '" -->', "\n" ].join if input.title =~ /\w/
96
+ d = [ '<!--#set var="DESCRIPTION" value="', input.description, '" -->', "\n" ].join if input.description =~ /\w/
97
+ k = [ '<!--#set var="KEYWORDS" value="', input.keywords, '" -->', "\n" ].join if input.keywords =~ /\w/
98
+
99
+ [ t, d, k ].join
100
+ end
101
+
102
+ def parse_content c
103
+ BlueCloth.new(c).to_html
104
+ end
105
+
106
+ def make_placeholder path, template
107
+ if template == nil or template == ""
108
+ template = "template.html"
109
+ end
110
+
111
+ [
112
+ '<!--#set var="PAGE" value="',
113
+ path,
114
+ '" -->',
115
+ "\n",
116
+ '<!--#include virtual="/includes/',
117
+ template,
118
+ '" -->',
119
+ "\n"
120
+ ].join
121
+ end
122
+
123
+ def delete obj
124
+ f = path_to_orig_content( obj )
125
+
126
+ if File.exists?( f )
127
+ if File.ftype( f ) == "directory"
128
+ Dir.delete( f )
129
+ [ :path_to_content,
130
+ :path_to_placeholder ].each do |p|
131
+ f = send( p, obj ).gsub(/\.html/, '')
132
+ Dir.delete( f )
133
+ end
134
+ else
135
+ File.delete( f )
136
+ [ :path_to_metatags,
137
+ :path_to_content,
138
+ :path_to_placeholder ].each do |p|
139
+ f = send( p, obj )
140
+ File.delete( f )
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ def get_file_metadata path
147
+ metadata = YAML.load( File.open( path_to_orig_content( path ) ) )
148
+ metadata[ "filename" ] = File.basename( path_to_orig_content( path ) )
149
+ metadata[ "path" ] = File.dirname( path )
150
+
151
+ metadata
152
+ end
153
+
154
+ def write_file path, contents
155
+ f = File.new( path, "w" )
156
+ f.puts contents
157
+ f.close
158
+ begin
159
+ File.chmod(0776,path)
160
+ rescue
161
+ end
162
+ end
163
+
164
+ def write_all_files input
165
+ path = merge( input.path, sanitize_filename( input.filename ) )
166
+
167
+ write_file( path_to_orig_content( path ), make_content_as_yaml( input ) )
168
+ write_file( path_to_placeholder( path ), make_placeholder( path, input.template ) )
169
+ write_file( path_to_content( path ), parse_content( input.content ) )
170
+
171
+ if input.partial != "y"
172
+ write_file( path_to_metatags( path ), make_metatags( input ) )
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,14 @@
1
+ ---
2
+ title: ""
3
+ description: ""
4
+ content: |-
5
+ # Congratulations! #
6
+
7
+ You have successfully completed a Yurt CMS installation! At this point you have a few possible next steps in building your site:
8
+
9
+ * Designing a new template to use with this site
10
+ * Setting up and pouring in the content through the [web interface](/yurt/)
11
+ * Setting up and pouring in content in your terminal app.
12
+
13
+ (Help files to come)
14
+ keywords: ""
@@ -0,0 +1,11 @@
1
+ <h1>Congratulations!</h1>
2
+
3
+ <p>You have successfully completed a Yurt CMS installation! At this point you have a few possible next steps in building your site:</p>
4
+
5
+ <ul>
6
+ <li>Designing a new template to use with this site</li>
7
+ <li>Setting up and pouring in the content through the <a href="/yurt/">web interface</a></li>
8
+ <li>Setting up and pouring in content in your terminal app.</li>
9
+ </ul>
10
+
11
+ <p>(Help files to come)</p>
@@ -0,0 +1,22 @@
1
+ <!--#include virtual="/includes/template.metatags.html" -->
2
+ <!--#include virtual="/includes/content/${PAGE}.metatags.html" -->
3
+
4
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
7
+ <head>
8
+ <title><!--#echo var="TITLE" --></title>
9
+ <meta name="keywords" content="<!--#echo var="KEYWORDS" -->" />
10
+ <meta name="description" content="<!--#echo var="DESCRIPTION" -->" />
11
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
12
+ <link rel="stylesheet" type="text/css" href="/media/css/styles.css" />
13
+ <script type="text/javascript" language="javascript" src="/media/js/prototype.js"></script>
14
+ </head>
15
+ <body>
16
+ <img src="/yurt/media/images/admin_head.jpg" width="481" height="79" border="0" alt="yurt: a content management system" id="header_img" />
17
+
18
+ <div id="main">
19
+ <!--#include virtual="/includes/content/${PAGE}.html" -->
20
+ </div>
21
+ </body>
22
+ </html>
@@ -0,0 +1,4 @@
1
+ <!--#set var="TITLE" value="Yurt CMS site" -->
2
+ <!--#set var="DESCRIPTION" value="This is a fresh Yurt CMS installation. Please change the generic site metatags!" -->
3
+ <!--#set var="KEYWORDS" value="yurt cms" -->
4
+
@@ -0,0 +1,2 @@
1
+ <!--#set var="PAGE" value="/index" -->
2
+ <!--#include virtual="/includes/template.html" -->
@@ -0,0 +1,106 @@
1
+ body {
2
+ margin: 0;
3
+ padding: 0;
4
+ background-color: #eeeeea;
5
+ background-image: url("/yurt/media/images/admin_bg.gif");
6
+ background-position: top left;
7
+ background-repeat: repeat-x;
8
+ font-size: 100%;
9
+ }
10
+
11
+ #header_img {
12
+ position: absolute;
13
+ margin: 0;
14
+ padding: 0;
15
+ }
16
+
17
+ #menu {
18
+ padding: 0;
19
+ position: absolute;
20
+ margin-left: 50px;
21
+ margin-top: 100px;
22
+ width: 220px;
23
+ font-size: 0.8em;
24
+ font-family: arial, sans-serif;
25
+ }
26
+
27
+ #menu ul {
28
+ margin: 0;
29
+ padding: 0;
30
+ font-size: 1.0em;
31
+ margin-bottom: 1.0em;
32
+ list-style: none;
33
+ }
34
+
35
+ #menu li {
36
+ margin: 0;
37
+ margin-bottom: 0.3em;
38
+ padding: 0;
39
+ }
40
+
41
+ #menu a {
42
+ text-decoration: none;
43
+ }
44
+
45
+ #menu a:hover {
46
+ text-decoration: underline;
47
+ }
48
+
49
+ #main {
50
+ padding: 0;
51
+ position: absolute;
52
+ margin-left: 285px;
53
+ margin-top: 100px;
54
+ width: 600px;
55
+ font-size: 1.0em;
56
+ font-family: optima, arial, sans-serif;
57
+ line-height: 1.3em;
58
+ }
59
+
60
+ #main h1 {
61
+ margin: 0;
62
+ padding: 0;
63
+ font-size: 1.4em;
64
+ margin-bottom: 0.3em;
65
+ }
66
+
67
+ #main h2 {
68
+ margin: 0;
69
+ padding: 0;
70
+ font-size: 1.2em;
71
+ margin-bottom: 0.2em;
72
+ }
73
+
74
+ #main h3 {
75
+ margin: 0;
76
+ padding: 0;
77
+ font-size: 1.1em;
78
+ margin-bottom: 0.2em;
79
+ }
80
+
81
+ #main h4, #main h5, #main h6 {
82
+ margin: 0;
83
+ padding: 0;
84
+ font-size: 1.0em;
85
+ }
86
+
87
+ #main p {
88
+ margin: 0;
89
+ padding: 0;
90
+ font-size: 1.0em;
91
+ margin-bottom: 1.0em;
92
+ text-indent: 2em;
93
+ }
94
+
95
+ #main ul {
96
+ margin: 0;
97
+ padding: 0;
98
+ padding-left: 2em;
99
+ font-size: 1.0em;
100
+ margin-bottom: 1.0em;
101
+ list-style: square;
102
+ }
103
+
104
+ #main li {
105
+ margin-bottom: 0.3em;
106
+ }
Binary file