YurtCMS 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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