rtunesu 0.2.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/History.txt +0 -5
- data/README.txt +2 -2
- data/Rakefile +15 -2
- data/VERSION +1 -0
- data/lib/rtunesu/connection.rb +152 -54
- data/lib/rtunesu/document.rb +21 -26
- data/lib/rtunesu/entities/course.rb +4 -13
- data/lib/rtunesu/entities/division.rb +5 -12
- data/lib/rtunesu/entities/external_feed.rb +14 -0
- data/lib/rtunesu/entities/group.rb +5 -14
- data/lib/rtunesu/entities/permission.rb +1 -5
- data/lib/rtunesu/entities/section.rb +15 -11
- data/lib/rtunesu/entities/site.rb +6 -13
- data/lib/rtunesu/entities/template.rb +4 -0
- data/lib/rtunesu/entities/theme.rb +3 -15
- data/lib/rtunesu/entities/track.rb +12 -27
- data/lib/rtunesu/entity.rb +185 -74
- data/lib/rtunesu/log.rb +69 -0
- data/lib/rtunesu/subentities.rb +60 -0
- data/lib/rtunesu/user.rb +3 -1
- data/lib/rtunesu/version.rb +2 -2
- data/lib/rtunesu.rb +11 -6
- data/lib/show_tree.xml +7 -0
- data/spec/connection_spec.rb +6 -30
- data/spec/document_spec.rb +82 -8
- data/spec/entities/course_spec.rb +13 -40
- data/spec/entities/division_spec.rb +13 -3
- data/spec/entities/external_feed_spec.rb +21 -0
- data/spec/entities/group_spec.rb +14 -3
- data/spec/entities/permission_spec.rb +12 -3
- data/spec/entities/section_spec.rb +32 -3
- data/spec/entities/site_spec.rb +16 -3
- data/spec/entities/track_spec.rb +32 -3
- data/spec/entity_spec.rb +147 -86
- data/spec/fixtures/requests/create_course.xml +17 -0
- data/spec/fixtures/requests/create_division.xml +11 -0
- data/spec/fixtures/requests/create_group.xml +11 -0
- data/spec/fixtures/requests/create_section.xml +11 -0
- data/spec/fixtures/requests/delete_course.xml +8 -0
- data/spec/fixtures/requests/delete_group.xml +8 -0
- data/spec/fixtures/requests/delete_section.txt +8 -0
- data/spec/fixtures/requests/show_course.xml +8 -0
- data/spec/fixtures/requests/show_group.xml +8 -0
- data/spec/fixtures/requests/show_section.xml +8 -0
- data/spec/fixtures/requests/update_course.xml +10 -0
- data/spec/fixtures/requests/update_group.xml +9 -0
- data/spec/fixtures/requests/update_section.txt +10 -0
- data/spec/fixtures/responses/failure/create_course.xml +5 -0
- data/spec/fixtures/responses/failure/create_division.xml +5 -0
- data/spec/fixtures/responses/failure/create_group.xml +5 -0
- data/spec/fixtures/responses/failure/create_section.xml +5 -0
- data/spec/fixtures/responses/failure/delete_group.xml +5 -0
- data/spec/fixtures/responses/failure/show_course.xml +11 -0
- data/spec/fixtures/responses/failure/show_division.xml +11 -0
- data/spec/fixtures/responses/failure/show_group.xml +11 -0
- data/spec/fixtures/responses/failure/show_section.xml +11 -0
- data/spec/fixtures/responses/failure/show_site.xml +11 -0
- data/spec/fixtures/responses/failure/update_section.txt +5 -0
- data/spec/fixtures/responses/success/create_course.xml +5 -0
- data/spec/fixtures/responses/success/create_division.xml +5 -0
- data/spec/fixtures/responses/success/create_group.xml +5 -0
- data/spec/fixtures/responses/success/create_section.xml +5 -0
- data/spec/fixtures/responses/success/delete_course.xml +4 -0
- data/spec/fixtures/responses/success/delete_division.xml +4 -0
- data/spec/fixtures/responses/success/delete_group.xml +4 -0
- data/spec/fixtures/responses/success/delete_section.txt +4 -0
- data/spec/fixtures/responses/success/delete_section.xml +4 -0
- data/spec/fixtures/responses/success/show_course.xml +393 -0
- data/spec/fixtures/responses/success/show_division.xml +393 -0
- data/spec/fixtures/responses/success/show_group.xml +137 -0
- data/spec/fixtures/responses/success/show_section.xml +152 -0
- data/spec/fixtures/responses/success/show_site.xml +393 -0
- data/spec/fixtures/responses/success/update_course.xml +4 -0
- data/spec/fixtures/responses/success/update_division.xml +4 -0
- data/spec/fixtures/responses/success/update_group.xml +4 -0
- data/spec/fixtures/responses/success/update_section.txt +4 -0
- data/spec/fixtures/responses/success/update_section.xml +4 -0
- data/spec/fixtures/responses/success/update_site.xml +4 -0
- data/spec/spec_helper.rb +72 -7
- data/spec/token_generation_spec.rb +23 -0
- data/spec/user_spec.rb +3 -3
- metadata +81 -76
- data/Manifest.txt +0 -77
- data/config/hoe.rb +0 -76
- data/config/requirements.rb +0 -15
- data/lib/multipart.rb +0 -53
- data/spec/documents/add_spec.rb +0 -41
- data/spec/documents/delete_spec.rb +0 -30
- data/spec/documents/merge_spec.rb +0 -30
- data/spec/documents/show_tree_spec.rb +0 -16
- data/spec/fixtures/add_course.xml +0 -26
- data/spec/fixtures/add_division.xml +0 -26
- data/spec/fixtures/add_group.xml +0 -27
- data/spec/fixtures/add_permission.xml +0 -12
- data/spec/fixtures/add_section.xml +0 -34
- data/spec/fixtures/add_track.xml +0 -19
- data/spec/fixtures/delete_course.xml +0 -8
- data/spec/fixtures/delete_division.xml +0 -8
- data/spec/fixtures/delete_group.xml +0 -8
- data/spec/fixtures/delete_permission.xml +0 -9
- data/spec/fixtures/delete_section.xml +0 -8
- data/spec/fixtures/delete_track.xml +0 -7
- data/spec/fixtures/merge_course.xml +0 -38
- data/spec/fixtures/merge_division.xml +0 -47
- data/spec/fixtures/merge_group.xml +0 -29
- data/spec/fixtures/merge_permission.xml +0 -12
- data/spec/fixtures/merge_section.xml +0 -36
- data/spec/fixtures/merge_site.xml +0 -31
- data/spec/fixtures/merge_track.xml +0 -18
- data/spec/fixtures/requests/add_coures_request.xml +0 -0
- data/spec/fixtures/show_tree.xml +0 -273
- data/spec/fixtures/update_group.xml +0 -7
- data/tasks/deployment.rake +0 -34
- data/tasks/website.rake +0 -17
- data/website/index.html +0 -54
- data/website/index.txt +0 -7
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -138
- data/website/template.html.erb +0 -48
data/.gitignore
ADDED
data/History.txt
CHANGED
data/README.txt
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
== DESCRIPTION:
|
|
4
4
|
RTunesU is a ruby library for accessing Apple's iTunes U Webservices to integrate your education institutions iTunes U account into ruby applications. iTunes U's Webservices interface is fairly primitive by today's standards for XML based APIs. Some known flaws of iTunes U
|
|
5
5
|
* No arbitrary search
|
|
6
|
-
* Queries for missing objects return an XML document representing the entire institution instead of returning an error
|
|
6
|
+
* Queries for missing objects may return an XML document representing the entire institution instead of returning an error (rdar://7253913)
|
|
7
|
+
* No ability to directly find Tracks (rdar://7254419)
|
|
7
8
|
* Does not follow REST principles
|
|
8
9
|
* Does not use HTTP status codes meaningfully
|
|
9
|
-
* Collections not contained in an outer element
|
|
10
10
|
|
|
11
11
|
== FEATURES/PROBLEMS:
|
|
12
12
|
- TODO: file uploading
|
data/Rakefile
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'jeweler'
|
|
4
|
+
|
|
5
|
+
Jeweler::Tasks.new do |gemspec|
|
|
6
|
+
gemspec.name = 'rtunesu'
|
|
7
|
+
gemspec.summary = "A library for using Apple's iTunes U Webservices API"
|
|
8
|
+
gemspec.description = "A library for using Apple's iTunes U Webservices API"
|
|
9
|
+
gemspec.email = 'pietrekg@umich.edu'
|
|
10
|
+
gemspec.authors = ['Trek Glowacki']
|
|
11
|
+
gemspec.add_dependency('hpricot', '>= 0.6.164')
|
|
12
|
+
gemspec.add_dependency('builder', '>= 2.0')
|
|
13
|
+
gemspec.add_dependency('ruby-hmac', '>= 0.3.1')
|
|
14
|
+
end
|
|
15
|
+
Jeweler::GemcutterTasks.new
|
|
3
16
|
|
|
4
17
|
Dir['tasks/**/*.rake'].each { |rake| load rake }
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.3.5
|
data/lib/rtunesu/connection.rb
CHANGED
|
@@ -4,12 +4,14 @@ require 'hmac'
|
|
|
4
4
|
require 'hmac-sha2'
|
|
5
5
|
require 'digest'
|
|
6
6
|
require 'net/https'
|
|
7
|
-
require '
|
|
7
|
+
require 'mime/types'
|
|
8
8
|
require 'uri'
|
|
9
|
-
require 'timeout'
|
|
10
9
|
|
|
11
10
|
module RTunesU
|
|
12
|
-
|
|
11
|
+
class LocationNotFound < StandardError; end
|
|
12
|
+
# Connection is a class for opening and using a connection to the iTunes U Webservices system.
|
|
13
|
+
# To open a connection to iTunes U, you first need to create a RTunesU::User object using the
|
|
14
|
+
# administrator data you received from Apple when they created your iTunes U site.
|
|
13
15
|
# === Creating a Connection
|
|
14
16
|
# include RTunesU
|
|
15
17
|
# # create a new admin user from our Institution's login information
|
|
@@ -20,26 +22,79 @@ module RTunesU
|
|
|
20
22
|
# connection = Connection.new(:user => user, :site => 'example.edu', :shared_secret => 'STRINGOFTHIRTYLETTERSORNUMBERS')
|
|
21
23
|
#
|
|
22
24
|
# === Using a Connection
|
|
23
|
-
# A Connection object is needed for operations that communicate with Apple's iTunes U Webservices.
|
|
24
|
-
#
|
|
25
|
+
# A Connection object is needed for operations that communicate with Apple's iTunes U Webservices.
|
|
26
|
+
# Connection objects can be used two ways:
|
|
27
|
+
# First, you can set a base connection object for all entities with
|
|
28
|
+
# Entity.set_base_connection(connection_object)
|
|
29
|
+
# This Connection and its associated user will be used for all iTunes U interaction. This is useful if
|
|
30
|
+
# you are generally using RTunesU as a utility libray for a learning managemnet system.
|
|
31
|
+
# This methodology will allow you to use methods that communicate with iTunes U without having to
|
|
32
|
+
# specify a connection object for each request. e.g.:
|
|
33
|
+
# Course.find(11234567)
|
|
34
|
+
# group.save
|
|
35
|
+
# division.delete
|
|
36
|
+
#
|
|
37
|
+
# Secondly, you supply a new connection objec to be used instead of the class connection object. This is useful for
|
|
38
|
+
# generating login urls for users, or allowing iTunes U's included permissions system to limit what a specfic user can do
|
|
39
|
+
# Pass this custom connection object as the only arguemnt to method calls that interact with iTunes U and it will
|
|
40
|
+
# be used instead of the classes base connection object.
|
|
41
|
+
# For example, calls to .save, .create, .update, and .delete on Entity objects take a Connection object as
|
|
42
|
+
# their only argument.
|
|
43
|
+
#
|
|
44
|
+
# Course.find(11234567, custom_connection_object_with_limited_permissions)
|
|
45
|
+
# group.save(custom_connection_object_with_limited_permissions)
|
|
46
|
+
# division.delete(custom_connection_object_with_limited_permissions)
|
|
47
|
+
#
|
|
48
|
+
# To communicate with the iTunes U Service, a Connection object will generate proper authentication data,
|
|
49
|
+
# hash your request, and (if neccessary) send XML data to iTunes U.
|
|
25
50
|
# For more inforamtion about this processs see: http://deimos.apple.com/rsrc/doc/iTunesUAdministratorsGuide/IntegratingAuthenticationandAuthorizationServices/chapter_3_section_3.html
|
|
26
|
-
|
|
27
|
-
# Because the you will likely only need a single unchanging Connection object for your application you may wish to initialize a single Connection object for the admin credentials at application start time and assign it to a constant. This is especially beneficial for long running applications like web applications.
|
|
28
|
-
class Connection
|
|
29
|
-
TIMEOUT = 240
|
|
30
|
-
|
|
51
|
+
class Connection
|
|
31
52
|
attr_accessor :user, :options
|
|
32
53
|
|
|
33
54
|
def initialize(options = {})
|
|
34
55
|
self.user, self.options = options[:user], options
|
|
35
56
|
end
|
|
36
57
|
|
|
37
|
-
#
|
|
58
|
+
# Generates HTTP mulitpart/form-data body and headers.
|
|
59
|
+
def http_multipart_data(params) #:nodoc:
|
|
60
|
+
crlf = "\r\n"
|
|
61
|
+
body = ""
|
|
62
|
+
headers = {}
|
|
63
|
+
|
|
64
|
+
boundary = "x" + Time.now.to_i.to_s(16)
|
|
65
|
+
|
|
66
|
+
headers["Content-Type"] = "multipart/form-data; boundary=#{boundary}"
|
|
67
|
+
headers["User-Agent"] = "RTunesU/#{RTunesU::VERSION::STRING}"
|
|
68
|
+
params.each do |key,value|
|
|
69
|
+
esc_key = key.to_s
|
|
70
|
+
body << "--#{boundary}#{crlf}"
|
|
71
|
+
|
|
72
|
+
if value.respond_to?(:read)
|
|
73
|
+
mime_type = MIME::Types.type_for(value.path)[0] || MIME::Types["application/octet-stream"][0]
|
|
74
|
+
body << "Content-Disposition: form-data; name=\"#{esc_key}\"; filename=\"#{File.basename(value.path)}\"#{crlf}"
|
|
75
|
+
body << "Content-Type: #{mime_type.simplified}#{crlf*2}"
|
|
76
|
+
body << value.read
|
|
77
|
+
value.rewind
|
|
78
|
+
else
|
|
79
|
+
body << "Content-Disposition: form-data; name=\"#{esc_key}\"#{crlf*2}#{value}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
body << "#{crlf}--#{boundary}--#{crlf*2}"
|
|
84
|
+
headers["Content-Length"] = body.size.to_s
|
|
85
|
+
|
|
86
|
+
return [ body, headers ]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# iTunes U requires all request to include an authorization token that includes a User's credentials, indetifiying information,
|
|
90
|
+
# and the time of the request. This data is hashed against your institution's shared secret (provided by Apple
|
|
91
|
+
# with your iTunes U account information). Because tokens are valid only for 90 seconds they are generated for each
|
|
92
|
+
# request attempt.
|
|
38
93
|
def generate_authorization_token
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
94
|
+
# create the token that contains the necessary elements to authorize the user
|
|
95
|
+
# using a nested array because the alphabetical order must be maintained
|
|
96
|
+
token = [['credentials', self.user.to_credential_string,], ['identity', self.user.to_identity_string], ['time', Time.now.to_i.to_s]]
|
|
97
|
+
encoded_parms = token.collect {|pair| pair[1] = CGI.escape(pair[1]); pair.join('=')}.join('&')
|
|
43
98
|
|
|
44
99
|
digest = Digest::SHA2.new
|
|
45
100
|
digest.update(encoded_parms)
|
|
@@ -50,10 +105,10 @@ module RTunesU
|
|
|
50
105
|
# add the hashed digital signature to the end of the query parameters
|
|
51
106
|
encoded_parms += "&signature=#{hmac.hexdigest}"
|
|
52
107
|
end
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
108
|
+
|
|
109
|
+
# Sends a request to iTunes U for a valid upload location for a file.
|
|
110
|
+
def upload_url_for_location(location) #:nodoc:
|
|
111
|
+
url_string = "#{API_URL}/GetUploadURL/#{self.options[:site]}.#{location}?#{self.generate_authorization_token}&type=XMLControlFile"
|
|
57
112
|
url = URI.parse(url_string)
|
|
58
113
|
http = Net::HTTP.new(url.host, url.port)
|
|
59
114
|
http.use_ssl = true
|
|
@@ -61,58 +116,101 @@ module RTunesU
|
|
|
61
116
|
response = http.start {|http|
|
|
62
117
|
http.request(Net::HTTP::Get.new(url.path + '?' + url.query))
|
|
63
118
|
}
|
|
119
|
+
raise LocationNotFound if response.kind_of?(Net::HTTPNotFound)
|
|
64
120
|
response.body
|
|
65
121
|
end
|
|
66
122
|
|
|
67
|
-
# The URL that receives all iTunes U webservices requests.
|
|
123
|
+
# The URL that receives all iTunes U webservices requests.
|
|
124
|
+
# This is different for each institution and inclues your site name provided by Apple.
|
|
68
125
|
def webservices_url
|
|
69
126
|
"#{API_URL}/ProcessWebServicesDocument/#{options[:site]}?#{self.generate_authorization_token}"
|
|
70
127
|
end
|
|
71
128
|
|
|
72
|
-
#
|
|
129
|
+
# The URL users should be redirected to access the iTunes U site.
|
|
73
130
|
def browse_url
|
|
74
131
|
"#{BROWSE_URL}/#{options[:site]}?#{self.generate_authorization_token}"
|
|
75
132
|
end
|
|
76
133
|
|
|
77
|
-
def show_tree
|
|
134
|
+
def show_tree(handle = nil, xml = nil)
|
|
78
135
|
url = URI.parse("#{SHOW_TREE_URL}/#{options[:site]}?#{self.generate_authorization_token}")
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
136
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
137
|
+
http.use_ssl = true
|
|
138
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
139
|
+
http.start {
|
|
140
|
+
request = Net::HTTP::Post.new(url.to_s)
|
|
141
|
+
response = http.request(request)
|
|
142
|
+
response.body
|
|
143
|
+
}
|
|
87
144
|
end
|
|
88
145
|
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
146
|
+
# Sends a string of XML data to iTunes U's webservices url for processing. Returns the iTunes U response XML.
|
|
147
|
+
# Used by Entity objects to send generated XML to iTunes U.
|
|
148
|
+
def process(xml, options = {})
|
|
149
|
+
url = URI.parse(webservices_url)
|
|
150
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
151
|
+
http.use_ssl = true
|
|
152
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
153
|
+
http.start {
|
|
154
|
+
request = Net::HTTP::Post.new(url.to_s)
|
|
155
|
+
request.body = xml
|
|
156
|
+
response = http.request(request)
|
|
157
|
+
response.body
|
|
158
|
+
}
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def upload_file(file, handle)
|
|
162
|
+
upload_url = upload_url_for_location(handle)
|
|
163
|
+
|
|
164
|
+
url = URI.parse(upload_url)
|
|
165
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
166
|
+
http.use_ssl = true
|
|
167
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
168
|
+
http.start {
|
|
169
|
+
request = Net::HTTP::Post.new(url.path)
|
|
170
|
+
body, headers = http_multipart_data({:file => file})
|
|
171
|
+
request.body = body
|
|
172
|
+
request.initialize_http_header(headers)
|
|
173
|
+
response = http.request(request)
|
|
174
|
+
response.body
|
|
175
|
+
}
|
|
103
176
|
end
|
|
104
177
|
|
|
105
|
-
#
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
178
|
+
# Retrieves and parses iTunesU usage logs.
|
|
179
|
+
# Options:
|
|
180
|
+
# * +:from+:: Specifies the date to retrieve logs from. Defaults to +Date.today+
|
|
181
|
+
# * +:to+:: (optional) Specifies the ending date of a date range: (:from)..(:to).
|
|
182
|
+
# * +:parse+:: Controls whether logs are parsed or returned raw.
|
|
183
|
+
#
|
|
184
|
+
# Example:
|
|
185
|
+
# connection.get_logs # Retrieves and parses logs from today
|
|
186
|
+
# connection.get_logs :from => Date.today-1 # Retrieves and parses logs from yesterday
|
|
187
|
+
# connection.get_logs :parse => false # Retrieves raw logs
|
|
188
|
+
# connection.get_logs :from => "2004-05-13", :to => "2004-06-12"
|
|
189
|
+
def get_logs(opt={})
|
|
190
|
+
opt[:from] ||= Date.today
|
|
191
|
+
#opt[:to] ||= Date.today - 1
|
|
192
|
+
opt[:parse] = opt[:parse].nil? ? true : opt[:parse]
|
|
193
|
+
|
|
194
|
+
#raise ":from date must preceed :to date." if opt[:to] && opt[:to] < opt[:from]
|
|
195
|
+
|
|
196
|
+
uri_str = "#{API_URL}/GetDailyReportLogs/#{options[:site]}?#{self.generate_authorization_token}"
|
|
197
|
+
uri_str << "&StartDate=#{opt[:from]}"
|
|
198
|
+
uri_str << "&EndDate=#{opt[:to]}" if opt[:to]
|
|
199
|
+
url = URI.parse uri_str
|
|
200
|
+
|
|
201
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
202
|
+
http.use_ssl = true
|
|
203
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
204
|
+
resp = http.start {
|
|
205
|
+
request = Net::HTTP::Get.new(url.to_s)
|
|
206
|
+
response = http.request(request)
|
|
207
|
+
response.body
|
|
208
|
+
}
|
|
209
|
+
if resp =~ /No.*?log data found/
|
|
210
|
+
return opt[:parse] ? [] : resp
|
|
115
211
|
end
|
|
212
|
+
|
|
213
|
+
return opt[:parse] ? Log.parse(resp) : resp
|
|
116
214
|
end
|
|
117
215
|
end
|
|
118
216
|
end
|
data/lib/rtunesu/document.rb
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
require 'builder'
|
|
2
2
|
|
|
3
3
|
module RTunesU
|
|
4
|
-
# Document is a class
|
|
4
|
+
# Document is a class for generating the neccessary XML to interact with iTunes U.
|
|
5
|
+
# Documents are generated and sent when calling .save, .create, .update, and .delete on
|
|
6
|
+
# specific Entity instances. Classes in the Document:: namespace aren't intended for direct use.
|
|
5
7
|
# For example:
|
|
6
|
-
# c = Course.find(12345
|
|
7
|
-
# c.
|
|
8
|
-
# c.
|
|
9
|
-
# c.save
|
|
10
|
-
module Document
|
|
8
|
+
# c = Course.find(12345)
|
|
9
|
+
# c.name # "exemple course"
|
|
10
|
+
# c.name = 'Example Course'
|
|
11
|
+
# c.save # genertes and sends a Document::Merge object with the Course data.
|
|
12
|
+
module Document # :nodoc:
|
|
11
13
|
class Base
|
|
12
14
|
INDENT = 2
|
|
13
15
|
attr_accessor :builder, :source, :options, :xml
|
|
@@ -26,30 +28,35 @@ module RTunesU
|
|
|
26
28
|
self
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
# Implemented in each of the subclasses of Document::Base. Adds the particular action you would like to take (AddFoo, MergeFoo, DeleteFoo) to the Source class.
|
|
30
|
-
# For example, if the source Entity is of the type Track and you are creating a Document::Add the ITunesUDocument element will have a child element of <AddTrack>...</AddTrack>
|
|
31
|
-
# tag_action is called from inside Document::Base.new and is based the initializer's builder object so that proper nesting is maintained.
|
|
32
31
|
private
|
|
32
|
+
# Implemented in each of the subclasses of Document::Base. Adds the particular action you would
|
|
33
|
+
# like to take (AddFoo, MergeFoo, DeleteFoo) to the Source class.
|
|
34
|
+
# For example, if the source Entity is of the type Track and you are creating a
|
|
35
|
+
# Document::Add the ITunesUDocument element will have a child element of <AddTrack>...</AddTrack>
|
|
36
|
+
# tag_action is called from inside Document::Base.new and is based the initializer's builder
|
|
37
|
+
# object so that proper nesting is maintained.
|
|
33
38
|
def tag_action(xml_builder)
|
|
34
39
|
return
|
|
35
40
|
end
|
|
36
41
|
end
|
|
37
42
|
|
|
38
|
-
# Creates an XML document that comforms to iTunes U's specification for adding an entity.
|
|
43
|
+
# Creates an XML document that comforms to iTunes U's specification for adding an entity.
|
|
44
|
+
# This class is used internally by Entity classes when saving.
|
|
39
45
|
class Add < Base
|
|
40
46
|
private
|
|
41
47
|
def tag_action(xml_builder)
|
|
42
48
|
raise MissingParent if source.parent_handle.nil?
|
|
43
49
|
xml_builder.tag!("Add#{source.class_name}") {
|
|
44
50
|
xml_builder.tag!('ParentHandle', source.parent_handle)
|
|
45
|
-
# The existance of ParentPath is required for
|
|
51
|
+
# The existance of ParentPath is required for iTunes U to validate a document, but it can be blank
|
|
46
52
|
xml_builder.tag!('ParentPath', '')
|
|
47
53
|
source.to_xml(xml_builder)
|
|
48
54
|
}
|
|
49
55
|
end
|
|
50
56
|
end
|
|
51
57
|
|
|
52
|
-
# Creates an XML document that comforms to iTunes U's specification for updating an entity.
|
|
58
|
+
# Creates an XML document that comforms to iTunes U's specification for updating an entity.
|
|
59
|
+
# This class is used internally by Entity classes when saving.
|
|
53
60
|
class Merge < Base
|
|
54
61
|
private
|
|
55
62
|
def tag_action(xml_builder)
|
|
@@ -60,7 +67,8 @@ module RTunesU
|
|
|
60
67
|
end
|
|
61
68
|
end
|
|
62
69
|
|
|
63
|
-
# Creates an XML document that comforms to iTunes U's specification for deleting an entity.
|
|
70
|
+
# Creates an XML document that comforms to iTunes U's specification for deleting an entity.
|
|
71
|
+
# This class is used internally by Entity classes when saving.
|
|
64
72
|
class Delete < Base
|
|
65
73
|
private
|
|
66
74
|
def tag_action(xml_builder)
|
|
@@ -70,18 +78,5 @@ module RTunesU
|
|
|
70
78
|
}
|
|
71
79
|
end
|
|
72
80
|
end
|
|
73
|
-
|
|
74
|
-
# Shows the hierarchy tree for a portion of your iTunes U site. If the source object has no handle, the tree for your entire site is shown.
|
|
75
|
-
# ShowTree.new can take an option options hash. The possible values for this hash are
|
|
76
|
-
# :key_group. They iTunes U 'KeyGroup' that defines the amount of data returned from the ShowTree action. Possible values are 'minimal','most','maximal'.
|
|
77
|
-
class ShowTree < Base
|
|
78
|
-
private
|
|
79
|
-
def tag_action(xml_builder)
|
|
80
|
-
xml_builder.tag!('ShowTree') {
|
|
81
|
-
xml_builder.tag!('Handle', self.source.handle) if self.source.handle
|
|
82
|
-
xml_builder.tag!('KeyGroup', options[:key_group] || 'most')
|
|
83
|
-
}
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
81
|
end
|
|
87
82
|
end
|
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
module RTunesU
|
|
2
2
|
# A Course in iTunes U.
|
|
3
|
-
# == Attributes
|
|
4
|
-
# Name
|
|
5
|
-
# Handle
|
|
6
|
-
# Instructor
|
|
7
|
-
# Description
|
|
8
|
-
# Identifier
|
|
9
|
-
# ThemeHandle
|
|
10
|
-
# ShortName
|
|
11
|
-
|
|
12
|
-
# == Nested Entities
|
|
13
|
-
# Permission
|
|
14
|
-
# Group
|
|
15
3
|
|
|
4
|
+
# ? LinkedFolderHandle
|
|
16
5
|
class Course < Entity
|
|
17
|
-
|
|
6
|
+
composed_of :name, :instructor, :description, :identifier, :theme_handle, :short_name, :allow_subscription
|
|
7
|
+
composed_of :aggregate_file_size, :readonly => true
|
|
8
|
+
has_n :permissions, :groups
|
|
18
9
|
end
|
|
19
10
|
end
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
module RTunesU
|
|
2
|
-
# A Division in iTunes U is a seperate nested area of your iTunes U Site.
|
|
3
|
-
#
|
|
4
|
-
# Handle
|
|
5
|
-
# Name
|
|
6
|
-
# ShortName
|
|
7
|
-
# AggregateFileSize (read only)
|
|
8
|
-
# Identifier
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# == Nested Entities
|
|
12
|
-
# Permission
|
|
13
|
-
# Section
|
|
2
|
+
# A Division in iTunes U is a seperate nested area of your iTunes U Site.
|
|
3
|
+
# It is different than a Section which is expressed in iTunes a a seperate area on a single page.
|
|
14
4
|
class Division < Entity
|
|
5
|
+
composed_of :name, :short_name, :identifier, :allow_subscription, :theme_handle, :description
|
|
6
|
+
composed_of :aggregate_file_size, :readonly => true
|
|
7
|
+
has_n :permissions, :sections
|
|
15
8
|
end
|
|
16
9
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module RTunesU
|
|
2
|
+
class ExternalFeed < Entity
|
|
3
|
+
composed_of :polling_interval, :owner_email, :security_type, :signature_type, :basic_auth_username, :basic_auth_password
|
|
4
|
+
composed_of :url, :as => 'URL'
|
|
5
|
+
validates! :polling_interval, [:never, :daily]
|
|
6
|
+
validates! :security_type, [:none, 'Basic Authentication']
|
|
7
|
+
|
|
8
|
+
# As an entirely nested entity ExternalFeed instances don't have a parent handle. However, something 'truthy' needs
|
|
9
|
+
# to be presnet for proper XML document genration.
|
|
10
|
+
def parent_handle # :nodoc:
|
|
11
|
+
""
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
module RTunesU
|
|
2
2
|
# A Group in iTunes U. A Group is expressed as a tab in the iTunes interface.
|
|
3
|
-
# == Attributes
|
|
4
|
-
# Name
|
|
5
|
-
# Handle
|
|
6
|
-
# GroupType
|
|
7
|
-
# ShortName
|
|
8
|
-
# AggregateFileSize
|
|
9
|
-
# AllowSubscription
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# == Nested Entities
|
|
13
|
-
# Permission
|
|
14
|
-
# Track
|
|
15
|
-
# SharedObjects
|
|
16
|
-
# ExternalFeed
|
|
17
3
|
|
|
18
4
|
class Group < Entity
|
|
5
|
+
composed_of :name, :group_type, :short_name, :allow_subscription, :explicit
|
|
6
|
+
composed_of :aggregate_file_size, :readonly => true
|
|
7
|
+
validates! :group_type, [:simple, :smart, :feed]
|
|
8
|
+
has_a :external_feed
|
|
9
|
+
has_n :permissions, :tracks, :shared_objects
|
|
19
10
|
end
|
|
20
11
|
end
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
module RTunesU
|
|
2
|
-
# A Section in iTunes U. A Section is expressed in the iTunes interface as a visually
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# Name
|
|
6
|
-
# AllowSubscription
|
|
7
|
-
# AggregateFileSize
|
|
8
|
-
#
|
|
9
|
-
# == Nested Entities
|
|
10
|
-
# Permission
|
|
11
|
-
# Course
|
|
12
|
-
# Division
|
|
2
|
+
# A Section in iTunes U. A Section is expressed in the iTunes interface as a visually
|
|
3
|
+
# seperate area on a single page. This is different than a Division which is a
|
|
4
|
+
# seperate nested area of iTunes U.
|
|
13
5
|
class Section < Entity
|
|
6
|
+
composed_of :name
|
|
7
|
+
has_n :courses
|
|
8
|
+
|
|
9
|
+
# Sections have additional required attributes for updating. These attributes *must* appear
|
|
10
|
+
# in a specific order.
|
|
11
|
+
# see http://deimos.apple.com/rsrc/doc/iTunesUAdministratorsGuide/iTunesUWebServices/chapter_17_section_12.html
|
|
12
|
+
def to_xml(xml_builder = Builder::XmlMarkup.new)
|
|
13
|
+
xml_builder.tag!("SectionPath", '')
|
|
14
|
+
xml_builder.tag!("MergeByHandle", "false")
|
|
15
|
+
xml_builder.tag!("Destructive", "false")
|
|
16
|
+
super
|
|
17
|
+
end
|
|
14
18
|
end
|
|
15
19
|
end
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
module RTunesU
|
|
2
2
|
# A Site in iTunes U.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
# AggregateFileSize
|
|
8
|
-
# ThemeHandle
|
|
9
|
-
# LoginURL
|
|
3
|
+
class Site < Entity
|
|
4
|
+
composed_of :name, :theme_handle, :allow_subscription
|
|
5
|
+
has_n :permissions, :sections, :templates
|
|
6
|
+
end
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# Theme
|
|
14
|
-
# LinkCollectionSet
|
|
15
|
-
# Section
|
|
16
|
-
class Site
|
|
8
|
+
def delete(connection)
|
|
9
|
+
raise "You cannot delete your entire site"
|
|
17
10
|
end
|
|
18
11
|
end
|
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
module RTunesU
|
|
2
2
|
# A visual theme for iTunesU pages. Color values are in 6 digit hex (e.g. #ffffff)
|
|
3
|
-
# == Attributes
|
|
4
|
-
# Name
|
|
5
|
-
# Handle
|
|
6
|
-
# BackgroundColor
|
|
7
|
-
# LineColor
|
|
8
|
-
# LinkArrowColor
|
|
9
|
-
# LinkBackgroundColor
|
|
10
|
-
# LinkBackgroundColorAlpha
|
|
11
|
-
# LinkBoxColor
|
|
12
|
-
# LinkTextColor
|
|
13
|
-
# LinkTitleColor
|
|
14
|
-
# RegularTextColor
|
|
15
|
-
# TitleTextColor
|
|
16
|
-
# TimeFormat
|
|
17
|
-
# DateFormat
|
|
18
3
|
class Theme < Entity
|
|
4
|
+
composed_of :background_color, :line_color, :link_arrow_color, :link_background_color,
|
|
5
|
+
:link_background_color_alpha, :link_box_color, :link_text_color, :link_title_color,
|
|
6
|
+
:regular_text_color, :title_text_color, :time_format, :date_format
|
|
19
7
|
end
|
|
20
8
|
end
|
|
@@ -1,33 +1,18 @@
|
|
|
1
1
|
module RTunesU
|
|
2
|
-
# A Track in iTunes U.
|
|
3
|
-
# == Attributes
|
|
4
|
-
# Name
|
|
5
|
-
# Handle
|
|
6
|
-
# Kind
|
|
7
|
-
# TrackNumber
|
|
8
|
-
# DiscNumber
|
|
9
|
-
# DurationMilliseconds
|
|
10
|
-
# AlbumName
|
|
11
|
-
# ArtistName
|
|
12
|
-
# GenreName
|
|
13
|
-
# DownloadURL
|
|
14
|
-
# Comment
|
|
15
2
|
class Track < Entity
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
composed_of :name, :kind, :disc_number, :album_name, :arist_name, :category_code, :explicit
|
|
4
|
+
composed_of :duration_in_milliseconds, :readonly => true
|
|
5
|
+
composed_of :downloadURL, :as => 'DownloadURL'
|
|
6
|
+
|
|
7
|
+
# Tracks can only be found from within their Course.
|
|
8
|
+
# There is currently (v1.1.3) no way to find a Track separete from its Course in iTunes U.
|
|
9
|
+
def self.find(handle, course_handle, connection = nil)
|
|
10
|
+
connection ||= self.base_connection
|
|
11
|
+
|
|
12
|
+
entity = self.new
|
|
13
|
+
entity.instance_variable_set('@handle', handle)
|
|
19
14
|
entity.source_xml = Course.find(course_handle, connection).source_xml.at("Handle[text()=#{entity.handle}]..")
|
|
20
15
|
entity
|
|
21
16
|
end
|
|
22
|
-
|
|
23
|
-
# Duration in millseconds is the one attribute in plural form that is not a collection
|
|
24
|
-
def DurationMilliseconds
|
|
25
|
-
self.value_from_edits_or_store('DurationMilliseconds')
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Duration in millseconds is the one attribute in plural form that is not a collection
|
|
29
|
-
def DurationMilliseconds=(duration)
|
|
30
|
-
self.edits['DurationMilliseconds'] = duration
|
|
31
|
-
end
|
|
32
17
|
end
|
|
33
|
-
end
|
|
18
|
+
end
|