ipp_quickbase_devkit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,286 @@
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
+
16
+ module QuickBase
17
+
18
+ # This class generates RSS data from specific fields in one or more
19
+ # QuickBase tables. It is intended to make standard RSS text that can
20
+ # be displayed by RSS readers or processed by other utilities.
21
+ #
22
+ # See the test() method at the bottom of this file for an example
23
+ # of using this class.
24
+ #
25
+ # Note that by default this class sorts the RSS items by <pubDate>
26
+ # descending order (i.e. most recent first); this means
27
+ # that items generated from records from different tables can be
28
+ # interspersed which each other. Some RSS readers do not appear
29
+ # to interpret <pubDate> correctly and therefore don't sort items
30
+ # correctly by themselves. Use 'unSorted' to prevent this class
31
+ # from sorting <items>. Also note that the sort order of records
32
+ # retrieved from tables can be set separately for each table, which
33
+ # is useful if the output is not going to be processed by an RSS readser.
34
+ class RSSGenerator
35
+
36
+ # Nested class to encapsulate RSS retrieval options for a QuickBase table.
37
+ class Table
38
+
39
+ attr_reader :dbid, :tableName, :fields, :clist, :slist, :query, :qid, :qname, :numRecords, :options
40
+ attr_reader :optionalFields, :title, :link, :description
41
+
42
+ # 'tableName' is the table name as it should appear in the output.
43
+ # Gets all records by default, sorted by Date Modified ("2") in descending order.
44
+ # Only retrieves fields in 'fields' parameter.
45
+ # <pubDate> is set to Date Modified ("2") by default
46
+ # <link> is set to Record ID# ("3") by default
47
+ # any additional quickbase field can be included in 'fields'
48
+ #-----------------------------------------------------------------------------
49
+ def initialize( dbid, tableName, fields, query = nil, qid = nil, qname = nil, numRecords = 0, slist = "2", options = "sortorder-D" )
50
+
51
+ raise "fields parameter must be a Hash" if !fields.is_a?( Hash )
52
+
53
+ @dbid, @tableName, @fields = dbid, tableName, fields
54
+ @query, @qid, @qname, @numRecords, @slist, @options = query, qid, qname, numRecords, slist, options
55
+
56
+ @fields["pubDate"] = "2" if @fields["pubDate"].nil?
57
+ @fields["link"] = "3" if @fields["link"].nil?
58
+
59
+ @optionalFields = Hash.new
60
+
61
+ @clist = ""
62
+ if @fields["description"] and @fields["title"]
63
+ @fields.each{ |k,v|
64
+ @clist << "#{v}."
65
+ if k != "pubDate" and k != "link" and k != "description" and k != "title"
66
+ @optionalFields[v] = k
67
+ end
68
+ }
69
+ @clist[-1] = ""
70
+ else
71
+ raise "fields parameter must include a 'description' and 'title'"
72
+ end
73
+
74
+ @title = "Default RSS Item Title"
75
+ @link = "http://www.quickbase.com/db/main"
76
+ @description = "Default RSS item description"
77
+
78
+ @options << ".num-#{numRecords}" if numRecords > 0
79
+ end
80
+
81
+ end # class Table
82
+
83
+
84
+ def initialize( qbc )
85
+ raise "Bad qbc parameter. qbc must be an instance of QuickBase::Client." if !qbc.is_a?( Client )
86
+ @qbc = qbc
87
+ raise "Please sign into QuickBase before using the RSSGenerator class" if @qbc.ticket.nil?
88
+ @items = Hash.new
89
+ @sortableItems = Hash.new
90
+ @sorted = true
91
+ @tables = Array.new
92
+ @timeNow = @namespace = ""
93
+ end
94
+
95
+ # Generates RSS <item>s from a filtered list of records and fields from a table.
96
+ def generateRSSItems( table )
97
+
98
+ @qbc.doQuery( table.dbid, table.query, table.qid, table.qname, table.clist, table.slist, "structured", table.options )
99
+
100
+ @qbc.eachRecord { |r|
101
+
102
+ pubDate = ""
103
+ title = ""
104
+ description = ""
105
+ link = ""
106
+ otherFieldValues = ""
107
+ itemText = ""
108
+ sortValue = 0
109
+
110
+ @qbc.eachField(r){ |f|
111
+ case f.attributes[ "id" ]
112
+ when table.fields[ "title" ]
113
+ title = f.text if f.has_text?
114
+ next
115
+ when table.fields[ "description" ]
116
+ description = f.text if f.has_text?
117
+ next
118
+ when table.fields[ "pubDate" ]
119
+ pubDate = f.text if f.has_text?
120
+ next
121
+ when table.fields[ "link" ]
122
+ link = f.text if f.has_text?
123
+ next
124
+ when table.slist
125
+ sortValue = f.text if f.has_text? and @sorted
126
+ next
127
+ end
128
+ table.optionalFields.each{ |fid,fieldName|
129
+ if f.attributes["id"] == fid and f.has_text?
130
+ field, value = onSetItemField( "#{@namespace}#{fieldName}", f.text )
131
+ otherFieldValues << " <#{field}>#{@qbc.encodeXML(value)}</#{field}>\n"
132
+ end
133
+ }
134
+ }
135
+
136
+ titleHash = "#{table.tableName}(#{link}): #{description[0..20]}..."
137
+ link = "https://www.quickbase.com/db/#{table.dbid}?a=dr&rid=#{link}"
138
+
139
+ itemText << " <item>\n"
140
+
141
+ title << " (#{table.tableName})"
142
+ field, value = onSetItemField( "#{@namespace}title", title )
143
+ itemText << " <#{field}>#{@qbc.encodeXML(value)}</#{field}>\n"
144
+
145
+ field, value = onSetItemField( "#{@namespace}pubDate", @qbc.formatDate(pubDate, "%a, %d %b %Y %H:%M PST" ) )
146
+ itemText << " <#{field}>#{value}</#{field}>\n"
147
+
148
+ guid = link.dup
149
+ field, value = onSetItemField( "#{@namespace}link", link )
150
+ itemText << " <#{field}>#{@qbc.encodeXML(value)}</#{field}>\n"
151
+
152
+ field, value = onSetItemField( "#{@namespace}guid", guid )
153
+ itemText << " <#{field}>#{@qbc.encodeXML(value)}</#{field}>\n"
154
+
155
+ field, value = onSetItemField( "#{@namespace}description", description )
156
+ itemText << " <#{field}>#{@qbc.encodeXML(value)}</#{field}>\n"
157
+
158
+ itemText << otherFieldValues
159
+ itemText << " </item>\n"
160
+
161
+ @items[titleHash] = itemText
162
+ @sortableItems[titleHash] = sortValue
163
+
164
+ }
165
+
166
+ end
167
+
168
+ # To modify item field names or values before they are inserted into the RSS output,
169
+ # derive from this class, override this method, and modify the return values.
170
+ def onSetItemField( field, value )
171
+ return field, value
172
+ end
173
+
174
+ # Prepend 'quickbase:' namespace to all data from QuickBase.
175
+ # intended for use by RSS processors other than RSS Readers.
176
+ def useNamespace
177
+ @namespace = "quickbase:"
178
+ end
179
+
180
+ # Items are sorted by <pubDate> unless this method is called.
181
+ def unSorted
182
+ @sorted = false
183
+ end
184
+
185
+ # Set the title for the RSS feed generated by this class.
186
+ def setTitle( title )
187
+ @title = " <#{@namespace}title>#{@qbc.encodeXML(title)}</#{@namespace}title>\n"
188
+ end
189
+
190
+ # Set the link for the RSS feed generated by this class.
191
+ # Set isDBID = false if this is not a QuickBase dbid.
192
+ def setLink( link, isDBID = true )
193
+ if isDBID
194
+ @link = " <#{@namespace}link>https://www.quickbase.com/db/#{link}</#{@namespace}link>\n"
195
+ else
196
+ @link = " <#{@namespace}link>#{@qbc.encodeXML(link)}</#{@namespace}link>\n"
197
+ end
198
+ end
199
+
200
+ # Insert the current time at the end of the descripton for the RSS
201
+ # feed generated by this class.
202
+ def appendTimeToDescription
203
+ @timeNow = " (#{Time.now})"
204
+ end
205
+
206
+ # Set the description for the RSS feed generated by this class.
207
+ def setDescription( description, appendTime = true )
208
+ appendTimeToDescription if appendTime
209
+ @description = " <#{@namespace}description>#{@qbc.encodeXML(description)}#{@timeNow}</#{@namespace}description>\n"
210
+ end
211
+
212
+ def header
213
+ text = "<?xml version=\"1.0\" ?>\n"
214
+ text << " <rss version=\"2.0\">\n"
215
+ text << " <channel>\n"
216
+ end
217
+
218
+ def footer
219
+ text = " </channel>\n"
220
+ text << " </rss>\n"
221
+ end
222
+
223
+ # Add a QuickBase table to the list of tables from which to generate RSS
224
+ #
225
+ # * dbid = QuickBase table dbid
226
+ # * tableName = the name of the QuickBase table as it should appear in the generated RSS
227
+ # * fields = Hash of RSS fields and the IDs of QuickBase fields , e.g. { "description" => "10", "myRSSfield" => "13" }
228
+ # * query, qid, qname = query parameters passed directly to QuickBase::Client,doQuery()
229
+ # * numItems = the number of records to retrieve from QuickBase
230
+ def addTable( dbid, tableName, fields, query = nil, qid = nil, qname = nil, numRecords = 0 )
231
+ t = Table.new( dbid, tableName, fields, query, qid, qname, numRecords )
232
+ @tables << t
233
+ end
234
+
235
+ # Generate all the RSS text for all the tables.
236
+ def generateRSStext
237
+ rssText = ""
238
+ if @tables.length > 0
239
+ rssText = header
240
+ rssText << @title if @title
241
+ rssText << @link if @link
242
+ rssText << @description if @description
243
+ @tables.each{ |table| generateRSSItems( table ) }
244
+ if @sorted
245
+ sortedItems = @sortableItems.sort{|a,b| b[1]<=>a[1] if a[1] and b[1] }
246
+ sortedItems.each{ |item| rssText << @items[item[0]] }
247
+ else
248
+ @items.each{ |item| rssText << @items[item[0]] }
249
+ end
250
+ rssText << footer
251
+ else
252
+ raise "Call addTable() one or more times before calling generateRSS"
253
+ end
254
+ rssText
255
+ end
256
+
257
+ end # class RSSGenerator
258
+
259
+ end #module QuickBase
260
+
261
+ # The following test code generates RSS text from recently modified
262
+ # records in the QuickBase Community Forum and KnowledgeBase.
263
+ def test( username, password )
264
+
265
+ qbc = QuickBase::Client.new( username, password )
266
+ qbRSSgen = QuickBase::RSSGenerator.new( qbc )
267
+
268
+ qbRSSgen.unSorted
269
+
270
+ qbRSSgen.setTitle( "QuickBase Forum/KnowledgeBase RSS" )
271
+ qbRSSgen.setLink( "main" )
272
+ qbRSSgen.setDescription( "RSS view of QuickBase Community Forum and KnowledgeBase" )
273
+
274
+ qbRSSgen.addTable("8emtadvk", "Community Forum", { "title" => "6", "description" => "10" }, nil, nil, nil, 75 )
275
+ qbRSSgen.addTable( "6mztyxu8", "KnowledgeBase", { "title" => "5", "description" => "6" }, nil, nil, nil, 50 )
276
+
277
+ rssText = qbRSSgen.generateRSStext
278
+
279
+ File.open( "QuickBaseInfoRSS.xml", "w" ) { |file| file.write( rssText ) }
280
+ print "\nPlease view QuickBaseInfoRSS.xml in an RSS reader, a browser or an XML editor.\n"
281
+
282
+ end
283
+
284
+ # uncomment the following line to test this class using 'ruby QuickBaseRSSGenerator.rb username password'
285
+ #test( ARGV[0], ARGV[1] ) if ARGV[1]
286
+
@@ -0,0 +1,545 @@
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
+ require 'QuickBaseClient'
14
+
15
+ module QuickBase
16
+
17
+ =begin rdoc
18
+
19
+ The data file format processed by this class is:-
20
+
21
+ application: <application name>
22
+ table: <table name>
23
+ dbid: <dbid>
24
+ record: [record id#]
25
+ <field name>: value
26
+ <field name>: value
27
+ ...
28
+ record:
29
+ <field name>: value
30
+ <field name>: value
31
+
32
+ 1) application:, table:, dbid:, record: <field name>: must appear at the beginning of a line.
33
+ 2) Except for text fields, whitespace will be trimmed from field values.
34
+ 3) Lines starting with whitespace are processed as data for a multi-line text field.
35
+ The first whitespace character will be ignored.
36
+ 4) If <application name> is followed by 'record:' , the table is assumed to have the
37
+ same name as the application, as in a single-table application.
38
+ 5) <dbid> will change the target/source table for any subsequent 'record:'.
39
+ When possible, use <dbid> instead of <application name> and <table name>.
40
+ 6) If <application name> refers to a non-existent application, the <application name> will be created
41
+ as a new single-table application and <table name> will be ignored.
42
+ 7) If <field name>: is not an existing field and is followed by a valid field type name, such as 'date',
43
+ the <field name> field wil be added to the QuickBase table. Field properties can appear after the
44
+ field type.
45
+ 8) If <field name> is a number and is not an existing field, the field will be treated as a QuickBase field id.
46
+ 9) If record: is followed by a number, the number is assumed to be the id of an existing record, and data
47
+ in subsequent <field name>: will overwite existing fields when sent to QuickBase.
48
+
49
+
50
+ Example:
51
+
52
+ application:Email Inbox
53
+ table:Messages
54
+ record:
55
+ From: my@email.com
56
+ Date Sent: 12/31/2007
57
+ Subject:Testing TextData class
58
+ Body: This is a test of the TextData class.
59
+ This is a multi-line text field.
60
+
61
+ =end
62
+
63
+ # Class to read and write human-editable text files of data that can be sent to QuickBase.
64
+ # The file format can also be used as a simple intermediate format for getting data into
65
+ # QuickBase programmatically from other sources. This format is better than CSV for
66
+ # human-readability and allows fields to be skipped and to appear in any sequence.
67
+ # The format is like yaml, probably isn't a subset of it, but is simpler.
68
+ class TextData
69
+
70
+ attr_reader :dbid
71
+
72
+ def initialize(username,password)
73
+ @username,@password=username,password
74
+ end
75
+
76
+ # Read a file in the format described above and send the data to QuickBase.
77
+ # Records are added or edited when tables, fields and values have been validated
78
+ # and all the data for a record has been accumulated.
79
+ # Any error in the input file will stop further processing of the file.
80
+ def sendDataToQuickBase(inputFilename, errorFilename)
81
+ @errorFilename = errorFilename
82
+ if FileTest.exist?(inputFilename)
83
+ if login
84
+ lineNumber = 1
85
+ begin
86
+ IO.foreach(inputFilename){|line|
87
+ if line.index( "application:") == 0
88
+ line.sub!("application:","")
89
+ line.strip!
90
+ setApplication(line)
91
+ elsif line.index( "table:") == 0
92
+ line.sub!("table:","")
93
+ line.strip!
94
+ setTable(line)
95
+ elsif line.index( "dbid:") == 0
96
+ line.sub!("dbid:","")
97
+ line.strip!
98
+ setDBID(line)
99
+ elsif line.index( "record:") == 0
100
+ line.sub!("record:","")
101
+ line.strip!
102
+ if line.match(/[0-9]+/)
103
+ setRecord(line)
104
+ else
105
+ setRecord(nil)
106
+ end
107
+ elsif line.match( /^([^:]+):(.*)$/)
108
+ setFieldValue($1, $2)
109
+ elsif line.match( /^([\s\t])(.*)$/)
110
+ appendFieldValue($2)
111
+ end
112
+ lineNumber = lineNumber + 1
113
+ }
114
+ addOrEditRecord
115
+ rescue StandardError => error
116
+ writeError("File #{inputFilename}, line #{lineNumber}: #{error}.")
117
+ end
118
+ else
119
+ writeError("Error connecting to QuickBase.")
120
+ end
121
+ logout
122
+ else
123
+ writeError("Input file #{inputFilename} does not exist.")
124
+ end
125
+ end
126
+
127
+ def writeError(error)
128
+ @errorFile = File.new(@errorFilename,"w") if @errorFilename and @errorFile.nil?
129
+ @errorFile.puts(error)
130
+ end
131
+
132
+ def login
133
+ if @username.nil? or @password.nil?
134
+ writeError("Must set username and password")
135
+ elsif @qbc.nil?
136
+ @qbc = QuickBase::Client.new(@username,@password)
137
+ end
138
+ ret = @qbc and @qbc.requestSucceeded
139
+ end
140
+
141
+ def logout
142
+ @qbc.signOut if @qbc
143
+ @qbc = nil
144
+ end
145
+
146
+ def resetTableVars
147
+ @activeRecordNumber = nil
148
+ @activeFieldID = nil
149
+ @activeField = nil
150
+ @fieldIDValues = nil
151
+ @fieldValues = nil
152
+ @fieldType = nil
153
+ @fieldProperties = nil
154
+ @fieldAllowsNewChoices = false
155
+ @fieldIsMultiLineText = false
156
+ @fieldIsValidFileAttachment = false
157
+ end
158
+
159
+ def setApplication(appName)
160
+ addOrEditRecord
161
+ resetTableVars
162
+ @singleTableApplication = false
163
+ @qbc.findDBByname(appName)
164
+ if @qbc.dbid.nil?
165
+ @qbc.createDatabase(appName,appName)
166
+ raise "Error creating application '#{name}'" if @qbc.dbid.nil?
167
+ raise "Unable to find table '#{@appName}'" if !@qbc.lookupChdbid(@appName)
168
+ @singleTableApplication = true
169
+ end
170
+ raise "Error finding or creating application '#{name}'" if @qbc.dbid.nil?
171
+ @appName = appName
172
+ @qbc._getSchema
173
+ @dbid = @qbc.dbid.dup
174
+ end
175
+
176
+ def setTable(name)
177
+ addOrEditRecord
178
+ resetTableVars
179
+ if !@singleTableApplication
180
+ raise "Unable to find table '#{name}'" if !@qbc.lookupChdbid(name)
181
+ @qbc._getSchema
182
+ end
183
+ @dbid = @qbc.dbid.dup
184
+ end
185
+
186
+ def setDBID(id)
187
+ addOrEditRecord
188
+ resetTableVars
189
+ @singleTableApplication = false
190
+ @qbc.getSchema(id)
191
+ raise "Unable to find table with the '#{id}' id" if @qbc.dbid.nil?
192
+ @dbid = @qbc.dbid.dup
193
+ end
194
+
195
+ def setRecord(number)
196
+ addOrEditRecord
197
+ @activeRecordNumber = number
198
+ @activeRecordNumber = nil if @activeRecordNumber and @activeRecordNumber.length == 0
199
+ if @activeRecordNumber
200
+ @qbc._setActiveRecord(@activeRecordNumber)
201
+ else
202
+ @qbc.resetrid
203
+ end
204
+ end
205
+
206
+ def checkFieldNameAndValue(fieldName,fieldValue)
207
+
208
+ @fieldNameIsFieldID = false
209
+ @fieldAllowsNewChoices = false
210
+ @fieldIsMultiLineText = false
211
+ @fieldIsValidFileAttachment = false
212
+ @fieldType = nil
213
+ @fieldProperties = nil
214
+
215
+ fieldElement = @qbc.lookupField( @qbc.lookupFieldIDByName(fieldName) )
216
+ if !fieldElement and fieldName.match(/[0-9]+/) # if it's a number, see if it's a valid field id
217
+ fieldElement = @qbc.lookupField(fieldName,fieldValue)
218
+ @fieldNameIsFieldID = true if fieldElement
219
+ end
220
+
221
+ if !fieldElement # field doesn't exist. the field can be created if the value is actually a valid field type
222
+ if fieldValue and fieldValue.length > 0
223
+ fieldValue.strip!
224
+ if fieldValue.length > 0
225
+ fieldTypeAndProperties = fieldValue.split(/ /)
226
+ type = fieldTypeAndProperties[0]
227
+ if @qbc.isValidFieldType?(type)
228
+ @fieldType = Hash.new if @fieldType.nil?
229
+ @fieldType[fieldName] = type.dup
230
+ fieldTypeAndProperties.shift # anything after the field type must be valid field properties in the form property="value"
231
+ fieldTypeAndProperties.each { |property|
232
+ propertyType = property[0..(property.index("=")-1)]
233
+ raise "Invalid field property type #{propertyType}" if !@qbc.isValidFieldProperty?(propertyType)
234
+ propertyValue = property[(property.index("=")+1)..(property.rindex("\"")-1)]
235
+ raise "Missing value for field property #{propertyType}" if propertyValue.nil? or propertyValue.length == 0
236
+ @fieldProperties = Hash.new if @fieldProperties.nil?
237
+ propertyAndValuePair = Hash.new
238
+ propertyAndValuePair[propertyType]=propertyValue
239
+ @fieldProperties[fieldName] << propertyAndValuePair
240
+ }
241
+ else
242
+ raise "Invalid field type #{type}."
243
+ end
244
+ else
245
+ raise "Invalid field name #{fieldName}."
246
+ end
247
+ end
248
+ else
249
+ # do basic data type check
250
+ type = fieldElement.attributes["field_type"].dup
251
+ raise "Unable to determine data type for #{fieldName} field" if type.nil?
252
+ if fieldValue.length > 0 #any field can be blanked out
253
+ case type
254
+ when "checkbox"
255
+ if !(fieldValue == "1" or fieldValue == "0")
256
+ raise "Invalid data '#{fieldValue}' for checkbox field #{fieldName}"
257
+ end
258
+ when "date"
259
+ fieldValue.gsub!("/","-")
260
+ if !fieldValue.match(/[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9][0-9][0-9]/)
261
+ raise "Invalid data '#{fieldValue}' for date field #{fieldName}"
262
+ end
263
+ when "duration", "float", "currency", "rating"
264
+ fieldValue.gsub!(",","")
265
+ if !fieldValue.match(/[0-9]*\.?[0-9]*/)
266
+ raise "Invalid data '#{fieldValue}' for field #{fieldName}"
267
+ end
268
+ when "phone"
269
+ if !fieldValue.match(/[0-9|\.|x]+/)
270
+ raise "Invalid data '#{fieldValue}' for phone field #{fieldName}"
271
+ end
272
+ when "file"
273
+ if FileTest.exist?(fieldValue)
274
+ @fieldIsValidFileAttachment = true
275
+ else
276
+ raise "Invalid file name '#{fieldValue}' for file attachment field #{fieldName}"
277
+ end
278
+ end
279
+ end
280
+ # check whether field allows user to add to a choicelist.
281
+ @fieldAllowsNewChoices = fieldAllowsNewChoices?(fieldElement)
282
+ # check whether field allows mutliple lines.
283
+ @fieldIsMultiLineText = fieldIsMultiLineText?(fieldElement)
284
+ end
285
+ fieldElement
286
+ end
287
+
288
+ def fieldAllowsNewChoices?(fieldElement)
289
+ allowsUserChoices = false
290
+ findPropertyBlock = proc { |element|
291
+ if element.is_a?(REXML::Element) and element.name == "allow_new_choices" and element.has_text?
292
+ allowsUserChoices = (element.text == "1")
293
+ end
294
+ }
295
+ @qbc.processChildElements(fieldElement, true, findPropertyBlock)
296
+ allowsUserChoices
297
+ end
298
+
299
+ def fieldIsMultiLineText?(fieldElement)
300
+ isMultiLineText = false
301
+ findPropertyBlock = proc { |element|
302
+ if element.is_a?(REXML::Element) and element.name == "num_lines" and element.has_text?
303
+ isMultiLineText = (element.text != "1")
304
+ end
305
+ }
306
+ @qbc.processChildElements(fieldElement, true, findPropertyBlock)
307
+ isMultiLineText
308
+ end
309
+
310
+ def addField(field,type,properties)
311
+ newFieldID, newfieldLabel = @qbc._addField(field, type)
312
+ if newFieldID
313
+ if properties
314
+ fieldPropertiesToSet = Hash.new
315
+ properties.each{|property|
316
+ property.each{|propertyName,propertyValue| fieldPropertiesToSet[propertyName] = propertyValue }
317
+ }
318
+ @qbc._setFieldProperties(fieldPropertiesToSet,newFieldID)
319
+ @qbc._getSchema
320
+ end
321
+ else
322
+ raise "Error creating new field #{field}"
323
+ end
324
+ end
325
+
326
+ def setFieldValue(fieldName, value)
327
+ if checkFieldNameAndValue(fieldName,value)
328
+ if @fieldNameIsFieldID
329
+ @activeFieldID = fieldName
330
+ @activeField = nil
331
+ @fieldIDValues = Hash.new if @fieldIDValues.nil?
332
+ @fieldIDValues[@activeFieldID] = value.dup
333
+ @fieldIDProperties = Hash.new if @fieldIDProperties.nil?
334
+ @fieldIDProperties[@activeFieldID] = { "fieldAllowsNewChoices"=>@fieldAllowsNewChoices, "fieldIsMultiLineText"=>@fieldIsMultiLineText, "fieldIsValidFileAttachment"=>@fieldIsValidFileAttachment }
335
+ else
336
+ @activeField = fieldName
337
+ @activeFieldID = nil
338
+ @fieldValues = Hash.new if @fieldValues.nil?
339
+ @fieldValues[@activeField] = value.dup
340
+ @fieldProperties = Hash.new if @fieldProperties.nil?
341
+ @fieldProperties[@activeField] = { "fieldAllowsNewChoices"=>@fieldAllowsNewChoices,"fieldIsMultiLineText"=>@fieldIsMultiLineText, "fieldIsValidFileAttachment"=>@fieldIsValidFileAttachment }
342
+ end
343
+ elsif @fieldType and @fieldType[fieldName]
344
+ if @fieldProperties and @fieldProperties[fieldName]
345
+ addField(fieldName,@fieldType[fieldName],@fieldProperties[fieldName])
346
+ else
347
+ addField(fieldName,@fieldType[fieldName],nil)
348
+ end
349
+ end
350
+ end
351
+
352
+ def appendFieldValue(value)
353
+ if value and value.length > 0
354
+ if @activeField and @fieldProperties[@activeField]["fieldIsMultiLineText"]
355
+ @fieldValues = Hash.new if @fieldValues.nil?
356
+ @fieldValues[@activeField] << "\n"
357
+ @fieldValues[@activeField] << value.dup
358
+ elsif @activeFieldID and @fieldIDProperties[@activeFieldID]["fieldIsMultiLineText"]
359
+ @fieldIDValues = Hash.new if @fieldIDValues.nil?
360
+ @fieldIDValues[@activeFieldID] << "\n"
361
+ @fieldIDValues[@activeFieldID] << value.dup
362
+ end
363
+ end
364
+ end
365
+
366
+ def addFieldValuePairs
367
+ @qbc.clearFieldValuePairList
368
+ if @fieldValues
369
+ @fieldValues.each{ |f,v|
370
+ if f and v and @fieldProperties and @fieldProperties[f] and @fieldProperties[f]["fieldAllowsNewChoices"]
371
+ @fieldChoicesToSet = Hash.new if @fieldChoicesToSet.nil?
372
+ @fieldChoicesToSet[f] = Array.new if @fieldChoicesToSet[f].nil?
373
+ @fieldChoicesToSet[f] << v
374
+ end
375
+ if f and v and @fieldProperties and @fieldProperties[f] and @fieldProperties[f]["fieldIsValidFileAttachment"]
376
+ @qbc.addFieldValuePair(f.dup,nil,v.dup,nil)
377
+ else
378
+ @qbc.addFieldValuePair(f.dup,nil,nil,v.dup)
379
+ end
380
+ }
381
+ @fieldValues = nil
382
+ end
383
+ if @fieldIDValues
384
+ @fieldIDValues.each{ |f,v|
385
+ if @fieldIDProperties and @fieldIDProperties[f] and @fieldIDProperties[f]["fieldAllowsNewChoices"]
386
+ @fieldIDChoicesToSet = Hash.new if @fieldChoicesToSet.nil?
387
+ @fieldIDChoicesToSet[f] << v
388
+ end
389
+ if @fieldIDProperties and @fieldIDProperties[f] and @fieldIDProperties[f]["fieldIsValidFileAttachment"]
390
+ @qbc.addFieldValuePair(nil,f.dup,v.dup,nil)
391
+ else
392
+ @qbc.addFieldValuePair(nil,f.dup,nil,v.dup)
393
+ end
394
+ }
395
+ @fieldIDValues = nil
396
+ end
397
+ @qbc.fvlist
398
+ end
399
+
400
+ def isValidRecord?(addingRecord)
401
+ valid = true
402
+ if addingRecord and @qbc.fields
403
+ # check that all required fields are present
404
+ requiredFieldIDs = Hash.new
405
+ requiredFields = Hash.new
406
+ fieldID = ""
407
+ fieldName = ""
408
+ findRequiredFieldsBlock = proc { |element|
409
+ if element.is_a?(REXML::Element) and element.name == "field"
410
+ fieldID = element.attributes["id"]
411
+ elsif element.is_a?(REXML::Element) and element.name == "label" and element.has_text?
412
+ fieldName = element.text
413
+ elsif element.is_a?(REXML::Element) and element.name == "required" and element.has_text?
414
+ if element.text == "1"
415
+ requiredFieldIDs[fieldID] = "1"
416
+ requiredFields[fieldName] = "1"
417
+ end
418
+ end
419
+ }
420
+ @qbc.processChildElements(@qbc.fields, false, findRequiredFieldsBlock)
421
+
422
+ if @fieldValues
423
+ @fieldValues.each{ |f,v| requiredFields[f] = nil if requiredFields[f] }
424
+ end
425
+ if @fieldIDValues
426
+ @fieldIDValues.each{ |f,v| requiredFieldIDs[f] = nil if requiredFieldIDs[f] }
427
+ end
428
+ missingFields = ""
429
+ requiredFields.each{ |f,v| missingFields << "#{f} " if v } if @fieldValues
430
+ requiredFieldIDs.each{ |f,v| missingFields << "#{f} " if v } if @fieldIDValues
431
+ raise "Required fields are missing: #{missingFields}" if missingFields.length > 0
432
+ end
433
+ valid
434
+ end
435
+
436
+ def addOrEditRecord
437
+ if @activeRecordNumber
438
+ editRecord
439
+ elsif isValidRecord?(true)
440
+ addRecord
441
+ end
442
+ end
443
+
444
+ def addRecord
445
+ if addFieldValuePairs
446
+ addFieldChoices
447
+ @qbc.addRecord(@qbc.dbid, @qbc.fvlist)
448
+ end
449
+ end
450
+
451
+ def editRecord
452
+ if @activeRecordNumber and addFieldValuePairs
453
+ addFieldChoices
454
+ @qbc.editRecord(@qbc.dbid, @activeRecordNumber, @qbc.fvlist)
455
+ @activeRecordNumber = nil
456
+ end
457
+ end
458
+
459
+ def addFieldChoices
460
+ if @fieldIDChoicesToSet
461
+ @fieldIDChoicesToSet.each{ |f,choices|
462
+ @qbc._fieldAddChoices(f,choices)
463
+ }
464
+ @fieldIDChoicesToSet = nil
465
+ end
466
+ if @fieldChoicesToSet
467
+ @fieldChoicesToSet.each{ |f,choices|
468
+ @qbc._fieldNameAddChoices(f,choices)
469
+ }
470
+ @fieldChoicesToSet = nil
471
+ end
472
+ end
473
+
474
+ # Writes all records and all fields from a table to the specified
475
+ # outputFilename, in the format specified above, sorted by record ID#.
476
+ # Since they can't be sent back into QuickBase, built-in fields are excluded.
477
+ def writeDataFromQuickBase(dbid, outputFilename, errorFilename)
478
+ @errorFilename = errorFilename
479
+ if login
480
+ @qbc.getSchema(dbid)
481
+ if @qbc.dbid and @qbc.requestSucceeded
482
+ @outputFile = File.new(outputFilename,"w")
483
+ @outputFile.puts( "dbid:#{dbid}")
484
+ @isBuitlInField = false
485
+ recordIDs = @qbc.getAllValuesForFields(dbid,["Record ID#"],nil,nil,nil,"3","3")
486
+ if recordIDs
487
+ recordIDs["Record ID#"].each{ |recordID|
488
+ @outputFile.puts( "record:#{recordID}")
489
+ @qbc._getRecordInfo(recordID)
490
+ processFieldDataBlock = proc { |element|
491
+ if element.is_a?(REXML::Element)
492
+ if element.name == "fid"
493
+ @isBuitlInField = element.text.to_i < 6
494
+ elsif element.name == "name" and !@isBuitlInField
495
+ @outputFile.print("#{element.text}:")
496
+ elsif element.name == "type"
497
+ @outputFieldType = element.text.dup.downcase!
498
+ elsif element.name == "value" and !@isBuitlInField
499
+ outputFieldValue = ""
500
+ outputFieldValue = element.text.dup if element.has_text?
501
+ outputFieldValue.gsub!("<BR/>","\n ")
502
+ outputFieldValue = @qbc.formatFieldValue(outputFieldValue,@outputFieldType)
503
+ outputFieldValue = "" if outputFieldValue.nil?
504
+ @outputFile.puts(outputFieldValue)
505
+ end
506
+ end
507
+ }
508
+ @qbc.processChildElements(@qbc.field_data_list,true,processFieldDataBlock)
509
+ }
510
+ @outputFile.close
511
+ end
512
+ else
513
+ writeError("Invalid dbid #{dbid}.")
514
+ end
515
+ logout
516
+ else
517
+ writeError("Error connecting to QuickBase.")
518
+ end
519
+ end
520
+
521
+ def TextData.uploadData(username,password,file)
522
+ td = QuickBase::TextData.new(username,password)
523
+ td.sendDataToQuickBase(file,"textDataUploadErrors.txt")
524
+ end
525
+
526
+ def TextData.downloadData(username,password,dbid)
527
+ td = QuickBase::TextData.new(username,password)
528
+ td.writeDataFromQuickBase(dbid,"downloadedTextData.txt","textDataDownloadErrors.txt")
529
+ end
530
+
531
+ # Sends data from file to QuickBase, then overwrites the same
532
+ # file with data from the last referenced table. It's best to
533
+ # use this for synchronizing just one table's data.
534
+ def TextData.synchDataFile(username,password,file)
535
+ td = QuickBase::TextData.new(username,password)
536
+ td.sendDataToQuickBase(file,"textDataUploadErrors.txt")
537
+ td.writeDataFromQuickBase(td.dbid,file,"textDataDownloadErrors.txt")
538
+ end
539
+
540
+ end # class TextData
541
+
542
+ end # module QuickBase
543
+
544
+ #QuickBase::TextData.uploadData(ARGV[0],ARGV[1],ARGV[2]) if ARGV[2]
545
+ #QuickBase::TextData.downloadData(ARGV[0],ARGV[1],ARGV[3]) if ARGV[3]