ipp_quickbase_devkit 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.
Files changed (239) hide show
  1. data/LICENSE +87 -0
  2. data/README.rdoc +112 -0
  3. data/doc/QuickBaseClient.rb.htm +1896 -0
  4. data/doc/ReleaseNotes.txt +43 -0
  5. data/doc/qbc.makeCSVFile.qbc +4 -0
  6. data/doc/qbc.makeCSVFile.rb +4 -0
  7. data/doc/quickbase_adapter.rb.htm +399 -0
  8. data/examples/cookbookfiles/QuickBaseAPICookbook.html +2590 -0
  9. data/examples/cookbookfiles/addChangeRemoveUserRole.rb +21 -0
  10. data/examples/cookbookfiles/addOrEditRecord.rb +10 -0
  11. data/examples/cookbookfiles/application_object.rb +55 -0
  12. data/examples/cookbookfiles/applyRubyFormulas.rb +10 -0
  13. data/examples/cookbookfiles/average.rb +27 -0
  14. data/examples/cookbookfiles/backupApplication.rb +8 -0
  15. data/examples/cookbookfiles/cacheSchemas.rb +53 -0
  16. data/examples/cookbookfiles/calculateRunningTotals.rb +11 -0
  17. data/examples/cookbookfiles/copyrecords.rb +73 -0
  18. data/examples/cookbookfiles/count.rb +26 -0
  19. data/examples/cookbookfiles/createRecordNavigatorHTML.rb +42 -0
  20. data/examples/cookbookfiles/createReportDashboard.rb +35 -0
  21. data/examples/cookbookfiles/createTable.rb +12 -0
  22. data/examples/cookbookfiles/deviation.rb +25 -0
  23. data/examples/cookbookfiles/downloadCookbook.rb +97 -0
  24. data/examples/cookbookfiles/downloadFile.rb +10 -0
  25. data/examples/cookbookfiles/downloadFilesToFolder.rb +81 -0
  26. data/examples/cookbookfiles/downloadToTextFile.rb +64 -0
  27. data/examples/cookbookfiles/dumpSchema.rb +11 -0
  28. data/examples/cookbookfiles/duplicateRecord.rb +8 -0
  29. data/examples/cookbookfiles/dynamicMethods.rb +33 -0
  30. data/examples/cookbookfiles/editRecords.rb +15 -0
  31. data/examples/cookbookfiles/findJohnsLast10Records.rb +17 -0
  32. data/examples/cookbookfiles/findRubyRecords.rb +17 -0
  33. data/examples/cookbookfiles/formatCurrency.rb +24 -0
  34. data/examples/cookbookfiles/formatDate.rb +10 -0
  35. data/examples/cookbookfiles/formatDuration.rb +27 -0
  36. data/examples/cookbookfiles/formatPercent.rb +24 -0
  37. data/examples/cookbookfiles/getAllValuesForFields.rb +18 -0
  38. data/examples/cookbookfiles/getAppDTMInfo.rb +29 -0
  39. data/examples/cookbookfiles/getApplicationVariable.rb +5 -0
  40. data/examples/cookbookfiles/getChildTableDBID.rb +11 -0
  41. data/examples/cookbookfiles/getColumnListForReport.rb +6 -0
  42. data/examples/cookbookfiles/getFieldChoices.rb +13 -0
  43. data/examples/cookbookfiles/getFieldIDs.rb +6 -0
  44. data/examples/cookbookfiles/getFieldNames.rb +6 -0
  45. data/examples/cookbookfiles/getLastModTime.rb +8 -0
  46. data/examples/cookbookfiles/getLastRecModTime.rb +8 -0
  47. data/examples/cookbookfiles/getNumRecords.rb +8 -0
  48. data/examples/cookbookfiles/getNumTables.rb +4 -0
  49. data/examples/cookbookfiles/getRecord.rb +5 -0
  50. data/examples/cookbookfiles/getRecordDisplayURL.rb +13 -0
  51. data/examples/cookbookfiles/getRecordsAddedToday.rb +20 -0
  52. data/examples/cookbookfiles/getRecordsAsJSON.rb +6 -0
  53. data/examples/cookbookfiles/getReportNames.rb +25 -0
  54. data/examples/cookbookfiles/getRoleInfo.rb +48 -0
  55. data/examples/cookbookfiles/getServerStatus.rb +11 -0
  56. data/examples/cookbookfiles/getSortListForReport.rb +6 -0
  57. data/examples/cookbookfiles/getTableIDs.rb +6 -0
  58. data/examples/cookbookfiles/getTableName.rb +8 -0
  59. data/examples/cookbookfiles/getTableNames.rb +25 -0
  60. data/examples/cookbookfiles/getTimeCreated.rb +8 -0
  61. data/examples/cookbookfiles/getTimeInMilliseconds.rb +5 -0
  62. data/examples/cookbookfiles/getUserInfo.rb +26 -0
  63. data/examples/cookbookfiles/getUserRole.rb +15 -0
  64. data/examples/cookbookfiles/intranet.rb +101 -0
  65. data/examples/cookbookfiles/isAverageField.rb +17 -0
  66. data/examples/cookbookfiles/isDbidString.rb +8 -0
  67. data/examples/cookbookfiles/isTotalField.rb +16 -0
  68. data/examples/cookbookfiles/iterateDBPages.rb +8 -0
  69. data/examples/cookbookfiles/iterateFilteredRecords.rb +12 -0
  70. data/examples/cookbookfiles/iterateJoinRecords.rb +68 -0
  71. data/examples/cookbookfiles/iterateRecordInfos.rb +8 -0
  72. data/examples/cookbookfiles/iterateRecords.rb +23 -0
  73. data/examples/cookbookfiles/iterateSummaryRecords.rb +13 -0
  74. data/examples/cookbookfiles/iterateUnionRecords.rb +38 -0
  75. data/examples/cookbookfiles/listAccessibleApplications.rb +6 -0
  76. data/examples/cookbookfiles/logRequestAndResponseXML.rb +8 -0
  77. data/examples/cookbookfiles/lookupFieldPropertyByName.rb +62 -0
  78. data/examples/cookbookfiles/lookupFieldTypeByName.rb +10 -0
  79. data/examples/cookbookfiles/makeCSVFile.rb +4 -0
  80. data/examples/cookbookfiles/makeSlideShow.rb +42 -0
  81. data/examples/cookbookfiles/makerecs.rb +64 -0
  82. data/examples/cookbookfiles/max.rb +26 -0
  83. data/examples/cookbookfiles/min.rb +26 -0
  84. data/examples/cookbookfiles/percent.rb +29 -0
  85. data/examples/cookbookfiles/printChildElements.rb +54 -0
  86. data/examples/cookbookfiles/printNewRecords.rb +12 -0
  87. data/examples/cookbookfiles/processRESTRequest.rb +21 -0
  88. data/examples/cookbookfiles/provisionAndInviteNewUser.rb +13 -0
  89. data/examples/cookbookfiles/purgeRecords.rb +15 -0
  90. data/examples/cookbookfiles/quickbase_adapter.rb.htm +397 -0
  91. data/examples/cookbookfiles/quickbase_record_finder.zip +0 -0
  92. data/examples/cookbookfiles/recordAndFieldIterator.rb +24 -0
  93. data/examples/cookbookfiles/runImport.rb +9 -0
  94. data/examples/cookbookfiles/runQuickBaseTwitterConnector.rb +41 -0
  95. data/examples/cookbookfiles/sendToQuickBase.rb +33 -0
  96. data/examples/cookbookfiles/setDBvar.rb +6 -0
  97. data/examples/cookbookfiles/showRequestAndResponseXML.rb +8 -0
  98. data/examples/cookbookfiles/sqlQuery.rb +11 -0
  99. data/examples/cookbookfiles/stopOnError.rb +10 -0
  100. data/examples/cookbookfiles/sum.rb +26 -0
  101. data/examples/cookbookfiles/twitterFromQuickBase.rb +42 -0
  102. data/examples/cookbookfiles/twitterWithQuickBase.rb +36 -0
  103. data/examples/cookbookfiles/uploadCSVData.rb +20 -0
  104. data/examples/cookbookfiles/uploadExcelData.rb +22 -0
  105. data/examples/cookbookfiles/uploadFileEveryHour.rb +18 -0
  106. data/examples/cookbookfiles/uploadFileIntoNewRecord.rb +8 -0
  107. data/examples/cookbookfiles/uploadFilesFromFolder.exe +0 -0
  108. data/examples/cookbookfiles/uploadFilesFromFolder.rb +69 -0
  109. data/examples/cookbookfiles/useCompanyURL.rb +12 -0
  110. data/examples/cookbookfiles/userRoles.rb +49 -0
  111. data/examples/cookbookfiles/watchCommunityForum.rb +5 -0
  112. data/examples/cookbookfiles/wikifyTable.rb +29 -0
  113. data/examples/cookbookfiles/xmlShortcuts.rb +33 -0
  114. data/examples/pmp/app/controllers/application.rb +7 -0
  115. data/examples/pmp/app/controllers/contacts_controller.rb +8 -0
  116. data/examples/pmp/app/controllers/document_library_controller.rb +2 -0
  117. data/examples/pmp/app/controllers/issues_controller.rb +5 -0
  118. data/examples/pmp/app/controllers/projects_controller.rb +22 -0
  119. data/examples/pmp/app/controllers/resources_controller.rb +2 -0
  120. data/examples/pmp/app/controllers/tasks_controller.rb +13 -0
  121. data/examples/pmp/app/controllers/time_cards_controller.rb +5 -0
  122. data/examples/pmp/app/helpers/application_helper.rb +3 -0
  123. data/examples/pmp/app/helpers/contacts_helper.rb +2 -0
  124. data/examples/pmp/app/helpers/document_library_helper.rb +2 -0
  125. data/examples/pmp/app/helpers/issues_helper.rb +2 -0
  126. data/examples/pmp/app/helpers/projects_helper.rb +2 -0
  127. data/examples/pmp/app/helpers/resources_helper.rb +2 -0
  128. data/examples/pmp/app/helpers/tasks_helper.rb +2 -0
  129. data/examples/pmp/app/helpers/time_cards_helper.rb +2 -0
  130. data/examples/pmp/app/models/contacts.rb +26 -0
  131. data/examples/pmp/app/models/document_library.rb +2 -0
  132. data/examples/pmp/app/models/issues.rb +6 -0
  133. data/examples/pmp/app/models/projects.rb +26 -0
  134. data/examples/pmp/app/models/resources.rb +2 -0
  135. data/examples/pmp/app/models/tasks.rb +12 -0
  136. data/examples/pmp/app/models/time_cards.rb +7 -0
  137. data/examples/pmp/app/schemas/contacts.xml +1 -0
  138. data/examples/pmp/app/schemas/document_library.xml +1 -0
  139. data/examples/pmp/app/schemas/issues.xml +1 -0
  140. data/examples/pmp/app/schemas/pmp.xml +1 -0
  141. data/examples/pmp/app/schemas/projects.xml +1 -0
  142. data/examples/pmp/app/schemas/readme.txt +8 -0
  143. data/examples/pmp/app/schemas/resources.xml +1 -0
  144. data/examples/pmp/app/schemas/tasks.xml +1 -0
  145. data/examples/pmp/app/schemas/time_cards.xml +1 -0
  146. data/examples/pmp/app/views/contacts/companies.rhtml +31 -0
  147. data/examples/pmp/app/views/contacts/project_contacts.rhtml +31 -0
  148. data/examples/pmp/app/views/issues/filter_issues.rhtml +26 -0
  149. data/examples/pmp/app/views/layouts/application.rhtml +56 -0
  150. data/examples/pmp/app/views/projects/all_projects.rhtml +33 -0
  151. data/examples/pmp/app/views/projects/home.rhtml +11 -0
  152. data/examples/pmp/app/views/projects/my_open_projects.rhtml +27 -0
  153. data/examples/pmp/app/views/projects/open_projects.rhtml +44 -0
  154. data/examples/pmp/app/views/projects/project_sorted_by_company.rhtml +40 -0
  155. data/examples/pmp/app/views/projects/projects_sorted_by_priority.rhtml +30 -0
  156. data/examples/pmp/app/views/projects/updated_projects.rhtml +0 -0
  157. data/examples/pmp/app/views/tasks/all_tasks.rhtml +27 -0
  158. data/examples/pmp/app/views/tasks/search.rhtml +23 -0
  159. data/examples/pmp/app/views/tasks/search2.rhtml +23 -0
  160. data/examples/pmp/app/views/tasks/search3.rhtml +23 -0
  161. data/examples/pmp/app/views/time_cards/summary.rhtml +38 -0
  162. data/examples/pmp/config/boot.rb +45 -0
  163. data/examples/pmp/config/database.yml +30 -0
  164. data/examples/pmp/config/environment.rb +60 -0
  165. data/examples/pmp/config/environments/development.rb +21 -0
  166. data/examples/pmp/config/environments/production.rb +18 -0
  167. data/examples/pmp/config/environments/test.rb +19 -0
  168. data/examples/pmp/config/routes.rb +23 -0
  169. data/examples/pmp/db/migrate/001_create_projects.rb +10 -0
  170. data/examples/pmp/db/migrate/002_create_tasks.rb +10 -0
  171. data/examples/pmp/db/migrate/003_create_issues.rb +10 -0
  172. data/examples/pmp/db/migrate/004_create_document_libraries.rb +10 -0
  173. data/examples/pmp/db/migrate/005_create_resources.rb +10 -0
  174. data/examples/pmp/db/migrate/006_create_time_cards.rb +10 -0
  175. data/examples/pmp/db/migrate/007_create_contacts.rb +10 -0
  176. data/examples/pmp/public/404.html +30 -0
  177. data/examples/pmp/public/500.html +30 -0
  178. data/examples/pmp/public/app.index.html +277 -0
  179. data/examples/pmp/public/dispatch.cgi +10 -0
  180. data/examples/pmp/public/dispatch.fcgi +24 -0
  181. data/examples/pmp/public/dispatch.rb +10 -0
  182. data/examples/pmp/public/favicon.ico +0 -0
  183. data/examples/pmp/public/images/rails.png +0 -0
  184. data/examples/pmp/public/javascripts/application.js +2 -0
  185. data/examples/pmp/public/javascripts/controls.js +833 -0
  186. data/examples/pmp/public/javascripts/dragdrop.js +942 -0
  187. data/examples/pmp/public/javascripts/effects.js +1088 -0
  188. data/examples/pmp/public/javascripts/prototype.js +2515 -0
  189. data/examples/pmp/public/robots.txt +1 -0
  190. data/examples/pmp/test/fixtures/contacts.yml +5 -0
  191. data/examples/pmp/test/fixtures/document_libraries.yml +5 -0
  192. data/examples/pmp/test/fixtures/issues.yml +5 -0
  193. data/examples/pmp/test/fixtures/projects.yml +5 -0
  194. data/examples/pmp/test/fixtures/resources.yml +5 -0
  195. data/examples/pmp/test/fixtures/tasks.yml +5 -0
  196. data/examples/pmp/test/fixtures/time_cards.yml +5 -0
  197. data/examples/pmp/test/functional/contacts_controller_test.rb +18 -0
  198. data/examples/pmp/test/functional/document_library_controller_test.rb +18 -0
  199. data/examples/pmp/test/functional/issues_controller_test.rb +18 -0
  200. data/examples/pmp/test/functional/projects_controller_test.rb +18 -0
  201. data/examples/pmp/test/functional/resources_controller_test.rb +18 -0
  202. data/examples/pmp/test/functional/tasks_controller_test.rb +18 -0
  203. data/examples/pmp/test/functional/time_cards_controller_test.rb +18 -0
  204. data/examples/pmp/test/test_helper.rb +28 -0
  205. data/examples/pmp/test/unit/contacts_test.rb +10 -0
  206. data/examples/pmp/test/unit/document_library_test.rb +10 -0
  207. data/examples/pmp/test/unit/issues_test.rb +10 -0
  208. data/examples/pmp/test/unit/projects_test.rb +10 -0
  209. data/examples/pmp/test/unit/resources_test.rb +10 -0
  210. data/examples/pmp/test/unit/tasks_test.rb +10 -0
  211. data/examples/pmp/test/unit/time_cards_test.rb +10 -0
  212. data/lib/QuickBaseClient.rb +5054 -0
  213. data/lib/QuickBaseCommandLineClient.rb +401 -0
  214. data/lib/QuickBaseContactsAppBuilder.rb +419 -0
  215. data/lib/QuickBaseEmailer.rb +334 -0
  216. data/lib/QuickBaseEventNotifier.rb +592 -0
  217. data/lib/QuickBaseMisc.rb +96 -0
  218. data/lib/QuickBaseObjects.rb +566 -0
  219. data/lib/QuickBaseRSSGenerator.rb +286 -0
  220. data/lib/QuickBaseTextData.rb +545 -0
  221. data/lib/QuickBaseTwitterConnector.rb +300 -0
  222. data/lib/QuickBaseWebClient.rb +126 -0
  223. data/lib/WorkPlaceClient.rb +45 -0
  224. data/lib/qbc.makeCSVFile.qbc +4 -0
  225. data/lib/qbc.makeCSVFile.rb +17 -0
  226. data/lib/quickbase_adapter.rb +320 -0
  227. data/lib/runFieldEntryDialog.rb +151 -0
  228. data/lib/runOfflineFieldEntryDialog.rb +203 -0
  229. data/rakefile +100 -0
  230. data/test/run_tests.bat +7 -0
  231. data/test/spec_all_tests.rb +13 -0
  232. data/test/spec_smoke_tests.rb +58 -0
  233. data/test/spec_workplace_addrecord_test.rb +46 -0
  234. data/test/spec_workplace_base_test.rb +57 -0
  235. data/test/spec_workplace_editrecord_test.rb +38 -0
  236. data/test/spec_workplace_json_test.rb +38 -0
  237. data/test/spec_workplace_objects_test.rb +39 -0
  238. data/test/spec_workplace_smoke_tests.rb +45 -0
  239. metadata +353 -0
@@ -0,0 +1,334 @@
1
+ #--#####################################################################
2
+ # Copyright (c) 2009 Gareth Lewis and Intuit, Inc.
3
+ #
4
+ # All rights reserved. This program and the accompanying materials
5
+ # are made available under the terms of the Eclipse Public License v1.0
6
+ # which accompanies this distribution, and is available at
7
+ # http://www.opensource.org/licenses/eclipse-1.0.php
8
+ #
9
+ # Contributors:
10
+ # Gareth Lewis - Initial contribution.
11
+ # Intuit Partner Platform.
12
+ #++#####################################################################
13
+
14
+ require "QuickBaseClient"
15
+ require "net/smtp"
16
+
17
+ #require 'tls_smtp'
18
+
19
+ module QuickBase
20
+
21
+ # Simple class to read data from QuickBase and email it using the SMTP client that
22
+ # comes with Ruby. This class should handle sending emails to people in the same domain
23
+ # as you, e.g. when your 'from' and 'to' email addresses all end in '@ourcompany.com'.
24
+ # To send emails outside your domain, it's likely that you will have to override
25
+ # the sendUnauthenticatedEmail() method or the sendAuthenticatedEmail() method
26
+ # and add authentication acceptable to your email server.
27
+ #
28
+ # To send email via GMail's server:
29
+ #
30
+ # * download http://s3.amazonaws.com/drawohara.com.ruby/tls_smtp.rb
31
+ # * comment out the 'require "net/smtp"' line above
32
+ # * uncomment the #require 'tls_smtp' line
33
+ # * use smtp.gmail.com as the mail server
34
+ #
35
+ class Emailer
36
+
37
+ attr_writer :from, :to, :subject, :message
38
+ attr_writer :mailServer, :mailPort, :fromDomain, :authenticationType
39
+ attr_writer :emailUsername, :emailPassword
40
+
41
+ def initialize(username,password, emailUsername=nil, emailPassword=nil)
42
+ @username,@password = username,password
43
+ @emailUsername, @emailPassword=emailUsername, emailPassword
44
+ @emailUsername = @username if @emailUsername.nil?
45
+ @emailPassword = @password if @emailPassword.nil?
46
+ @mailPort = 25
47
+ end
48
+
49
+ def sendBccEmail(from, to, bcc, subject, message, mailServer, mailPort, fromDomain)
50
+ @bcc = bcc.dup
51
+ sendEmail(from, to, subject, message, mailServer, mailPort, fromDomain)
52
+ @bcc = nil
53
+ end
54
+
55
+ def sendEmail(from, to, subject, message, mailServer, mailPort, fromDomain)
56
+
57
+ @from = from if from
58
+ @to = to if to
59
+ @subject = subject if subject
60
+ @message = message if message
61
+ @mailServer = mailServer if mailServer
62
+ @mailPort = mailPort if mailPort
63
+ @fromDomain = fromDomain if fromDomain
64
+
65
+ if validateEmailData
66
+ email = buildEmail(@from,@to,@subject,@message)
67
+ @to = @to + @bcc if @bcc
68
+ if @authenticationType.nil? or @authenticationType == ""
69
+ sendUnauthenticatedEmail(email,@from,@to,@mailServer, @mailPort, @fromDomain)
70
+ else
71
+ sendAuthenticatedEmail(email,
72
+ @from,
73
+ @to,
74
+ @mailServer,
75
+ @mailPort,
76
+ @fromDomain,
77
+ @emailUsername,
78
+ @emailPassword,
79
+ @authenticationType )
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+ def sendUnauthenticatedEmail(email,from,to,mailServer, mailPort, fromDomain)
86
+ begin
87
+ Net::SMTP::start(mailServer,mailPort,fromDomain) { |smtp|
88
+ smtp.send_message(email,from,to)
89
+ }
90
+ rescue StandardError => error
91
+ raise "Error sending email: #{error}"
92
+ end
93
+ end
94
+
95
+ def sendAuthenticatedEmail(email,from,to,mailServer, mailPort, fromDomain,
96
+ emailUsername,emailPassword,authenticationType)
97
+ begin
98
+ Net::SMTP::start(mailServer,mailPort,fromDomain,emailUsername,emailPassword,authenticationType) { |smtp|
99
+ smtp.send_message(email,from,to)
100
+ }
101
+ rescue StandardError => error
102
+ raise "Error sending email: #{error}"
103
+ end
104
+ end
105
+
106
+ def validateEmailData
107
+
108
+ raise "'@from' email address is missing" if @from.nil?
109
+ raise "'@to' email address is missing" if @to.nil?
110
+ @subject = "" if @subject.nil?
111
+ @message = "" if @message.nil?
112
+ raise "'@mailServer' is missing" if @mailServer.nil?
113
+ raise "'@mailPort' is missing" if @mailPort.nil?
114
+
115
+ raise "'@mailServer' must be a String" if not @mailServer.is_a?(String)
116
+
117
+ if @mailServer and @fromDomain.nil?
118
+ @fromDomain = @mailServer
119
+ end
120
+
121
+ raise "'@fromDomain' must be a String" if not @fromDomain.is_a?(String)
122
+
123
+ if not @to.is_a?(Array)
124
+ raise "'@to' must be an Array"
125
+ end
126
+
127
+ @to.each { |toAddress|
128
+ if not toAddress.index('@')
129
+ raise "'#{toAddress}' is not a valid email address"
130
+ end
131
+ }
132
+
133
+ if @bcc
134
+ if not @bcc.is_a?(Array)
135
+ raise "'@bcc' must be an Array"
136
+ end
137
+
138
+ @bcc.each { |toAddress|
139
+ if not toAddress.index('@')
140
+ raise "'#{toAddress}' is not a valid email address"
141
+ end
142
+ }
143
+ end
144
+
145
+ if not @from.is_a?(String)
146
+ raise "'@from' must be an String"
147
+ end
148
+
149
+ if not @from.index('@')
150
+ raise "'@from' is not a valid email address"
151
+ end
152
+
153
+ if not @mailPort.is_a?(Numeric)
154
+ raise "'@mailPort' must be a positive number"
155
+ end
156
+
157
+ raise "'@subject' must be a String" if not @subject.is_a?(String)
158
+ raise "'@message' must be a String" if not @message.is_a?(String)
159
+
160
+ if @authenticationType and @authenticationType != ""
161
+ if not ["login","cram-md5","plain"].include?(@authenticationType)
162
+ raise "'@authenticationType' #{@authenticationType} is invalid."
163
+ end
164
+ end
165
+
166
+ true
167
+ end
168
+
169
+ def buildEmail(from,to,subject,message)
170
+ from.strip!
171
+ email = "From:#{from}\n"
172
+ email << "Date:#{Time.now}\n"
173
+ to.each{|toAddress|
174
+ toAddress.strip!
175
+ email << "To:#{toAddress}\n"
176
+ }
177
+ email << "Subject:#{subject}\n"
178
+ email << "#{message}\n"
179
+ email
180
+ end
181
+
182
+ def readEmailServerConfigFromQuickBase(dbid,fieldNames,fids,query=nil,qname=nil,qid=nil)
183
+ emailConfiguration = nil
184
+ if not fids.is_a?(Hash)
185
+ raise "'fids' must be a Hash of email server configuration field names and their QuickBase field id's"
186
+ end
187
+ if not fieldNames.is_a?(Hash)
188
+ raise "'fieldNames' must be a Hash of email configuration fields and their field names in QuickBase"
189
+ end
190
+ clist = ""
191
+ ["mailServer","mailPort","fromDomain","authenticationType"].each{|fieldName|
192
+ if fids[fieldName]
193
+ clist << fids[fieldName]
194
+ clist << "." unless fieldName == "authenticationType"
195
+ else
196
+ raise "'#{fieldName}' fid entry is missing from the 'fids' Hash"
197
+ end
198
+ if not fieldNames[fieldName]
199
+ raise "'#{fieldName}' QuickBase field name entry is missing from the 'fieldNames' Hash"
200
+ end
201
+ }
202
+ qbc = QuickBase::Client.new(@username,@password)
203
+ if qbc and qbc.requestSucceeded
204
+ qbc.getSchema(dbid)
205
+ if qbc.requestSucceeded
206
+ emailConfiguration = qbc.getAllValuesForFields(dbid,fieldNames.values,query,qname,qid,clist,nil,"structured","num-1")
207
+ emailConfiguration = nil if emailConfiguration.length == 0
208
+ else
209
+ raise "Error accessing QuickBase table '#{dbid}'."
210
+ end
211
+ else
212
+ raise "Error accessing QuickBase. Please check your internet connection, username (#{@username}) and password (#{@password})"
213
+ end
214
+ qbc.signOut if qbc
215
+ emailConfiguration
216
+ end
217
+
218
+ def readEmailsToSendFromQuickBase(dbid,fieldNames,fids,query=nil,qname=nil,qid=nil)
219
+ emailMessages = nil
220
+ if not fids.is_a?(Hash)
221
+ raise "'fids' must be a Hash of email field names and their QuickBase field id's"
222
+ end
223
+ if not fieldNames.is_a?(Hash)
224
+ raise "'fieldNames' must be a Hash of email fields and their field names in QuickBase"
225
+ end
226
+ clist = ""
227
+ ["from","to","subject","message"].each{|fieldName|
228
+ if fids[fieldName]
229
+ clist << fids[fieldName]
230
+ clist << "." unless fieldName == "message"
231
+ else
232
+ raise "'#{fieldName}' fid entry is missing from the 'fids' Hash"
233
+ end
234
+ if not fieldNames[fieldName]
235
+ raise "'#{fieldName}' QuickBase field name entry is missing from the 'fieldNames' Hash"
236
+ end
237
+ }
238
+ qbc = QuickBase::Client.new(@username,@password)
239
+ if qbc and qbc.requestSucceeded
240
+ qbc.getSchema(dbid)
241
+ if qbc.requestSucceeded
242
+ emailMessages = qbc.getAllValuesForFields(dbid,fieldNames.values,query,qname,qid,clist)
243
+ emailMessages = nil if emailMessages.length == 0
244
+ else
245
+ raise "Error accessing QuickBase table '#{dbid}'."
246
+ end
247
+ else
248
+ raise "Error accessing QuickBase. Please check your internet connection, username and password"
249
+ end
250
+ qbc.signOut if qbc
251
+ emailMessages
252
+ end
253
+
254
+ def sendEmailMessages(emailMessages, emailConfiguration)
255
+ if emailMessages and emailConfiguration
256
+ raise "'emailMessages' must be a Hash" if not emailMessages.is_a?(Hash)
257
+ raise "'emailConfiguration' must be a Hash" if not emailConfiguration.is_a?(Hash)
258
+ (0..(emailMessages["from"].length-1)).each{|i|
259
+ begin
260
+
261
+ from = emailMessages["from"][i]
262
+ toAddresses = emailMessages["to"][i].split(/\<BR\/\>/)
263
+ subject = emailMessages["subject"][i]
264
+ subject.gsub!("<BR/>","")
265
+ message = emailMessages["message"][i]
266
+ message.gsub!("<BR/>","")
267
+
268
+ sendEmail(from,
269
+ toAddresses,
270
+ subject,
271
+ message,
272
+ emailConfiguration["mailServer"][0],
273
+ emailConfiguration["mailPort"][0].to_i,
274
+ emailConfiguration["fromDomain"][0])
275
+
276
+ puts "Sent '#{subject}' email."
277
+
278
+ rescue StandardError => error
279
+ puts error
280
+ end
281
+ }
282
+ end
283
+ end
284
+
285
+ def Emailer.sendEmailsFromQuickBase(username,password,configDBID,emailsDBID)
286
+ emailer = Emailer.new(username,password)
287
+
288
+ configFieldNames = Hash.new
289
+ configFieldNames["mailServer"]="mailServer"
290
+ configFieldNames["mailPort"]="mailPort"
291
+ configFieldNames["fromDomain"]="fromDomain"
292
+ configFieldNames["authenticationType"]="authenticationType"
293
+
294
+ configFieldIDs = Hash.new
295
+ configFieldIDs["mailServer"]="6"
296
+ configFieldIDs["mailPort"]="7"
297
+ configFieldIDs["fromDomain"]="8"
298
+ configFieldIDs["authenticationType"]="9"
299
+
300
+ emailConfiguration = emailer.readEmailServerConfigFromQuickBase(configDBID,configFieldNames,configFieldIDs)
301
+ if emailConfiguration
302
+ puts "Read an email configuration from '#{configDBID}'."
303
+
304
+ emailFieldNames = Hash.new
305
+ emailFieldNames["from"]="from"
306
+ emailFieldNames["to"]="to"
307
+ emailFieldNames["subject"]="subject"
308
+ emailFieldNames["message"]="message"
309
+
310
+ emailFieldIDs = Hash.new
311
+ emailFieldIDs["from"]="6"
312
+ emailFieldIDs["to"]="7"
313
+ emailFieldIDs["subject"]="8"
314
+ emailFieldIDs["message"]="9"
315
+
316
+ emailMessages = emailer.readEmailsToSendFromQuickBase(emailsDBID,emailFieldNames,emailFieldIDs)
317
+ if emailMessages and emailMessages["from"].length > 0
318
+ numMessages = emailMessages["from"].length
319
+ puts "Read #{numMessages} email messages from '#{emailsDBID}'."
320
+ emailer.sendEmailMessages(emailMessages, emailConfiguration)
321
+ else
322
+ puts "Did not read email messages from '#{emailsDBID}'."
323
+ end
324
+ else
325
+ puts "Did not read an email configuration from '#{configDBID}'."
326
+ end
327
+ end
328
+
329
+ end #class Emailer
330
+
331
+ end #module QuickBase
332
+
333
+ #QuickBase::Emailer.sendEmailsFromQuickBase(ARGV[0],ARGV[1],ARGV[2],ARGV[3]) if ARGV[3]
334
+
@@ -0,0 +1,592 @@
1
+ #--#####################################################################
2
+ # Copyright (c) 2009 Gareth Lewis and Intuit, Inc.
3
+ #
4
+ # All rights reserved. This program and the accompanying materials
5
+ # are made available under the terms of the Eclipse Public License v1.0
6
+ # which accompanies this distribution, and is available at
7
+ # http://www.opensource.org/licenses/eclipse-1.0.php
8
+ #
9
+ # Contributors:
10
+ # Gareth Lewis - Initial contribution.
11
+ # Intuit Partner Platform.
12
+ #++#####################################################################
13
+
14
+ require 'QuickBaseClient'
15
+ require 'tk'
16
+
17
+ module QuickBase
18
+
19
+ # This class maintains a list of QuickBase table changes to watch
20
+ # for, how to be notified of the changes, how frequently to check for the
21
+ # changes and when to start and stop checking. It can stop checking
22
+ # after a specific number of checks, or stop checking after a specific
23
+ # number of successful checks. It can also check for records changes
24
+ # that only meet certain conditions.
25
+ class EventNotifier
26
+
27
+ attr_reader :qbc
28
+ attr_writer :qbc
29
+
30
+ def initialize(qbc=nil, username=nil,password=nil)
31
+ @username,@password = username,password
32
+ if qbc
33
+ @qbc = qbc
34
+ elsif @username and @password
35
+ @qbc = QuickBase::Client.new(@username,@password)
36
+ end
37
+ @eventNotifications = Array.new
38
+ end
39
+
40
+ # In addition to a basic record add/modify/delete events,
41
+ # check these conditions before sending a notification.
42
+ class RecordCondition
43
+
44
+ # fieldName: field to test, e.g. "Date"
45
+ # logicalOperator: kind of test, e.g. "OAF"
46
+ # fieldValue: value to test for, e.g. "12/31/2006"
47
+ def initialize(fieldName,logicalOperator,fieldValue)
48
+ @fieldName,@logicalOperator,@fieldValue=fieldName,logicalOperator,fieldValue
49
+ end
50
+
51
+ def validate(qbc,dbid)
52
+ if qbc.lookupFieldIDByName(@fieldName)
53
+ fieldType = qbc.lookupFieldTypeByName(@fieldName)
54
+ if fieldType
55
+ queryOperator = qbc.verifyQueryOperator(@logicalOperator,fieldType)
56
+ if queryOperator.nil? or queryOperator.length == 0
57
+ raise "Invalid query operator '#{@logicalOperator}' for field '#{@fieldName}' in table #{dbid}"
58
+ else
59
+ if @fieldValue.length > 0 #any field can be blanked out
60
+ case fieldType
61
+ when "checkbox"
62
+ if !(@fieldValue == "1" or @fieldValue == "0")
63
+ raise "Invalid data '#{@fieldValue}' for checkbox field #{@fieldName}"
64
+ end
65
+ when "date"
66
+ fieldValue.gsub!("/","-")
67
+ if !fieldValue.match(/[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9][0-9][0-9]/)
68
+ raise "Invalid data '#{@fieldValue}' for date field #{@fieldName}"
69
+ end
70
+ when "duration", "float", "currency", "rating"
71
+ fieldValue.gsub!(",","")
72
+ if !fieldValue.match(/[0-9]*\.?[0-9]*/)
73
+ raise "Invalid data '#{@fieldValue}' for field #{@fieldName}"
74
+ end
75
+ when "phone"
76
+ if !fieldValue.match(/[0-9|\.|x]+/)
77
+ raise "Invalid data '#{@fieldValue}' for phone field #{@fieldName}"
78
+ end
79
+ when "file"
80
+ if FileTest.exist?(fieldValue)
81
+ @fieldIsValidFileAttachment = true
82
+ else
83
+ raise "Invalid file name '#{@fieldValue}' for file attachment field #{@fieldName}"
84
+ end
85
+ end
86
+ end
87
+ end
88
+ else
89
+ raise "Unable to validate field type for field '#{@fieldName}' in table #{dbid}"
90
+ end
91
+ else
92
+ raise "Unable to validate field '#{@fieldName}' in table #{dbid}"
93
+ end
94
+ true
95
+ end
96
+
97
+ end
98
+
99
+ # A record is added/modified/deleted from a table
100
+ class TableEvent
101
+
102
+ attr_reader :url
103
+
104
+ def initialize(table, eventType=["recordAdded","recordModified"],application=nil,recordCondition=nil)
105
+ if eventType.is_a?(String)
106
+ validateEventType(eventType)
107
+ elsif eventType.is_a?(Array)
108
+ eventType.each{|et| validateEventType(et) }
109
+ end
110
+ @table,@eventType,@application,@recordCondition=table,eventType,application,recordCondition
111
+ @lastModifiedTime = 0
112
+ @lastRecModTime = 0
113
+ @numRecords = 0
114
+ @url = "http://www.quickbase.com"
115
+ end
116
+
117
+ def validateEventType(eventType)
118
+ if @validEventTypes.nil?
119
+ @validEventTypes = Array.new
120
+ @validEventTypes << "recordAdded"
121
+ @validEventTypes << "recordModified"
122
+ @validEventTypes << "recordDeleted"
123
+ end
124
+ if ! (eventType.is_a?(String) and @validEventTypes.include?(eventType))
125
+ raise "Invalid event type '#{eventType}'."
126
+ end
127
+ end
128
+
129
+ def isEventType?(type)
130
+ ret=false
131
+ if @eventType.is_a?(String)
132
+ ret = @eventType == type
133
+ elsif @eventType.is_a?(Array)
134
+ @eventType.each{|et|
135
+ if et == type
136
+ ret = true
137
+ break
138
+ end
139
+ }
140
+ end
141
+ ret
142
+ end
143
+
144
+ def validateApplicationAndTable(qbc)
145
+ if @application and @application.length > 0
146
+ @table = @application if @table.nil?
147
+ qbc.findDBByName(@application)
148
+ if qbc.requestSucceeded
149
+ qbc.lookupChdbid(@table)
150
+ if qbc.requestSucceeded
151
+ @dbid = qbc.dbid.dup
152
+ qbc._getSchema
153
+ @tablename = @table.dup
154
+ end
155
+ end
156
+ elsif @table and @table.length > 0
157
+ qbc.getSchema(@table)
158
+ if qbc.requestSucceeded
159
+ @dbid = qbc.dbid.dup
160
+ @tableName = qbc.getResponseElement( "table/name" ).text
161
+ end
162
+ else
163
+ raise "table and/or application must be specified"
164
+ end
165
+ raise "Error retrieving schema for table #{@table}" if @dbid.nil?
166
+ @url = "https://www.quickbase.com/db/#{@dbid}"
167
+ @dbid
168
+ end
169
+
170
+ def validateRecordCondition(qbc)
171
+ if @recordCondition
172
+ if @recordCondition.is_a?(RecordCondition)
173
+ @recordCondition.validate(qbc,@dbid)
174
+ elsif @recordCondition.is_a?(Array)
175
+ @recordCondition.each{|rc|
176
+ if rc.is_a?(RecordCondition)
177
+ rc.validate(qbc,@dbid)
178
+ else
179
+ raise "recordcondition must be a RecordCondition or Array of RecordConditions"
180
+ end
181
+ }
182
+ else
183
+ raise "recordcondition must be a RecordCondition or Array of RecordConditions"
184
+ end
185
+ end
186
+ true
187
+ end
188
+
189
+ def validate(qbc)
190
+ if validateApplicationAndTable(qbc)
191
+ validateRecordCondition(qbc)
192
+ end
193
+ true
194
+ end
195
+
196
+ def eventOccurred?(qbc)
197
+ query = ""
198
+ ret = false
199
+ qbc.getDBInfo(@dbid)
200
+ if qbc.requestSucceeded
201
+ if @lastModifiedTime > 0 and qbc.lastModifiedTime.to_i > @lastModifiedTime
202
+ if isEventType?("recordModified")
203
+ if qbc.lastRecModTime.to_i > @lastRecModTime
204
+ query = "{'2'.GT.'#{@lastRecModTime}'}"
205
+ end
206
+ end
207
+ if isEventType?("recordAdded")
208
+ if qbc.numRecords.to_i > @numRecords
209
+ if query.length > 0
210
+ query << "OR{'1'.GT.'#{@lastModifiedTime}'}"
211
+ else
212
+ query = "{'1'.GT.'#{@lastModifiedTime}'}"
213
+ end
214
+ end
215
+ end
216
+ if isEventType?("recordDeleted")
217
+ if qbc.numRecords.to_i < @numRecords
218
+ ret = true
219
+ end
220
+ end
221
+ #puts query
222
+ if query.length > 0
223
+ @records = qbc.getAllValuesForFields(@dbid, "Record ID#", query, nil, nil, "3")
224
+ puts "Found #{@records.length} records."
225
+ ret = @records.length > 0
226
+ end
227
+ end
228
+ @numRecords = qbc.numRecords.to_i
229
+ @lastModifiedTime = qbc.lastModifiedTime.to_i
230
+ @lastRecModTime = qbc.lastRecModTime.to_i
231
+ else
232
+ raise "Error getting information for table #{@dbid}"
233
+ end
234
+ ret
235
+ end
236
+
237
+ def tableEventNotificationMessage
238
+ message = "Records have been "
239
+ if isEventType?("recordAdded")
240
+ eventTypeMessage = "added"
241
+ else
242
+ eventTypeMessage = ""
243
+ end
244
+ if isEventType?("recordModified")
245
+ if eventTypeMessage.length > 0
246
+ eventTypeMessage << " or modified"
247
+ else
248
+ eventTypeMessage = "modified"
249
+ end
250
+ end
251
+ if isEventType?("recordDeleted")
252
+ if eventTypeMessage.length > 0
253
+ eventTypeMessage << "or deleted"
254
+ else
255
+ eventTypeMessage = "deleted"
256
+ end
257
+ end
258
+ message << eventTypeMessage
259
+ message << " in the '#{@tableName}' QuickBase table."
260
+ return @tableName,message
261
+ end
262
+
263
+ end
264
+
265
+ # Frequency and start/stop time of checking for TableEvent
266
+ class EventCheckPolicy
267
+
268
+ attr_reader :interval, :starttime, :stoptime, :nextCheckTime, :numChecks, :numSuccessfulChecks
269
+
270
+ # interval: minutes between checks
271
+ # starttime: start checking at this time
272
+ # stoptime: stop checking at this time
273
+ # numChecks: check this number of times then stop
274
+ # numSuccessfulChecks: stop checking after changes found this number of times
275
+ def initialize(interval=nil,starttime=nil,stoptime=nil,numChecks=-1,numSuccessfulChecks=-1 )
276
+ if interval
277
+ if interval.is_a?(Integer) and interval > 0
278
+ @interval = interval * 60
279
+ else
280
+ raise "interval is not a positive number"
281
+ end
282
+ else
283
+ @interval = 1 * 60
284
+ end
285
+ if starttime
286
+ if starttime.is_a?(Time)
287
+ @starttime = starttime
288
+ else
289
+ raise "starttime must be a Time object"
290
+ end
291
+ else
292
+ @starttime = Time.now
293
+ end
294
+ if stoptime and !stoptime.is_a?(Time)
295
+ raise "stoptime must be a Time object"
296
+ end
297
+ @stoptime = stoptime
298
+ if @starttime and @stoptime and @starttime > @stoptime
299
+ raise "starttime must be before stoptime"
300
+ end
301
+ @numSuccessfulChecks = numSuccessfulChecks
302
+ if @numSuccessfulChecks.nil?
303
+ @numSuccessfulChecks = -1
304
+ elsif !@numSuccessfulChecks.is_a?(Integer)
305
+ raise "numSuccessfulChecks must be a number"
306
+ end
307
+ if @numSuccessfulChecks > 0
308
+ @numChecks = -1
309
+ else
310
+ @numChecks = numChecks
311
+ if @numChecks.nil?
312
+ @numChecks = -1
313
+ elsif !@numChecks.is_a?(Integer)
314
+ raise "numChecks must be a number"
315
+ end
316
+ end
317
+ setNextCheckTime(true)
318
+ end
319
+
320
+ def setNextCheckTime(initializing=false,checkSucceeded=false)
321
+ if @nextCheckTime
322
+ @nextCheckTime += @interval
323
+ else
324
+ @nextCheckTime = @starttime + @interval
325
+ end
326
+ if checkSucceeded and @numSuccessfulChecks > 0
327
+ @numSuccessfulChecks = @numSuccessfulChecks - 1
328
+ end
329
+ @stopChecking = true if @numSuccessfulChecks == 0
330
+ if !initializing and @numChecks > 0
331
+ @numChecks = @numChecks - 1
332
+ @stopChecking = true if @numChecks == 0
333
+ end
334
+ if !@stopChecking
335
+ @stopChecking = (@stoptime and @nextCheckTime > @stoptime)
336
+ end
337
+ end
338
+
339
+ def stopChecking?
340
+ @stopChecking
341
+ end
342
+ end
343
+
344
+ # What to do when a TableEvent has occurred
345
+ class Notification
346
+
347
+ attr_reader :beep, :message, :title, :launchBrowser
348
+
349
+ def initialize(beep=true,message="A QuickBase event has occurred.",title="QuickBase Event",launchBrowser=true)
350
+ if beep or message
351
+ @beep = beep
352
+ @message = message
353
+ @message = "" if @message.nil?
354
+ @title = title
355
+ @title = "" if @title.nil?
356
+ @launchBrowser = launchBrowser
357
+ else
358
+ raise "Notification must be a beep and/or a message"
359
+ end
360
+ end
361
+
362
+ def overrideDefaultTitleAndMessage(title,withThis)
363
+ if @message == "A QuickBase event has occurred."
364
+ @message = withThis
365
+ @title = title
366
+ end
367
+ return @title,@message
368
+ end
369
+
370
+ end
371
+
372
+ # Event, notification, and checking policy
373
+ class EventNotification
374
+
375
+ attr_reader :notification
376
+
377
+ def initialize(tableEvent, notification=nil, eventCheckPolicy=nil)
378
+ if tableEvent and tableEvent.is_a?(TableEvent)
379
+ @tableEvent,@notification,@eventCheckPolicy=tableEvent,notification,eventCheckPolicy
380
+ @notification = Notification.new if @notification.nil?
381
+ @eventCheckPolicy = EventCheckPolicy.new if @eventCheckPolicy.nil?
382
+ else
383
+ raise "tableEvent must be TableEvent"
384
+ end
385
+ end
386
+
387
+ def method_missing(method, *args)
388
+ if method == :nextCheckTime
389
+ @eventCheckPolicy.nextCheckTime
390
+ elsif method == :setNextCheckTime
391
+ @eventCheckPolicy.setNextCheckTime(args[0],args[1])
392
+ elsif method == :stopChecking?
393
+ @eventCheckPolicy.stopChecking?
394
+ elsif method == :numChecks
395
+ @eventCheckPolicy.numChecks
396
+ elsif method == :numSuccessfulChecks
397
+ @eventCheckPolicy.numSuccessfulChecks
398
+ elsif method == :eventOccurred?
399
+ @tableEvent.eventOccurred?(@qbc)
400
+ elsif method == :url
401
+ @tableEvent.url
402
+ elsif method == :tableEventNotificationMessage
403
+ @tableEvent.tableEventNotificationMessage
404
+ else
405
+ super.method_missing(method)
406
+ end
407
+ end
408
+
409
+ def validate(qbc)
410
+ if qbc and qbc.is_a?(QuickBase::Client)
411
+ @tableEvent.validate(qbc)
412
+ @qbc = qbc
413
+ else
414
+ raise "qbc must be an instance of QuickBase::Client"
415
+ end
416
+ true
417
+ end
418
+
419
+ end # class EventNotification
420
+
421
+ def addEventNotification(eventNotification)
422
+ @eventNotifications << eventNotification if eventNotification.validate(@qbc)
423
+ end
424
+
425
+ # Call with a block to run code when an event occurs, or
426
+ # call without a block to show a message and/or beep.
427
+ # The loop stops if all event checks are beyond their stop time.
428
+ def startChecking
429
+ startTime = Time.now
430
+ firstLoop = true
431
+ loop {
432
+ timeNow = Time.now
433
+ stopChecking = true
434
+ @eventNotifications.each { |eventNotification|
435
+ unless eventNotification.stopChecking?
436
+ stopChecking = false
437
+ title,message=eventNotification.tableEventNotificationMessage
438
+ if timeNow > eventNotification.nextCheckTime
439
+ puts "Checking for changes in QuickBase. (#{title} - #{timeNow})"
440
+ checkSucceeded = false
441
+ if eventNotification.eventOccurred?
442
+ checkSucceeded = true
443
+ if block_given?
444
+ yield eventNotification
445
+ else
446
+ eventNotification.notification.overrideDefaultTitleAndMessage(title,message)
447
+ notify(eventNotification.notification,eventNotification.url)
448
+ end
449
+ end
450
+ eventNotification.setNextCheckTime(false,checkSucceeded)
451
+ unless eventNotification.stopChecking?
452
+ checkTimeMessage = "Next check time for '#{title}' will be #{eventNotification.nextCheckTime}."
453
+ if eventNotification.numSuccessfulChecks > 0
454
+ checkTimeMessage << " (#{eventNotification.numSuccessfulChecks } more successful checks will be performed)."
455
+ elsif eventNotification.numChecks > 0
456
+ checkTimeMessage << " (#{eventNotification.numChecks} more checks will be performed)."
457
+ end
458
+ puts checkTimeMessage
459
+ end
460
+ elsif firstLoop
461
+ checkTimeMessage = "Next check time for '#{title}' will be #{eventNotification.nextCheckTime}."
462
+ puts checkTimeMessage
463
+ end
464
+ end
465
+ }
466
+ firstLoop = false
467
+ break if stopChecking
468
+ }
469
+ end
470
+
471
+ # Event occurred: beep and/or show a message on separate thread
472
+ def notify(notification,url)
473
+ Tk.bell if notification.beep
474
+ if notification.message
475
+ showMessage(notification.title,notification.message,url,notification.launchBrowser)
476
+ end
477
+ end
478
+
479
+ def showMessage(messageTitle,message,url,launchBrowser)
480
+
481
+ message = "#{message}\n\nLaunch QuickBase?" if url and launchBrowser
482
+
483
+ root = TkRoot.new{ title messageTitle }
484
+ frame = TkFrame.new(root){
485
+ pack "side" => "top"
486
+ borderwidth 8
487
+ }
488
+ messageLabel = TkLabel.new(frame){
489
+ text message
490
+ font "Arial 10 bold"
491
+ pack "side"=>"top"
492
+ }
493
+ if url and launchBrowser
494
+ buttonFrame = TkFrame.new(frame){
495
+ pack "side" => "bottom"
496
+ width 50
497
+ }
498
+ yesButton = TkButton.new(buttonFrame){
499
+ text "Yes"
500
+ font "Arial 10 bold"
501
+ pack "side"=>"left", "padx"=>5, "pady"=>5
502
+ }
503
+ yesButton.command {
504
+ launchURL(url)
505
+ Tk.exit
506
+ }
507
+ noButton = TkButton.new(buttonFrame){
508
+ text "No"
509
+ font "Arial 10 bold"
510
+ pack "side"=>"right","padx"=>5, "pady"=>5
511
+ }
512
+ noButton.command { Tk.exit }
513
+ else
514
+ okButton = TkButton.new(frame){
515
+ text "OK"
516
+ font "Arial 10 bold"
517
+ pack "side"=>"bottom"
518
+ }
519
+ okButton.command { Tk.exit }
520
+ end
521
+ Tk.mainloop
522
+ Tk.restart
523
+ end
524
+
525
+ def launchURL(url)
526
+ url.gsub!("&","^&")
527
+ url = "start #{url}" if RUBY_PLATFORM.split("-")[1].include?("mswin")
528
+ if !system(url)
529
+ message = "Error launching browser at #{url}."
530
+ Tk.messageBox({"icon"=>"error","title"=>"QuickBase Event Notifier", "message" => message})
531
+ end
532
+ end
533
+
534
+ # Simple method to get messages when records are added or modified in a table
535
+ def EventNotifier.watch(username,password,dbid)
536
+ en = EventNotifier.new(nil,username,password)
537
+ notification = EventNotification.new(TableEvent.new(dbid))
538
+ en.addEventNotification(notification)
539
+ en.startChecking
540
+ end
541
+
542
+ # Simple method to run code when records are added or modified in a table
543
+ def EventNotifier.watchAndRunCode(username,password,dbid)
544
+ en = EventNotifier.new(nil,username,password)
545
+ notification = EventNotification.new(TableEvent.new(dbid))
546
+ en.addEventNotification(notification)
547
+ en.startChecking { |eventNotification|
548
+ yield eventNotification
549
+ }
550
+ end
551
+
552
+ # Simple method to check one time only, 15 minutes from now, then stop
553
+ def EventNotifier.checkOnce(username,password,dbid)
554
+ en = EventNotifier.new(nil,username,password)
555
+ notification = EventNotification.new(TableEvent.new(dbid),nil,EventCheckPolicy.new(15,nil,nil,1))
556
+ en.addEventNotification(notification)
557
+ en.startChecking
558
+ end
559
+
560
+ # Simple method to wait for one QuickBase change then stop
561
+ def EventNotifier.waitOnce(username,password,dbid)
562
+ en = EventNotifier.new(nil,username,password)
563
+ notification = EventNotification.new(TableEvent.new(dbid),nil,EventCheckPolicy.new(nil,nil,nil,nil,1))
564
+ en.addEventNotification(notification)
565
+ en.startChecking
566
+ end
567
+
568
+ # Simple method to check for records created after 2006 and modified today
569
+ def EventNotifier.watch2007Changes(username,password,dbid)
570
+ en = EventNotifier.new(nil,username,password)
571
+ rc = RecordCondition.new("Date Created", "OAF", "01-01-2007")
572
+ te = TableEvent.new(dbid,"recordModified",nil,rc)
573
+ notification = EventNotification.new(te)
574
+ en.addEventNotification(notification)
575
+ en.startChecking
576
+ end
577
+
578
+ end #class EventNotifier
579
+
580
+ end #module QuickBase
581
+
582
+ #if ARGV[2] and ARGV[2] == "watchCommunityForum"
583
+ # QuickBase::EventNotifier.watch(ARGV[0],ARGV[1],"bbqm84dzy")
584
+ #elsif ARGV[2] == "watchDBID" and ARGV[3] and ARGV[3].length > 0
585
+ # QuickBase::EventNotifier.watch(ARGV[0],ARGV[1],ARGV[3])
586
+ #elsif ARGV[2] == "checkDBIDOnce" and ARGV[3] and ARGV[3].length > 0
587
+ # QuickBase::EventNotifier.checkOnce(ARGV[0],ARGV[1],ARGV[3])
588
+ #elsif ARGV[2] == "waitDBIDOnce" and ARGV[3] and ARGV[3].length > 0
589
+ # QuickBase::EventNotifier.waitOnce(ARGV[0],ARGV[1],ARGV[3])
590
+ #elsif ARGV[2] == "watch2007Changes" and ARGV[3] and ARGV[3].length > 0
591
+ # QuickBase::EventNotifier.watch2007Changes(ARGV[0],ARGV[1],ARGV[3])
592
+ #end