quickpress 0.0.1 → 0.1.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/lib/quickpress.rb CHANGED
@@ -6,6 +6,7 @@ require 'quickpress/version'
6
6
  require 'quickpress/wordpress'
7
7
  require 'quickpress/cli'
8
8
  require 'quickpress/options'
9
+ require 'digest/md5'
9
10
 
10
11
  class String
11
12
  # Removes starting whitespace.
@@ -36,7 +37,8 @@ module Quickpress
36
37
  ROOT_DIR = File.expand_path "~/.config/quickpress"
37
38
  CONFIG_FILE = "#{ROOT_DIR}/config.yml"
38
39
 
39
- @@inited = nil
40
+ @@inited = nil # config
41
+ @@started = false # overall
40
42
  @@ran_first_time = false
41
43
 
42
44
  # URL of the default site used to post.
@@ -205,6 +207,7 @@ module Quickpress
205
207
 
206
208
  File.write(CONFIG_FILE, YAML.dump(settings))
207
209
  end
210
+ puts "Site added"
208
211
 
209
212
  rescue StandardError => e
210
213
  retry if e.message =~ /will retry/
@@ -303,6 +306,7 @@ module Quickpress
303
306
  end
304
307
 
305
308
  File.write(CONFIG_FILE, YAML.dump(settings))
309
+ puts "Forgotten"
306
310
 
307
311
  # Ooh, boy
308
312
  # We've just ran out of sites! Better delete that config file!
@@ -422,6 +426,38 @@ module Quickpress
422
426
  end
423
427
  end
424
428
 
429
+ # Pretty-prints categories list.
430
+ def list_categories
431
+ Quickpress::startup
432
+
433
+ # Will show categories in columns of n
434
+ columns = 5
435
+ table = @@connection.categories.each_slice(columns).to_a
436
+
437
+ puts
438
+ Thor::Shell::Basic.new.print_table table
439
+ end
440
+
441
+ # Pretty-prints all options of the Wordpress site.
442
+ def list_options
443
+ Quickpress::startup
444
+ options = @@connection.get_options
445
+
446
+ puts
447
+ Thor::Shell::Basic.new.print_table options
448
+ end
449
+
450
+ # Pretty-prints all users currently registered on the site.
451
+ def list_users
452
+ Quickpress::startup
453
+ users = @@connection.get_users
454
+
455
+ users.each do |user|
456
+ puts
457
+ Thor::Shell::Basic.new.print_table user
458
+ end
459
+ end
460
+
425
461
  # Actually sends post/page `filename` to the blog.
426
462
  def new_file(what, filename)
427
463
  Quickpress::startup
@@ -431,16 +467,24 @@ module Quickpress
431
467
  id, link = nil, nil
432
468
 
433
469
  if what == :post
434
- title = CLI::get "Post title:"
435
470
 
436
- puts "Existing blog categories:"
437
- @@connection.categories.each { |c| puts "* #{c}" }
471
+ # User specified title/categories on command line?
472
+ title = $options[:title]
473
+ if title.nil?
474
+ title = CLI::get "Post title:"
475
+ end
438
476
 
439
- puts "Use a comma-separated list (eg. 'cat1, cat2, cat3')"
440
- puts "Tab-completion works."
441
- puts "(will create non-existing categories automatically)"
477
+ categories = $options[:category]
478
+ if categories.nil?
479
+ puts "Existing blog categories:"
480
+ Quickpress::list_categories
481
+ puts
482
+ puts "Use a comma-separated list (eg. 'cat1, cat2, cat3')"
483
+ puts "Tab-completion works."
484
+ puts "(will create non-existing categories automatically)"
442
485
 
443
- categories = CLI::tab_complete("Post categories:", @@connection.categories)
486
+ categories = CLI::tab_complete("Post categories:", @@connection.categories)
487
+ end
444
488
 
445
489
  cats = []
446
490
  categories.split(',').each { |c| cats << c.lstrip.strip }
@@ -458,7 +502,10 @@ module Quickpress
458
502
  puts "Post successful!"
459
503
 
460
504
  elsif what == :page
461
- title = CLI::get "Page title:"
505
+ title = $options[:title]
506
+ if title.nil?
507
+ title = CLI::get "Page title:"
508
+ end
462
509
 
463
510
  CLI::with_status("Creating page...") do
464
511
 
@@ -477,6 +524,131 @@ module Quickpress
477
524
  END
478
525
  end
479
526
 
527
+ # Entrance for when we're editing a page or a post
528
+ # with numerical `id` (`what` says so).
529
+ #
530
+ def edit(what, id, filename=nil)
531
+ Quickpress::startup
532
+
533
+ # Get previous content
534
+ old_content = nil
535
+ if what == :post
536
+ post = @@connection.get_post id
537
+
538
+ old_content = post["post_content"]
539
+ else
540
+ page = @@connection.get_page id
541
+
542
+ old_content = page["post_content"]
543
+ end
544
+
545
+ if filename.nil?
546
+
547
+ # Get editor to open temporary file
548
+ editor = ENV["EDITOR"]
549
+ if editor.nil?
550
+ editor = get("Which text editor we'll use?")
551
+ end
552
+
553
+ # Create draft file
554
+ tempfile = Tempfile.new ['quickpress', '.html']
555
+ tempfile.write old_content
556
+ tempfile.flush
557
+
558
+ # Oh yeah, baby
559
+ `#{editor} #{tempfile.path}`
560
+
561
+ if tempfile.size.zero?
562
+ puts "Empty file: did nothing"
563
+ tempfile.close
564
+ exit 666
565
+ end
566
+
567
+ tempfile.close # Apparently, calling `tempfile.flush`
568
+ tempfile.open # won't do it. Need to close and reopen.
569
+
570
+ md5old = Digest::MD5.hexdigest old_content
571
+ md5new = Digest::MD5.hexdigest tempfile.read
572
+
573
+ if (md5old == md5new) and (not $options[:force])
574
+ puts "Contents unchanged: skipping"
575
+ puts "(use --force if you want to do it anyway)"
576
+ return
577
+ end
578
+
579
+ puts "File: '#{tempfile.path}'" if $options[:debug]
580
+
581
+ edit_file(what, id, tempfile.path)
582
+ tempfile.close
583
+
584
+ else
585
+
586
+ md5old = Digest::MD5.hexdigest old_content
587
+ md5new = Digest::MD5.hexdigest File.read(filename)
588
+
589
+ if (md5old == md5new) and (not $options[:force])
590
+ puts "Contents unchanged: skipping"
591
+ puts "(use --force if you want to do it anyway)"
592
+ return
593
+ end
594
+
595
+ edit_file(what, id, filename)
596
+ end
597
+ end
598
+
599
+ # Actually edits post/page `filename` to the blog.
600
+ def edit_file(what, id, filename)
601
+
602
+ html = Tilt.new(filename).render
603
+
604
+ link = nil
605
+
606
+ if what == :post
607
+
608
+ # User specified title/categories on command line?
609
+ title = $options[:title]
610
+ if title.nil?
611
+ title = CLI::get("New Post title:", true)
612
+ end
613
+
614
+ categories = $options[:category]
615
+ if categories.nil?
616
+ puts "Existing blog categories:"
617
+ Quickpress::list_categories
618
+ puts
619
+ puts "Use a comma-separated list (eg. 'cat1, cat2, cat3')"
620
+ puts "Tab-completion works."
621
+ puts "(will create non-existing categories automatically)"
622
+ puts "(leave empty to keep current categories)"
623
+
624
+ categories = CLI::tab_complete("Post categories:", @@connection.categories)
625
+ end
626
+
627
+ cats = []
628
+ categories.split(',').each { |c| cats << c.lstrip.strip }
629
+
630
+ CLI::with_status("Editing post...") do
631
+ link = @@connection.edit_post(id, html, title, categories)
632
+ end
633
+
634
+ elsif what == :page
635
+ title = $options[:title]
636
+ if title.nil?
637
+ title = CLI::get("New Page title:", true)
638
+ end
639
+
640
+ CLI::with_status("Editing Page...") do
641
+ link = @@connection.edit_page(id, html, title)
642
+ end
643
+ end
644
+
645
+ puts <<-END.remove_starting!
646
+ Edit successful!
647
+ link: #{link}
648
+ END
649
+ end
650
+
651
+
480
652
  # Deletes comma-separated list of posts/pages with `ids`.
481
653
  # A single number is ok too.
482
654
  def delete(what, ids)
@@ -527,6 +699,7 @@ module Quickpress
527
699
  @@connection.delete_page id.to_i
528
700
  end
529
701
  end
702
+ puts "Deleted!"
530
703
  end
531
704
  end
532
705
 
@@ -570,12 +743,35 @@ module Quickpress
570
743
  end
571
744
  end
572
745
 
746
+ # Shows comment count according to their status.
747
+ def status_comments
748
+ Quickpress::startup
749
+ status = @@connection.get_comment_status
750
+
751
+ puts
752
+ Thor::Shell::Basic.new.print_table status
753
+ end
754
+
755
+ def status_categories
756
+ Quickpress::startup
757
+ status = @@connection.get_category_status
758
+
759
+ if $options[:"non-empty"]
760
+ status.reject! { |s| s[1].zero? }
761
+ end
762
+
763
+ puts
764
+ Thor::Shell::Basic.new.print_table status
765
+ end
766
+
573
767
  private
574
768
  module_function
575
769
 
576
770
  # Initializes everything based on the config file or
577
771
  # simply by asking the user.
578
772
  def startup
773
+ return if @started
774
+
579
775
  Quickpress::first_time if @@default_site.nil?
580
776
 
581
777
  puts "Using site '#{@@default_site}'"
@@ -585,15 +781,28 @@ module Quickpress
585
781
  CLI::with_status("Connecting...") do
586
782
  @@connection ||= Wordpress.new(@@default_site, @@username, @@password)
587
783
  end
784
+ @started = true
588
785
  end
589
786
 
590
- # Gets username and password.
787
+ # Gets username and password for the Wordpress site.
788
+ #
789
+ # There's three ways of authenticating,
790
+ # in order of importance:
591
791
  #
592
- # First, try getting from environment variables
593
- # `QP_USERNAME` and `QP_PASSWORD`.
792
+ # 1. Command-line options.
793
+ # User can specify user/pass with the `-u` and `-p`
794
+ # options, overrides everything else.
795
+ # 2. Environment variables.
796
+ # Whatever's inside `QP_USERNAME` and `QP_PASSWORD`
797
+ # envvars will be used.
798
+ # 3. Ask the user.
799
+ # If all else fails, we'll ask the user for
800
+ # username/password.
594
801
  #
595
- # If that fails, asks to the user.
596
802
  def authenticate
803
+ @@username ||= $options[:user]
804
+ @@password ||= $options[:pass]
805
+
597
806
  @@username ||= ENV["QP_USERNAME"]
598
807
  @@password ||= ENV["QP_PASSWORD"]
599
808
 
@@ -18,11 +18,20 @@ module Quickpress
18
18
  #
19
19
  # This ensures that the string is not empty
20
20
  # and pre/post spaces are removed.
21
- def get prompt
21
+ #
22
+ # If `allow_empty` is true, allows the user to
23
+ # type an empty input.
24
+ #
25
+ def get(prompt, allow_empty=false)
22
26
  print "#{prompt} "
23
27
 
24
28
  ret = ""
25
- ret = $stdin.gets.lstrip.strip while ret.empty?
29
+
30
+ if allow_empty
31
+ ret = $stdin.gets.lstrip.strip
32
+ else
33
+ ret = $stdin.gets.lstrip.strip while ret.empty?
34
+ end
26
35
  ret
27
36
  end
28
37
 
@@ -1,18 +1,33 @@
1
1
 
2
2
  module Quickpress
3
+
4
+ # Hash that holds global program's settings.
5
+ #
6
+ # There should be only one instalce of it.
7
+ #
3
8
  class Options
4
9
  def initialize
5
- @options = {}
6
- @options[:force] = false
7
- @options[:markup] = 'markdown'
10
+ @values = {}
8
11
  end
9
12
 
10
13
  def [] label
11
- @options[label]
14
+ @values[label]
12
15
  end
13
16
 
14
17
  def []=(label, val)
15
- @options[label] = val
18
+ @values[label] = val
19
+ end
20
+
21
+ # To add settings saved on other hash.
22
+ #
23
+ # @note I don't use Hash#merge because Thor's
24
+ # argument list creates a Hash with darn
25
+ # Strings as keys.
26
+ # I want symbols, dammit!
27
+ def merge! other_hash
28
+ other_hash.each do |key, val|
29
+ @values[key.to_sym] = val
30
+ end
16
31
  end
17
32
  end
18
33
 
@@ -1,5 +1,5 @@
1
1
  module Quickpress
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  VERSION_MAJOR = VERSION.split('.')[0]
4
4
  VERSION_MINOR = VERSION.split('.')[1]
5
5
  VERSION_PATCH = VERSION.split('.')[2]
@@ -17,6 +17,9 @@ module Quickpress
17
17
  # All categories on blog
18
18
  attr_reader :categories
19
19
 
20
+ # Blog's options in a Hash. Need to call `get_options` first.
21
+ attr_reader :options
22
+
20
23
  # Yes it is
21
24
  VERY_LARGE_NUMBER = 2**31 - 1
22
25
 
@@ -44,15 +47,17 @@ module Quickpress
44
47
  :username => user,
45
48
  :password => pass)
46
49
 
47
- # Actually connecting
48
- options = @client.getOptions
50
+ # Actually connecting, takes a while
51
+ options = @client.getOptions(:options => ["blog_title",
52
+ "blog_tagline",
53
+ "blog_url"])
49
54
 
50
55
  @title = options["blog_title"]["value"]
51
56
  @tagline = options["blog_tagline"]["value"]
52
57
  @url = options["blog_url"]["value"]
53
58
 
54
59
  @categories = []
55
- terms = @client.getTerms(:taxonomy => 'category')
60
+ terms = @client.getTerms(:taxonomy => "category")
56
61
  terms.each do |term|
57
62
  @categories << term["name"]
58
63
  end
@@ -61,15 +66,71 @@ module Quickpress
61
66
  # Sends a post/page to the Wordpress site.
62
67
  def post options
63
68
  id = @client.newPost(:content => options)
64
- link = @client.getPost(:post_id => id, :fields => [:link])["link"]
69
+
70
+ info = @client.getPost(:post_id => id,
71
+ :fields => [:link])
72
+ link = info["link"]
65
73
 
66
74
  return id, link
67
75
  end
68
76
 
77
+ # Returns post with numerical `id`.
78
+ # It's a Hash with attributes/values.
79
+ #
69
80
  def get_post id
70
81
  @client.getPost(:post_id => id)
71
82
  end
72
83
 
84
+ # Edits post with numerical `id` to have `new_content`.
85
+ #
86
+ # If any of the arguments is nil, will keep their
87
+ # old values.
88
+ #
89
+ def edit_post(id, new_content="", new_title="", new_categories=[])
90
+ old_post = get_post id
91
+
92
+ new_content = old_post["post_content"] if new_content.empty?
93
+ new_title = old_post["post_title"] if new_title.empty?
94
+
95
+ if new_categories == []
96
+
97
+ # Filtering out terms that are not categories
98
+ terms = old_post["terms"].select { |t| t["taxonomy"] == "category" }
99
+
100
+ # Getting category names
101
+ cats = terms.map { |c| c["name"] }
102
+ new_categories = cats
103
+ end
104
+
105
+ @client.editPost(:post_id => id,
106
+ :content => {
107
+ :post_content => new_content,
108
+ :post_title => new_title,
109
+ :terms_names => {
110
+ :category => new_categories
111
+ }
112
+ })
113
+ old_post["link"]
114
+ end
115
+
116
+ def edit_page(id, new_content="", new_title="")
117
+ old_page = get_page id
118
+
119
+ new_content = old_page["post_content"] if new_content.empty?
120
+ new_title = old_page["post_title"] if new_title.empty?
121
+
122
+ @client.editPost(:post_id => id,
123
+ :filter => {
124
+ :post_type => 'page'
125
+ },
126
+ :content => {
127
+ :post_content => new_content,
128
+ :post_title => new_title
129
+ })
130
+ old_page["link"]
131
+ end
132
+
133
+ # Returns page with numerical `id`.
73
134
  def get_page id
74
135
  @client.getPost(:post_id => id,
75
136
  :filter => {
@@ -98,10 +159,12 @@ module Quickpress
98
159
  })
99
160
  end
100
161
 
162
+ # Deletes post with numerical `id`.
101
163
  def delete_post id
102
164
  @client.deletePost(:post_id => id)
103
165
  end
104
166
 
167
+ # Deletes page with numerical `id`.
105
168
  def delete_page id
106
169
  @client.deletePost(:post_id => id,
107
170
  :filter => {
@@ -109,6 +172,83 @@ module Quickpress
109
172
  })
110
173
  end
111
174
 
175
+ # Retrieves as much metadata about the blog as it can.
176
+ #
177
+ # Returns an array of 3-element arrays:
178
+ #
179
+ # 1. Wordpress' internal option name.
180
+ # You must use it to set options. See `set_options`.
181
+ # 2. Human-readable description of the option.
182
+ # 3. Current value.
183
+ #
184
+ # The values are detailed here:
185
+ # http://codex.wordpress.org/Option_Reference
186
+ #
187
+ def get_options
188
+ options = @client.getOptions
189
+
190
+ options.map { |o| [o[0], o[1]["desc"], o[1]["value"]] }
191
+ end
192
+
193
+ # Sets the blog's options according to `new_options`
194
+ # hash.
195
+ # It points to an array with two elements:
196
+ #
197
+ # 1. Wordpress' internal option name.
198
+ # See `get_options`.
199
+ # 2. It's new value.
200
+ # See link on `get_options` for possible values.
201
+ #
202
+ # Returns the new options, the same way as `get_options`.
203
+ #
204
+ def set_options new_options
205
+ options = @client.setOptions
206
+
207
+ options.map { |o| [o[0], o[1]["desc"], o[1]["value"]] }
208
+
209
+ end
210
+
211
+ # Returns all users currently registered on the blog.
212
+ #
213
+ # It's an Array of two-element Arrays:
214
+ #
215
+ # 1. Wordpress' internal info name
216
+ # 2. It's value
217
+ def get_users
218
+ users = @client.getUsers
219
+
220
+ # Replacing XML-RPC's ugly DateTime class
221
+ # with Ruby's Time
222
+ users.each do |u|
223
+ u["registered"] = u["registered"].to_time
224
+ end
225
+
226
+ users.map { |u| u.to_a }
227
+ end
228
+
229
+ # Returns comment counts according to their status.
230
+ # It's an Array of two elements:
231
+ #
232
+ # 1. Wordpress' internal status name
233
+ # 2. Comment counts on that status
234
+ #
235
+ def get_comment_status
236
+ status = @client.getCommentCount
237
+
238
+ status.to_a
239
+ end
240
+
241
+ # Returns categories and how many posts they have.
242
+ # It's an Array of two elements:
243
+ #
244
+ # 1. Category name
245
+ # 2. Post count
246
+ #
247
+ def get_category_status
248
+ status = @client.getTerms(:taxonomy => 'category')
249
+
250
+ status.map { |s| [s["name"], s["count"]] } # all we need
251
+ end
112
252
  end
113
253
  end
114
254