icalPal 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a530f29b36a30e180b8bf2a82c3107a2bfa40a12fc62e1fd5431696c9fc860a0
4
+ data.tar.gz: 95caaf61238e643ef328dd5dfafaa00b4a14c50e4565ca5498ddf660e24245f3
5
+ SHA512:
6
+ metadata.gz: 8624c9eeea269874b876205cb556cff492bcd39d9196a428a1f95a3995ca997545ded47749841c8e113a6c4862405d85f053e99f9fc03ddaa877a3b543189eb2
7
+ data.tar.gz: 8870801cc536557e2cb8c4eb1e6a7831ae0c73ced12ff41be3a4a0e19ea482a934b514b9eee6228e09d2b82e2bdc7b51db7c7e1707c3fd600a6bc81bb5054bdd
data/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # icalPal
2
+
3
+ ## Description
4
+
5
+ icalPal is a command-line tool to query a macOS Calendar database for
6
+ accounts, calendars, and events. It can be run on any system with
7
+ [Ruby](https://www.ruby-lang.org/) and access to a Calendar database
8
+ file.
9
+
10
+ ## Installation
11
+
12
+ <code>gem install icalPal</code>
13
+
14
+
15
+ ## Usage
16
+
17
+ ical: Usage: ical [options] [-c] COMMAND
18
+
19
+ COMMAND must be one of the following:
20
+
21
+ events Print events
22
+ calendars Print calendars
23
+ accounts Print accounts
24
+
25
+ eventsToday Print events occurring today
26
+ eventsToday+NUM Print events occurring between today and NUM days into the future
27
+ eventsNow Print events occurring at present time
28
+
29
+ Global options:
30
+
31
+ -c, --cmd=COMMAND Command to run
32
+ --db=DB Use DB file instead of Calendar
33
+ --cf=FILE Set config file path (default: $HOME/.icalPal)
34
+ -o, --output=FORMAT Print as FORMAT (default: default)
35
+ [ansi, csv, default, hash, html, json, md, rdoc, toc, yaml]
36
+
37
+ Including/excluding calendars:
38
+
39
+ --is=ACCOUNTS List of accounts to include
40
+ --es=ACCOUNTS List of accounts to exclude
41
+
42
+ --it=TYPES List of calendar types to include
43
+ --et=TYPES List of calendar types to exclude
44
+ [Local, Exchange, CalDAV, MobileMe, Subscribed, Birthdays]
45
+
46
+ --ic=CALENDARS List of calendars to include
47
+ --ec=CALENDARS List of calendars to exclude
48
+
49
+ Choosing dates:
50
+
51
+ --from=DATE List events starting on or after DATE
52
+ --to=DATE List events starting on or before DATE
53
+ DATE can be yesterday, today, tomorrow, +N, -N, or anything accepted by DateTime.parse()
54
+ See https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-c-parse
55
+
56
+ -n Include only events from now on
57
+ --days=N Show N days of events, including start date
58
+ --sed Show empty dates with --sd
59
+ --ia Include only all-day events
60
+ --ea Exclude all-day events
61
+
62
+ Choose properties to include in the output:
63
+
64
+ --iep=PROPERTIES List of properties to include
65
+ --eep=PROPERTIES List of properties to exclude
66
+ --aep=PROPERTIES List of properties to include in addition to the default list
67
+
68
+ --uid Show event UIDs
69
+ --eed Exclude end datetimes
70
+
71
+ --nc No calendar names
72
+ --npn No property names
73
+ --nrd No relative dates
74
+
75
+ Properties are listed in the order specified
76
+
77
+ Use 'all' for PROPERTIES to include all available properties (except any listed in --eep)
78
+ Use 'list' for PROPERTIES to list all available properties and exit
79
+
80
+ Formatting the output:
81
+
82
+ --li=N Show at most N items (default: 0 for no limit)
83
+
84
+ --sc Separate by calendar
85
+ --sd Separate by date
86
+ --sep=PROPERTY Separate by PROPERTY
87
+
88
+ --sort=PROPERTY Sort by PROPERTY
89
+ -r, --reverse Sort in reverse
90
+
91
+ --ps=SEPARATORS List of property separators
92
+ --ss=SEPARATOR Set section separator
93
+
94
+ --df=FORMAT Set date format
95
+ --tf=FORMAT Set time format
96
+ See https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-i-strftime for details
97
+
98
+ -b, --ab=STRING Use STRING for bullets
99
+ --nnr=SEPARATOR Set replacement for newlines within notes
100
+
101
+ -f Format output using standard ANSI colors
102
+ --color Format output using a larger color palette
103
+
104
+ Help:
105
+
106
+ -h, --help Show this message
107
+ -V, -v, --version Show version and exit (1.0)
108
+ -d, --debug=LEVEL Set the logging level (default: warn)
109
+ [debug, info, warn, error, fatal]
110
+
111
+ Environment variables:
112
+
113
+ ICALPAL Additional arguments
114
+ ICALPAL_CONFIG Additional arguments from a file
115
+ (default: $HOME/.icalPal)
116
+
117
+
118
+ ## History
119
+
120
+ If you've found this page it's likely you've heard of [icalBuddy](https://github.com/ali-rantakari/icalBuddy):
121
+
122
+ > Command-line utility for printing events and tasks from the OS X calendar database.
123
+
124
+ I have used icalBuddy for many years. It's great for scripting,
125
+ automation, and as a desktop widget for apps like
126
+ [GeekTool](https://www.tynsoe.org/geektool/) and
127
+ [Übersicht](https://tracesof.net/uebersicht/).
128
+
129
+ As with many applications, I started to run into some limitations in
130
+ icalBuddy. The biggest being that active development ended over 8
131
+ years ago. It's only thanks to the efforts of [Jim
132
+ Lawton](https://github.com/jimlawton) that it even compiles anymore.
133
+
134
+ Instead of trying to understand and extend the existing code, I chose
135
+ to start anew using my language of choice.
136
+
137
+ - Output in CSV, JSON, HTML, Markdown, and [more](#label-Output+formats)
138
+ - Enhanced color option[#label-Usage]
139
+ - Show and filter by Account
140
+ - Show and filter by Calendar type
141
+ - Select a different Calendar database
142
+ - Multi-platform
143
+ - Much less code (1200 lines vs. 7000)
144
+
145
+ I won't pretend to understand **why** you would want this on Linux or
146
+ Windows. But since icalPal is written in Ruby and gets its data
147
+ directly from the Calendar database file instead of an API, you *can*.
148
+
149
+ ## Output formats
150
+
151
+ icalPal supports several output formats. The +default+ format tries
152
+ to mimic icalBuddy as much as possible.
153
+
154
+ CSV, Hash, JSON, and YAML print all fields for all items in their
155
+ respective formats. From that you can analyze the results any way you like.
156
+
157
+ All other formats, ANSI, HTML, Markdown, RDoc, and TOC, use Ruby's
158
+ [RDoc::Markup](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup.html)
159
+ framework to build and render the items.
160
+
161
+ Each item to be printed is a new
162
+ [RDoc::Markup::Document](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Document.html).
163
+
164
+ When using one of the <em>separate by</em> options, a section header is added first. The section contains:
165
+
166
+ * [RDoc::Markup::BlankLine](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/BlankLine.html)
167
+ (unless this is the first section)
168
+ * RDoc::Markup::Heading (level 1)
169
+ * [RDoc::Markup::Rule](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Rule.html)
170
+
171
+ The rest of the document is a series of
172
+ [RDoc::Markup::List](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/List.html)
173
+ objects, one for each of the item's properties:
174
+
175
+ * [RDoc::Markup::List](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/List.html)
176
+ * RDoc::Markup::Heading (level 2)
177
+ * [RDoc::Markup::BlankLine](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/BlankLine.html)
178
+ * [RDoc::Markup::ListItem](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/ListItem.html)
179
+ * [RDoc::Markup::Paragraph](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Paragraph.html)
180
+
181
+ The document will also include a number of
182
+ [RDoc::Markup::Verbatim](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Verbatim.html)
183
+ items. The are not included in the output, but are used to pass
184
+ information about the item and property to the default formatter.
data/bin/icalPal ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env ruby
2
+ # *-*- mode: enh-ruby -*-*
3
+
4
+ require 'logger'
5
+
6
+ require 'csv'
7
+ require 'json'
8
+ require 'rdoc'
9
+ require 'sqlite3'
10
+ require 'yaml'
11
+
12
+ require_relative '../lib/icalPal'
13
+ require_relative '../lib/options'
14
+
15
+
16
+ ##################################################
17
+ # Load options
18
+
19
+ # All kids love log!
20
+ $log = Logger.new(STDERR, { datetime_format: '%H:%M:%S.%L', level: $defaults[:common][:debug] })
21
+ $log.formatter = proc do |s, t, p, m| # Severity, time, progname, msg
22
+ ($log.level.positive?)? "#{s}: #{m}\n" :
23
+ "[%-5s] %s [%s] - %s\n" %
24
+ [ s, t.strftime($log.datetime_format), caller(4, 1)[0].split('/')[-1], m ]
25
+ end
26
+
27
+ $opts = ICalPal::Options.new.parse_options
28
+
29
+ $rows = [] # Rows from the database
30
+ $items = [] # Items to be printed
31
+
32
+
33
+ ##################################################
34
+ # All kids love log!
35
+
36
+ $log.info("Options: #{$opts}")
37
+
38
+
39
+ ##################################################
40
+ # Add an item to the list
41
+ #
42
+ # @param item[Object]
43
+ def add(item)
44
+ $log.debug("Adding #{item['UUID']} (#{item['title']})") if item['UUID']
45
+ item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if item['sdate']
46
+ $items.push(item)
47
+ end
48
+
49
+
50
+ ##################################################
51
+ # Load the data
52
+
53
+ # What are we getting?
54
+ klass = ICalPal::($opts[:cmd])
55
+ q = klass::QUERY
56
+
57
+ $log.debug(q.gsub(/\n/, ' '))
58
+
59
+ # Get it
60
+ stmt = $db.prepare(q)
61
+ abort(stmt.columns.sort.join(' ')) if $opts[:props].any? 'list'
62
+ $opts[:props] = stmt.columns - $opts[:eep] if $opts[:props].any? 'all'
63
+
64
+ # Iterate the SQLite3::ResultSet once
65
+ stmt.execute.each_with_index { |i, j| $rows[j] = i }
66
+ stmt.close
67
+
68
+ $log.info("Loaded #{$rows.count} rows from #{$db.filename}")
69
+ $db.close
70
+
71
+
72
+ ##################################################
73
+ # Process the data
74
+
75
+ # Add rows
76
+ $rows.each do |row|
77
+ # --es/--is
78
+ next if $opts[:es].any? row['account']
79
+ next unless $opts[:is].empty? or $opts[:is].any? row['account']
80
+
81
+ # --ec/--ic
82
+ next if $opts[:ec].any? row['calendar']
83
+ next unless $opts[:ic].empty? or $opts[:ic].any? row['calendar']
84
+
85
+ item = klass.new(row)
86
+
87
+ # --et/--it
88
+ next if $opts[:et].any? item['type']
89
+ next unless $opts[:it].empty? or $opts[:it].any? item['type']
90
+
91
+ unless ICalPal::Event === item
92
+ # Always add non-event items
93
+ add(item)
94
+ else
95
+ # Check for all-day and cancelled events
96
+ next if $opts[:ea] && item['all_day'].positive?
97
+ next if $opts[:ia] && !item['all_day'].positive?
98
+ next if item['status'] == :canceled
99
+
100
+ (item['has_recurrences'].positive?)?
101
+ item.recurring.each { |i| add(i) } :
102
+ item.non_recurring.each { |i| add(i) }
103
+ end
104
+ end
105
+
106
+ # Add placeholders for empty days
107
+ if $opts[:sed] && $opts[:sd] && klass == ICalPal::Event
108
+ days = $items.collect { |i| i['sday'] }.uniq.sort
109
+
110
+ $opts[:days].times do |n|
111
+ day = $opts[:from] + n
112
+ $items.push(klass.new(day)) unless days.any? { |i| i == day }
113
+ end
114
+ end
115
+
116
+ # Sort the rows
117
+ begin
118
+ $items.sort_by! { |i| [ i[$opts[:sep]], i[$opts[:sort]], i['sdate'] ] }
119
+ $items.reverse! if $opts[:reverse]
120
+ rescue ArgumentError => e
121
+ $log.warn("Sorting failed, results may be unexpected\n")
122
+ end
123
+
124
+ # Configure formatting
125
+ mu = case $opts[:output]
126
+ when 'ansi' then RDoc::Markup::ToAnsi.new
127
+ when 'csv' then proc { |f| f.respond_to?(:gsub)? f.gsub(/\n/, '\n') : f }
128
+ when 'default' then RDoc::Markup::ToICalPal.new($opts)
129
+ when 'html' then
130
+ rdoc = RDoc::Options.new
131
+ rdoc.pipe = true
132
+ rdoc.output_decoration = false
133
+ RDoc::Markup::ToHtml.new(rdoc)
134
+ when 'md' then RDoc::Markup::ToMarkdown.new
135
+ when 'rdoc' then RDoc::Markup::ToRdoc.new
136
+ when 'toc' then RDoc::Markup::ToTableOfContents.new
137
+ end
138
+
139
+
140
+ ##################################################
141
+ # Print the data
142
+
143
+ section = nil if $opts[:sep]
144
+
145
+ $items.each_with_index do |i, j|
146
+ # --li
147
+ break if $opts[:li].positive? && j >= $opts[:li]
148
+
149
+ case $opts[:output]
150
+ when 'csv' then
151
+ values = i.values.map { |v| v.to_s }
152
+ puts CSV.generate(write_converters: mu) { |k| k << i.keys } unless j.positive?
153
+ puts CSV.generate(write_converters: mu) { |k| k << values }
154
+ when 'hash' then p i.self
155
+ when 'json' then puts i.self.to_json
156
+ when 'yaml' then puts i.self.to_yaml
157
+ else
158
+ doc = RDoc::Markup::Document.new
159
+
160
+ # Sections
161
+ if $opts[:sep] && section != i[$opts[:sep]]
162
+ $log.debug("Section: #{i[$opts[:sep]]}")
163
+
164
+ v = RDoc::Markup::Verbatim.new
165
+ v.format = { item: i, prop: $opts[:sep] }
166
+ doc << v
167
+
168
+ doc << RDoc::Markup::BlankLine.new if j.positive?
169
+ doc << RDoc::Markup::Heading.new(1, i[$opts[:sep]].to_s)
170
+ doc << RDoc::Markup::Rule.new(0)
171
+
172
+ section = i[$opts[:sep]]
173
+ end
174
+
175
+ # Item
176
+ props = RDoc::Markup::List.new(:BULLET)
177
+
178
+ # Properties
179
+ $opts[:props].each_with_index do |prop, k|
180
+ next unless i[prop]
181
+ next if Array === i[prop] && !i[prop][0]
182
+
183
+ $log.debug("#{prop}: #{i[prop]}")
184
+
185
+ v = RDoc::Markup::Verbatim.new
186
+ v.format = { item: i, prop: prop }
187
+ props << v
188
+
189
+ unless k.positive?
190
+ # First property, value only
191
+ props << RDoc::Markup::Heading.new(2, i[prop].to_s)
192
+ else
193
+ props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
194
+ props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(i[prop]))
195
+ end
196
+ end
197
+
198
+ # Print it
199
+ unless props.empty?
200
+ doc << props
201
+ puts doc.accept(mu)
202
+ end
203
+ end
204
+ end
data/icalPal.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "icalPal"
3
+ s.version = "1.0.0"
4
+ s.summary = "Command-line tool to query the macOS Calendar"
5
+ s.description = <<-EOF
6
+ Inspired by icalBuddy and maintains close compatability. Includes
7
+ many additional features for querying, filtering, and formatting.
8
+ EOF
9
+
10
+ s.authors = "Andy Rosen"
11
+ s.email = "ajr@corp.mlfs.org"
12
+ s.homepage = "https://github.com/ajrosen/#{s.name}"
13
+ s.licenses = [ "GPL-3.0+" ]
14
+
15
+ s.files = Dir[ "#{s.name}.gemspec", "bin/*", "lib/*.rb" ]
16
+ s.executables = [ "#{s.name}" ]
17
+ s.extra_rdoc_files = [ "README.md" ]
18
+
19
+ s.bindir = 'bin'
20
+ s.required_ruby_version = '>= 2.6.0'
21
+ end
data/lib/EventKit.rb ADDED
@@ -0,0 +1,44 @@
1
+ # Constants from EventKit[https://developer.apple.com/documentation/eventkit/]
2
+ class EventKit
3
+ EKEntityType = [
4
+ 'event',
5
+ 'reminder',
6
+ ]
7
+
8
+ EKEventAvailability = {
9
+ notSupported: -1,
10
+ busy: 0,
11
+ free: 1,
12
+ tentative: 2,
13
+ unavailable: 3,
14
+ }
15
+
16
+ EKEventStatus = {
17
+ none: 0,
18
+ confirmed: 1,
19
+ tentative: 2,
20
+ canceled: 3,
21
+ }
22
+
23
+ EKRecurrenceFrequency = [
24
+ 'daily',
25
+ 'weekly',
26
+ 'monthly',
27
+ 'yearly',
28
+ ]
29
+
30
+ # EKSourceType (with color)
31
+ EKSourceType = [
32
+ { name: 'Local', color: '#FFFFFF' }, # White
33
+ { name: 'Exchange', color: '#00FFFF' }, # Cyan
34
+ { name: 'CalDAV', color: '#00FF00' }, # Green
35
+ { name: 'MobileMe', color: '#FFFF00' }, # Yellow
36
+ { name: 'Subscribed', color: '#FF0000' }, # Red
37
+ { name: 'Birthdays', color: '#FF00FF' }, # Magenta
38
+ ]
39
+
40
+ EKSpan = [
41
+ 'this',
42
+ 'future',
43
+ ]
44
+ end
data/lib/ToICalPal.rb ADDED
@@ -0,0 +1,177 @@
1
+ ##################################################
2
+ # Render an RDoc::Markup::Document, closely mimicking
3
+ # icalBuddy[https://github.com/ali-rantakari/icalBuddy]
4
+
5
+ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
6
+ # Standard
7
+ # ANSI[https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.416-199303-I!!PDF-E&type=items]
8
+ # colors
9
+ ANSI = {
10
+ 'black': 30, '#000000': '38;5;0',
11
+ 'red': 31, '#ff0000': '38;5;1',
12
+ 'green': 32, '#00ff00': '38;5;2',
13
+ 'yellow': 33, '#ffff00': '38;5;3',
14
+ 'blue': 34, '#0000ff': '38;5;4',
15
+ 'magenta': 35, '#ff00ff': '38;5;5',
16
+ 'cyan': 36, '#00ffff': '38;5;6',
17
+ 'white': 37, '#ffffff': '38;5;255',
18
+ 'default': 39, 'custom': nil,
19
+ }
20
+
21
+ # Increased intensity
22
+ BOLD = '%c[1m' % 27.chr
23
+
24
+ # Default rendition
25
+ NORM = '%c[0m' % 27.chr
26
+
27
+ # Properties for which we don't include labels
28
+ NO_LABEL = [ 'title', 'datetime' ]
29
+
30
+ # Properties that are always colorized
31
+ COLOR_LABEL = [ 'title', 'calendar' ]
32
+
33
+ # Default color for labels
34
+ LABEL_COLOR = [ 'cyan', '#00ffff' ]
35
+
36
+ # Color for datetime value
37
+ DATE_COLOR = [ 'yellow', '#ffff00' ]
38
+
39
+ # @param opts [Hash] Used for conditional formatting
40
+ # @option opts [String] :bullet Bullet
41
+ # @option opts [Boolean] :nc No calendar names
42
+ # @option opts [Boolean] :npn No property names
43
+ # @option opts [Integer] :palette (nil) 8 for \-f, 24 for \--color
44
+ # @option opts [Array<String>] :ps List of property separators
45
+ # @option opts [String] :ss Section separator
46
+ def initialize(opts)
47
+ @opts = opts
48
+ end
49
+
50
+ # Start a new document
51
+ def start_accepting
52
+ @res = []
53
+ @ps = 0
54
+ end
55
+
56
+ # Close the document
57
+ def end_accepting
58
+ @res.join
59
+ end
60
+
61
+ # Add a bullet for the first property of an item
62
+ #
63
+ # @param arg [Array] Ignored
64
+ def accept_list_start(arg)
65
+ begin
66
+ return if @item['placeholder']
67
+ rescue
68
+ end
69
+
70
+ @res << "#{@opts[:bullet]} "
71
+ end
72
+
73
+ # Add a property name
74
+ #
75
+ # @param arg [RDoc::Markup::ListItem]
76
+ # @option arg [String] .label Contains the property name
77
+ def accept_list_item_start(arg)
78
+ @res << @opts[:ps][@ps] || ' ' unless @item['placeholder']
79
+ @res << colorize(*LABEL_COLOR, arg.label) << ": " unless @opts[:npn] || NO_LABEL.any?(arg.label)
80
+
81
+ @ps += 1 unless @ps == @opts[:ps].count - 1
82
+ end
83
+
84
+ # Add a blank line
85
+ #
86
+ # @param arg [Array] Ignored
87
+ def accept_blank_line(*arg)
88
+ @res << "\n"
89
+ end
90
+
91
+ # Add either a section header or the first property of an item
92
+ #
93
+ # @param h [RDoc::Markup::Heading]
94
+ # @option h [Integer] :level 1 for a section header
95
+ # @option h [Integer] :level 2 for a property name
96
+ # @option h [String] :text The header's text
97
+ def accept_heading(h)
98
+ h.text = colorize(@item['symbolic_color_name'], @item['color'], h.text) if (h.level == 2) || COLOR_LABEL.any?(@prop)
99
+ @res << h.text
100
+
101
+ case h.level
102
+ when 1 then
103
+ @res << ":"
104
+ when 2 then
105
+ if @prop == 'title' && @item['calendar']
106
+ @res << bold(" (#{@item['calendar']})") unless @opts[:nc] || @item['title'] == @item['calendar']
107
+ end
108
+ end
109
+ end
110
+
111
+ # Add the property value
112
+ #
113
+ # @param p [RDoc::Markup::Paragraph]
114
+ # @option p [Array<String>] :parts The property's text
115
+ def accept_paragraph(p)
116
+ t = p.parts.join('; ').gsub(/\n/, "\n ")
117
+ t = colorize(*DATE_COLOR, t) if @prop == 'datetime'
118
+ @res << t
119
+ end
120
+
121
+ # Add a section separator
122
+ #
123
+ # @param weight Ignored
124
+ def accept_rule(weight)
125
+ @res << @opts[:ss]
126
+ accept_blank_line
127
+ end
128
+
129
+ # Don't add anything to the document, just save the item and
130
+ # property name for later
131
+ #
132
+ # @param h [RDoc::Markup::Verbatim]
133
+ # @option h [String] :parts Ignored
134
+ # @option h [{item, prop => ICalPal::Event, String}] :format
135
+ def accept_verbatim(h)
136
+ @item = h.format[:item]
137
+ @prop = h.format[:prop]
138
+ end
139
+
140
+ # @param str [String]
141
+ # @return [String] str with increased intensity[#BOLD]
142
+ def bold(str)
143
+ return str unless @opts[:palette]
144
+ BOLD + str + NORM
145
+ end
146
+
147
+ # @param c8 [String] Color used for \-f
148
+ # @param c24 [String] Color used for \--color
149
+ # @return [String] str in color, depending on opts[#]
150
+ def colorize(c8, c24, str)
151
+ return str unless c8 && c24 && @opts[:palette]
152
+
153
+ case @opts[:palette]
154
+ when 8 then # Default colour table
155
+ c = ANSI[c8.downcase.to_sym]
156
+ c ||= ANSI[c24[0..6].downcase.to_sym]
157
+ c ||= ANSI[:white]
158
+
159
+ when 24 then # Direct colour in RGB space
160
+ rgb = c24[1..].split(/(\h\h)(\h\h)(\h\h)/)
161
+ rgb.map! { |i| i.to_i(16) }
162
+ c = [ 38, 2, rgb[1..] ].join(';')
163
+ end
164
+
165
+ sprintf('%c[%sm%s%c[%sm', 27.chr, c, str, 27.chr, ANSI[:default])
166
+ end
167
+
168
+ # @!visibility private
169
+
170
+ # @param a [Array] Ignored
171
+ def accept_list_end(a)
172
+ end
173
+
174
+ # @param a [Array] Ignored
175
+ def accept_list_item_end(a)
176
+ end
177
+ end
data/lib/calendar.rb ADDED
@@ -0,0 +1,24 @@
1
+ module ICalPal
2
+ # Class representing items from the <tt>Calendar</tt> table
3
+ class Calendar
4
+ include ICalPal
5
+
6
+ QUERY = <<~SQL
7
+ SELECT DISTINCT
8
+
9
+ Store.name AS account,
10
+ Calendar.title AS calendar,
11
+ *
12
+
13
+ FROM #{self.name.split('::').last}
14
+
15
+ JOIN Store ON store_id = Store.rowid
16
+
17
+ WHERE Store.disabled IS NOT 1
18
+ AND Store.display_order IS NOT -1
19
+ AND (Calendar.display_order IS NOT -1 OR external_rep IS NOT NULL)
20
+ AND Calendar.flags IS NOT 519
21
+ SQL
22
+
23
+ end
24
+ end