rtunesu 0.2.4 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|