quickpress 0.0.1

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