quickpress 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,604 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+ require 'open-uri'
4
+ require 'tilt'
5
+ require 'quickpress/version'
6
+ require 'quickpress/wordpress'
7
+ require 'quickpress/cli'
8
+ require 'quickpress/options'
9
+
10
+ class String
11
+ # Removes starting whitespace.
12
+ def remove_starting
13
+ dup.remove_starting!
14
+ end
15
+
16
+ # Removes starting whitespace (destructive).
17
+ def remove_starting!
18
+ self.gsub(/^ +/, "")
19
+ end
20
+ end
21
+
22
+ # Controls all operations we can make.
23
+ #
24
+ # This module's a mess.
25
+ #
26
+ # The code sucks, things are not splitted well...
27
+ # There should be delegation up the ass but I couldn't stop
28
+ # coding.
29
+ #
30
+ # Gotta study ruby guidelines and see other librarys' codes
31
+ # to see where I can improve.
32
+ #
33
+ module Quickpress
34
+
35
+ # Main directory where we store everything.
36
+ ROOT_DIR = File.expand_path "~/.config/quickpress"
37
+ CONFIG_FILE = "#{ROOT_DIR}/config.yml"
38
+
39
+ @@inited = nil
40
+ @@ran_first_time = false
41
+
42
+ # URL of the default site used to post.
43
+ @@default_site = nil
44
+
45
+ # Nice name of the default site.
46
+ @@default_sitename = nil
47
+
48
+ @@username = nil
49
+ @@password = nil
50
+ @@connection = nil
51
+
52
+ # Supported templating languages and their file extensions.
53
+ @@supported_markup = [["markdown" , '.md'],
54
+ ["asciidoc" , '.adoc'],
55
+ ["erb" , '.erb'],
56
+ ["string" , '.str'],
57
+ ["erubis" , '.erubis'],
58
+ ["haml" , '.haml'],
59
+ ["sass" , '.sass'],
60
+ ["scss" , '.scss'],
61
+ ["less" , '.less'],
62
+ ["builder" , '.builder'],
63
+ ["liquid" , '.liquid'],
64
+ ["markdown" , '.md'],
65
+ ["textile" , '.textile'],
66
+ ["rdoc" , '.rdoc'],
67
+ ["radius" , '.radius'],
68
+ ["markaby" , '.mab'],
69
+ ["nokogiri" , '.nokogiri'],
70
+ ["coffeescript" , '.coffee'],
71
+ ["creole" , '.creole'],
72
+ ["mediawiki" , '.mw'],
73
+ ["yajl" , '.yajl'],
74
+ ["csv" , '.rcsv']]
75
+
76
+ module_function
77
+
78
+ # Loads default site from configuration file
79
+ def config_init
80
+
81
+ # Reading config file if exists
82
+ if File.exists? CONFIG_FILE
83
+ CLI::with_status("Initializing...") do
84
+
85
+ raw = File.read CONFIG_FILE
86
+ settings = YAML.load raw
87
+ settings = {} if not settings
88
+
89
+ @@default_site = settings["default_site"]
90
+ @@default_sitename = @@default_site.gsub(/htt(p|ps):\/\//, "").gsub(/\//, '-')
91
+ end
92
+ @@inited = true
93
+ end
94
+
95
+ FileUtils.mkdir_p ROOT_DIR if not File.exists? ROOT_DIR
96
+ end
97
+
98
+ # Executes at the first time, when there's no configuration
99
+ # directories.
100
+ #
101
+ # Asks stuff.
102
+ #
103
+ def first_time
104
+ puts <<-END.remove_starting!
105
+ Hello!
106
+ It looks like this is the first time you're
107
+ running quickpress.
108
+
109
+ Let's connect to your Wordpress(.com/.org) site.
110
+ END
111
+ puts
112
+
113
+ Quickpress::new_site(nil)
114
+ @@ran_first_time = true
115
+ end
116
+
117
+ # Adds site with URL `addr` to quickpress.
118
+ # If it's `nil`, will prompt the user for it.
119
+ def new_site(addr=nil)
120
+ return if @@ran_first_time
121
+ address = nil
122
+
123
+ # If retrying, go back here.
124
+ begin
125
+ address = addr.dup if not addr.nil? # cannot .dup NilClass
126
+ address ||= CLI::get("Address:")
127
+
128
+ address.gsub!(/http:\/\//, "")
129
+ address.gsub!(/www\./, "")
130
+ address.gsub!(/\/$/, "")
131
+
132
+ # Checking if site already exists
133
+ if File.exists? CONFIG_FILE
134
+ raw = File.read CONFIG_FILE
135
+
136
+ settings = {}
137
+ settings.merge!(YAML.load(raw))
138
+
139
+ settings["sites"].each do |s|
140
+ if address == s
141
+ puts
142
+ puts "There's already a site with address '#{address}'"
143
+ puts "Check it with `qp list-sites`."
144
+
145
+ if @@default_site == s
146
+ puts
147
+ puts "It's your default site, by the way"
148
+ end
149
+ exit 666
150
+ end
151
+ end
152
+ end
153
+
154
+ Quickpress::authenticate
155
+
156
+ # Will try to connect here.
157
+ # Might take a while.
158
+ CLI::with_status("Connecting...") do
159
+ @@connection = Wordpress.new(address, @@username, @@password)
160
+ end
161
+
162
+ puts <<-END.remove_starting!
163
+
164
+ Title: #{@@connection.title}
165
+ Tagline: #{@@connection.tagline}
166
+ Url: #{@@connection.url}
167
+ END
168
+
169
+ answer = CLI::ask "Is that right?"
170
+ fail "will retry" if not answer
171
+
172
+ # Last site added becomes the default
173
+ @@default_site = address
174
+
175
+ # Hey, this is our first site!
176
+ if not File.exists? CONFIG_FILE
177
+
178
+ # For a @@default_site like "http://myblog.com/this/dir"
179
+ #
180
+ # The @@default_sitename must be "myblog.com-this-dir"
181
+ @@default_sitename = address.gsub(/htt(p|ps):\/\//, "").gsub(/\//, '-')
182
+
183
+ # Saving to config file
184
+ settings = {}
185
+
186
+ settings["sites"] ||= []
187
+ settings["sites"] << @@default_site
188
+
189
+ settings["default_site"] = @@default_site
190
+
191
+ File.write(CONFIG_FILE, YAML.dump(settings))
192
+
193
+ # Config file exists
194
+ else
195
+
196
+ raw = File.read CONFIG_FILE
197
+
198
+ settings = {}
199
+ settings.merge!(YAML.load(raw))
200
+
201
+ settings["sites"] ||= []
202
+ settings["sites"] << @@default_site
203
+
204
+ settings["default_site"] = @@default_site
205
+
206
+ File.write(CONFIG_FILE, YAML.dump(settings))
207
+ end
208
+
209
+ rescue StandardError => e
210
+ retry if e.message =~ /will retry/
211
+
212
+ raise e
213
+ end
214
+ end
215
+
216
+ # Shows all saved sites.
217
+ def list_sites
218
+
219
+ # Hey, this is our first site!
220
+ if not File.exists? CONFIG_FILE
221
+ puts "No sites stored yet!"
222
+ puts
223
+ puts "Run `qp new-site` to create your first!"
224
+
225
+ else
226
+ raw = File.read CONFIG_FILE
227
+
228
+ settings = {}
229
+ settings.merge!(YAML.load(raw))
230
+
231
+ puts "Sites currently managed by quickpress:"
232
+ puts
233
+
234
+ settings["sites"].each_with_index do |site, i|
235
+
236
+ if @@default_site == site
237
+ puts (" %3d. %s <== default site" % [i, site])
238
+ else
239
+ puts (" %3d. %s" % [i, site])
240
+ end
241
+ end
242
+ end
243
+
244
+ end
245
+
246
+ def forget_site ids
247
+
248
+ # Hey, there's no sites added yet!
249
+ if not File.exists? CONFIG_FILE
250
+ puts "No sites managed with quickpress yet!"
251
+ puts "Add them with `qp new-site`"
252
+ exit 666
253
+ end
254
+
255
+ # Getting all sites from config file
256
+ raw = File.read CONFIG_FILE
257
+
258
+ settings = {}
259
+ settings.merge!(YAML.load(raw))
260
+
261
+ max_id = settings["sites"].size - 1
262
+ ids_to_delete = []
263
+
264
+ # Here we go!
265
+ ids.split(',').each do |id|
266
+
267
+ if not (0..max_id).include? id.to_i
268
+ puts "Invalid id!"
269
+ puts "Must be between 0 and #{max_id}."
270
+ next
271
+ end
272
+
273
+ puts "Will delete the following site:"
274
+ puts
275
+ puts settings["sites"][id.to_i]
276
+
277
+ if not $options[:force]
278
+ answer = CLI::ask("Is that right?")
279
+ if not answer
280
+ puts "Alright, then!"
281
+ next
282
+ end
283
+ end
284
+
285
+ ids_to_delete << id.to_i
286
+ end
287
+
288
+ # Forgetting a lot of sites at once
289
+ # Note: Is there a better way to do this?
290
+ # Once I delete an id, all the others change!
291
+ # I can't simply `each do delete` them.
292
+
293
+ ids_to_delete.each {|i| settings["sites"][i] = "will_delete" }
294
+
295
+ settings["sites"].reject! { |s| s == "will_delete" }
296
+
297
+ # Just in case we've just deleted the default site,
298
+ # let's grab the first one left
299
+ if not settings["sites"].include? @@default_site
300
+ if not settings["sites"].empty?
301
+ settings["default_site"] = settings["sites"].first
302
+ end
303
+ end
304
+
305
+ File.write(CONFIG_FILE, YAML.dump(settings))
306
+
307
+ # Ooh, boy
308
+ # We've just ran out of sites! Better delete that config file!
309
+ if settings["sites"].empty?
310
+ FileUtils.rm_f CONFIG_FILE
311
+ end
312
+ end
313
+
314
+ def use_site id
315
+ Quickpress::first_time if @@default_site.nil?
316
+ return if @@ran_first_time
317
+
318
+ # Hey, there's no sites added yet!
319
+ if not File.exists? CONFIG_FILE
320
+ puts "No sites managed with quickpress yet!"
321
+ puts ""
322
+ exit 666
323
+ end
324
+
325
+ # Getting all sites from config file
326
+ raw = File.read CONFIG_FILE
327
+
328
+ settings = {}
329
+ settings.merge!(YAML.load(raw))
330
+
331
+ max_id = settings["sites"].size - 1
332
+
333
+ if not (0..max_id).include? id.to_i
334
+ puts "Invalid id!"
335
+ puts "Must be between 0 and #{max_id}."
336
+ exit 666
337
+ end
338
+
339
+ site = settings["sites"][id]
340
+
341
+ puts "Default site: #{site}"
342
+ settings["default_site"] = site
343
+ File.write(CONFIG_FILE, YAML.dump(settings))
344
+ end
345
+
346
+ # Entrance for when we're creating a page or a post
347
+ # (`what` says so).
348
+ #
349
+ def new(what, filename=nil)
350
+ if filename.nil?
351
+
352
+ # Get editor to open temporary file
353
+ editor = ENV["EDITOR"]
354
+ if editor.nil?
355
+ editor = get("Which text editor we'll use?")
356
+ end
357
+
358
+ extension = nil
359
+
360
+ # No markup passed as argument
361
+ if $options[:markup].nil?
362
+ puts "Choose your templating language."
363
+ puts
364
+
365
+ @@supported_markup.each_with_index do |m, i|
366
+ puts (" %2d. %s (%s)" % [i, m[0], m[1]])
367
+ end
368
+ puts
369
+
370
+ id = CLI::get("Which one?").to_i
371
+
372
+ max_id = @@supported_markup.size - 1
373
+
374
+ if not (0..max_id).include? id
375
+ puts "Invalid id!"
376
+ puts "Must be between 0 and #{max_id}."
377
+ exit 666
378
+ end
379
+
380
+ extension = @@supported_markup[id][1]
381
+
382
+ # User specified filename to post
383
+ else
384
+ markup_id = nil
385
+ @@supported_markup.each_with_index do |m, i|
386
+
387
+ if m[0].casecmp($options[:markup]).zero?
388
+ markup_id = i
389
+ break
390
+ end
391
+ end
392
+
393
+ if markup_id.nil?
394
+ fail "Unknown markup laguage '#{$options[:markup]}'"
395
+ end
396
+
397
+ extension = @@supported_markup[markup_id][1]
398
+ end
399
+
400
+ # Create draft file
401
+ tempfile = Tempfile.new ['quickpress', extension]
402
+ tempfile.write "# Leave this file empty to cancel"
403
+ tempfile.flush
404
+
405
+ # Oh yeah, baby
406
+ `#{editor} #{tempfile.path}`
407
+
408
+ if tempfile.size.zero?
409
+ puts "Empty file: did nothing"
410
+ tempfile.close
411
+ exit 666
412
+ end
413
+
414
+ puts "File: '#{tempfile.path}'" if $options[:debug]
415
+
416
+ new_file(what, tempfile.path)
417
+ tempfile.close
418
+
419
+ else
420
+ # Post file and copy it to posted directory.
421
+ new_file(what, filename)
422
+ end
423
+ end
424
+
425
+ # Actually sends post/page `filename` to the blog.
426
+ def new_file(what, filename)
427
+ Quickpress::startup
428
+ html = Tilt.new(filename).render
429
+
430
+ # If successful, will store page id and link
431
+ id, link = nil, nil
432
+
433
+ if what == :post
434
+ title = CLI::get "Post title:"
435
+
436
+ puts "Existing blog categories:"
437
+ @@connection.categories.each { |c| puts "* #{c}" }
438
+
439
+ puts "Use a comma-separated list (eg. 'cat1, cat2, cat3')"
440
+ puts "Tab-completion works."
441
+ puts "(will create non-existing categories automatically)"
442
+
443
+ categories = CLI::tab_complete("Post categories:", @@connection.categories)
444
+
445
+ cats = []
446
+ categories.split(',').each { |c| cats << c.lstrip.strip }
447
+
448
+ CLI::with_status("Posting...") do
449
+
450
+ id, link = @@connection.post(:post_status => 'publish',
451
+ :post_date => Time.now,
452
+ :post_title => title,
453
+ :post_content => html,
454
+ :terms_names => {
455
+ :category => cats
456
+ })
457
+ end
458
+ puts "Post successful!"
459
+
460
+ elsif what == :page
461
+ title = CLI::get "Page title:"
462
+
463
+ CLI::with_status("Creating page...") do
464
+
465
+ id, link = @@connection.post(:post_status => 'publish',
466
+ :post_date => Time.now,
467
+ :post_title => title,
468
+ :post_content => html,
469
+ :post_type => 'page')
470
+ end
471
+ puts "Page created!"
472
+ end
473
+
474
+ puts <<-END.remove_starting!
475
+ id: #{id}
476
+ link: #{link}
477
+ END
478
+ end
479
+
480
+ # Deletes comma-separated list of posts/pages with `ids`.
481
+ # A single number is ok too.
482
+ def delete(what, ids)
483
+ Quickpress::startup
484
+
485
+ ids.split(',').each do |id|
486
+
487
+ thing = nil
488
+
489
+ CLI::with_status("Hold on a sec...") do
490
+
491
+ if what == :post
492
+ thing = @@connection.get_post id.to_i
493
+ elsif what == :page
494
+ thing = @@connection.get_page id.to_i
495
+ end
496
+
497
+ end
498
+
499
+ if what == :post
500
+ puts "Will delete the following post:"
501
+ elsif what == :page
502
+ puts "Will delete the following page:"
503
+ end
504
+
505
+ puts <<-END.remove_starting!
506
+
507
+ ID: #{thing["post_id"]}
508
+ Title: #{thing["post_title"]}
509
+ Date: #{thing["post_date"].to_time}
510
+ Status: #{thing["post_status"]}
511
+ URL: #{thing["link"]}
512
+
513
+ END
514
+
515
+ if not $options[:force]
516
+ answer = CLI::ask("Is that right?")
517
+ if not answer
518
+ puts "Alright, then!"
519
+ next
520
+ end
521
+ end
522
+
523
+ CLI::with_status("Deleting...") do
524
+ if what == :post
525
+ @@connection.delete_post id.to_i
526
+ elsif what == :page
527
+ @@connection.delete_page id.to_i
528
+ end
529
+ end
530
+ end
531
+ end
532
+
533
+ # Show last `ammount` of posts/pages in reverse order of
534
+ # publication.
535
+ #
536
+ def list(what, ammount)
537
+ Quickpress::startup
538
+
539
+ elements = nil
540
+ if what == :post
541
+ CLI::with_status("Retrieving posts...") do
542
+ elements = @@connection.get_posts ammount
543
+ end
544
+
545
+ elsif what == :page
546
+ CLI::with_status("Retrieving pages...") do
547
+ elements = @@connection.get_pages ammount
548
+ end
549
+ end
550
+
551
+ # Ugly as fuark :(
552
+ puts "+-----+---------------------------------------+-----------------------+--------+"
553
+ puts "| ID|Title |Date |Status |"
554
+ puts "+-----+---------------------------------------+-----------------------+--------+"
555
+ elements.each do |post|
556
+ puts sprintf("|%5d|%-39s|%s|%-8s|", post["post_id"].to_i,
557
+ post["post_title"],
558
+ post["post_date"].to_time,
559
+ post["post_status"])
560
+ end
561
+ puts "+-----+---------------------------------------+-----------------------+--------+"
562
+ end
563
+
564
+ def list_markup
565
+ puts "Name (file extension)"
566
+ puts
567
+
568
+ @@supported_markup.each do |m|
569
+ puts "* #{m[0]} (#{m[1]})"
570
+ end
571
+ end
572
+
573
+ private
574
+ module_function
575
+
576
+ # Initializes everything based on the config file or
577
+ # simply by asking the user.
578
+ def startup
579
+ Quickpress::first_time if @@default_site.nil?
580
+
581
+ puts "Using site '#{@@default_site}'"
582
+
583
+ Quickpress::authenticate
584
+
585
+ CLI::with_status("Connecting...") do
586
+ @@connection ||= Wordpress.new(@@default_site, @@username, @@password)
587
+ end
588
+ end
589
+
590
+ # Gets username and password.
591
+ #
592
+ # First, try getting from environment variables
593
+ # `QP_USERNAME` and `QP_PASSWORD`.
594
+ #
595
+ # If that fails, asks to the user.
596
+ def authenticate
597
+ @@username ||= ENV["QP_USERNAME"]
598
+ @@password ||= ENV["QP_PASSWORD"]
599
+
600
+ @@username ||= CLI::get("Username:")
601
+ @@password ||= CLI::get_secret("Password:")
602
+ end
603
+ end
604
+