storageroom-to-contentful 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,24 @@
1
+ en:
2
+ fields:
3
+ input_type:
4
+ select: Symbol
5
+ date_picker: Date
6
+ time_picker: Date
7
+ location: Location
8
+ file: Asset
9
+ json_field: Object
10
+ radio: Boolean
11
+ checkbox: Boolean
12
+ array_field: Array
13
+ text_area: Text
14
+ markdown_editor: Text
15
+ IntegerField:
16
+ text_field: Integer
17
+ FloatField:
18
+ text_field: Number
19
+ StringField:
20
+ text_field: Text
21
+ OneAssociationField:
22
+ association_field: Entry
23
+ ManyAssociationField:
24
+ association_field: Array
data/credentials.yml ADDED
@@ -0,0 +1,7 @@
1
+ #Contentful
2
+ ACCESS_TOKEN: access_token
3
+ ORGANIZATION_ID: organization_id
4
+
5
+ #Storageroom
6
+ ACCOUNT_ID: account_id
7
+ APPLICATION_API_KEY: application_key_id
@@ -0,0 +1,262 @@
1
+ require_relative 'mime_content_type'
2
+ require 'contentful/management'
3
+
4
+ class ContentfulImporter
5
+ ENTRIES_IDS = []
6
+ attr_reader :space
7
+
8
+ def initialize
9
+ Contentful::Management::Client.new(CREDENTIALS['ACCESS_TOKEN'])
10
+ end
11
+
12
+ def create_space
13
+ puts 'Write your contentful name of space:'
14
+ name_space = gets.strip
15
+ @space = Contentful::Management::Space.create(name: name_space, organization_id: CREDENTIALS['ORGANIZATION_ID'])
16
+ end
17
+
18
+ def import_content_types
19
+ Dir.glob("#{COLLECTIONS_DATA_DIR}/*json") do |file_path|
20
+ collection_attributes = JSON.parse(File.read(file_path))
21
+ content_type = space.content_types.create(name: collection_attributes['entry_type'], description: collection_attributes['note'])
22
+ puts "Importing content_type: #{content_type.name}"
23
+ create_content_type_fields(collection_attributes, content_type)
24
+ create_content_type_webhooks(collection_attributes['webhook_definitions'], content_type.space.id)
25
+ add_content_type_id_to_file(collection_attributes, content_type.id, content_type.space.id, file_path)
26
+ active_status(content_type.activate)
27
+ end
28
+ end
29
+
30
+ def map_entries_ids
31
+ Dir.glob("#{ENTRIES_DATA_DIR}/**/*json") do |dir_path|
32
+ ENTRIES_IDS << File.basename(dir_path, '.json')
33
+ end
34
+ end
35
+
36
+ def import_entries
37
+ map_entries_ids
38
+ Dir.glob("#{ENTRIES_DATA_DIR}/*") do |dir_path|
39
+ collection_name = File.basename(dir_path)
40
+ puts "Importing entries for #{collection_name}."
41
+ collection_attributes = JSON.parse(File.read("#{COLLECTIONS_DATA_DIR}/#{collection_name}.json"))
42
+ content_type_id = collection_attributes['content_type_id']
43
+ space_id = collection_attributes['space_id']
44
+ import_entry(content_type_id, dir_path, space_id)
45
+ end
46
+ end
47
+
48
+ def publish_all_entries
49
+ Dir.glob("#{COLLECTIONS_DATA_DIR}/*json") do |dir_path|
50
+ collection_name = File.basename(dir_path, '.json')
51
+ puts "Publish entries for #{collection_name}."
52
+ collection_attributes = JSON.parse(File.read("#{COLLECTIONS_DATA_DIR}/#{collection_name}.json"))
53
+ Contentful::Management::Space.find(get_space_id(collection_attributes)).entries.all.each do |entry|
54
+ puts "Publish an entry with ID #{entry.id}."
55
+ active_status(entry.publish)
56
+ end
57
+ end
58
+ end
59
+
60
+ def find_symbol_type_in_collection
61
+ Dir.glob("#{COLLECTIONS_DATA_DIR}/*json") do |file_path|
62
+ collection_attributes = JSON.parse(File.read(file_path))
63
+ collection_attributes['fields'].each do |field|
64
+ find_symbol_attribute(collection_attributes, field)
65
+ end
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def get_space_id(collection)
72
+ collection['space_id']
73
+ end
74
+
75
+ def find_symbol_attribute(collection_attributes, field)
76
+ find_symbol_type_in_entry(collection_attributes, field) if field['input_type'] == 'Symbol'
77
+ end
78
+
79
+ def find_symbol_type_in_entry(collection_attributes, field)
80
+ select_id = field['identifier']
81
+ Dir.glob("#{ENTRIES_DATA_DIR}/#{collection_attributes['entry_type'].downcase}/*json") do |entry_path|
82
+ convert_params_from_symbol_in_entry_file(entry_path, select_id)
83
+ end
84
+ end
85
+
86
+ def convert_params_from_symbol_in_entry_file(entry_path, select_id)
87
+ entry_attributes = JSON.parse(File.read(entry_path))
88
+ value_of_select = entry_attributes["#{select_id}"]
89
+ parse_symbol_value_to_string(entry_path, value_of_select, select_id, entry_attributes) unless value_of_select.is_a? String
90
+ end
91
+
92
+ def parse_symbol_value_to_string(entry_path, value_of_select, select_id, entry_attributes)
93
+ entry_attributes["#{select_id}"] = value_of_select.to_s
94
+ File.open(entry_path, 'w') do |file|
95
+ file.write(format_json(entry_attributes))
96
+ end
97
+ end
98
+
99
+ def create_content_type_fields(collection_attributes, content_type)
100
+ collection_attributes['fields'].each do |field|
101
+ create_field(field, content_type)
102
+ end
103
+ end
104
+
105
+ def create_content_type_webhooks(params, space_id)
106
+ if params
107
+ params.each do |webhook|
108
+ Contentful::Management::Webhook.create(space_id, url: webhook['url'])
109
+ end
110
+ end
111
+ end
112
+
113
+ def import_entry(content_type_id, dir_path, space_id)
114
+ Dir.glob("#{dir_path}/*.json") do |file_path|
115
+ entry_attributes = JSON.parse(File.read(file_path))
116
+ entry_id = File.basename(file_path, '.json')
117
+ puts "Creating entry: #{entry_id}."
118
+ entry_params = create_entry_parameters(content_type_id, entry_attributes, space_id)
119
+ entry = content_type(content_type_id, space_id).entries.create(entry_params.merge(id: entry_id))
120
+ import_status(entry)
121
+ end
122
+ end
123
+
124
+ def create_entry_parameters(content_type_id, entry_attributes, space_id)
125
+ entry_attributes.each_with_object({}) do |(attr, value), entry_params|
126
+ next if attr.start_with?('@')
127
+ entry_param = if value.is_a? Hash
128
+ parse_attributes_from_hash(value, space_id, content_type_id)
129
+ elsif value.is_a? Array
130
+ parse_attributes_from_array(value, space_id, content_type_id)
131
+ else
132
+ value
133
+ end
134
+ entry_params[attr.to_sym] = entry_param unless validate_param(entry_param)
135
+ end
136
+ end
137
+
138
+ def parse_attributes_from_hash(params, space_id, content_type_id)
139
+ type = params['@type']
140
+ if type
141
+ case type
142
+ when 'Location'
143
+ create_location_file(params)
144
+ when 'File'
145
+ create_asset(space_id, params)
146
+ when 'Image'
147
+ create_asset(space_id, params)
148
+ else
149
+ create_entry(params, space_id, content_type_id)
150
+ end
151
+ else
152
+ params
153
+ end
154
+ end
155
+
156
+ def parse_attributes_from_array(params, space_id, content_type_id)
157
+ params.each_with_object([]) do |attr, array_attributes|
158
+ value = if attr['@type']
159
+ create_entry(attr, space_id, content_type_id)
160
+ else
161
+ attr
162
+ end
163
+ array_attributes << value unless value.nil?
164
+ end
165
+ end
166
+
167
+ def import_status(entry)
168
+ if entry.is_a? Contentful::Management::Entry
169
+ puts 'Imported successfully!'
170
+ else
171
+ puts "### Failure! - #{entry.message} ###"
172
+ end
173
+ end
174
+
175
+ def content_type(content_type_id, space_id)
176
+ Contentful::Management::ContentType.find(space_id, content_type_id)
177
+ end
178
+
179
+ def add_content_type_id_to_file(collection, content_type_id, space_id, file_path)
180
+ File.open(file_path, 'w') { |file| file.write(format_json(collection.merge(content_type_id: content_type_id, space_id: space_id))) }
181
+ end
182
+
183
+ def create_entry(params, space_id, content_type_id)
184
+ entry_id = get_id(params)
185
+ if ENTRIES_IDS.include? entry_id
186
+ content_type = Contentful::Management::ContentType.find(space_id, content_type_id)
187
+ content_type.entries.new.tap do |entry|
188
+ entry.id = entry_id
189
+ end
190
+ end
191
+ end
192
+
193
+ def get_id(params)
194
+ File.basename(params['@url'] || params['url'])
195
+ end
196
+
197
+ def create_asset(space_id, params)
198
+ asset_file = Contentful::Management::File.new.tap do |file|
199
+ file.properties[:contentType] = file_content_type(params)
200
+ file.properties[:fileName] = params['@type']
201
+ file.properties[:upload] = params['@url']
202
+ end
203
+ space = Contentful::Management::Space.find(space_id)
204
+ space.assets.create(title: "#{params['@type']}", description: '', file: asset_file).process_file
205
+ end
206
+
207
+ def create_location_file(params)
208
+ Contentful::Management::Location.new.tap do |file|
209
+ file.lat = params['lat']
210
+ file.lon = params['lng']
211
+ end
212
+ end
213
+
214
+ def create_field(field, content_type)
215
+ field_params = {id: field['identifier'], name: field['name'], required: field['required']}
216
+ field_params.merge!(additional_field_params(field))
217
+ puts "Creating field: #{field_params[:type]}"
218
+ content_type.fields.create(field_params)
219
+ end
220
+
221
+ def active_status(ct_object)
222
+ if ct_object.is_a? Contentful::Management::Error
223
+ puts "### Failure! - #{ct_object.message} ! ###"
224
+ else
225
+ puts 'Successfully activated!'
226
+ end
227
+ end
228
+
229
+ def additional_field_params(field)
230
+ field_type = field['input_type']
231
+ if field_type == 'Entry' || field_type == 'Asset'
232
+ {type: 'Link', link_type: field_type}
233
+ elsif field_type == 'Array'
234
+ {type: 'Array', items: create_array_field(field)}
235
+ else
236
+ {type: field_type}
237
+ end
238
+ end
239
+
240
+ def validate_param(param)
241
+ if param.is_a? Array
242
+ param.empty?
243
+ else
244
+ param.nil?
245
+ end
246
+ end
247
+
248
+ def file_content_type(params)
249
+ MimeContentType::EXTENSION_LIST[File.extname(params['@url'])]
250
+ end
251
+
252
+ def format_json(item)
253
+ JSON.pretty_generate(JSON.parse(item.to_json))
254
+ end
255
+
256
+ def create_array_field(params)
257
+ Contentful::Management::Field.new.tap do |field|
258
+ field.type = params['link'] || 'Link'
259
+ field.link_type = params['link_type']
260
+ end
261
+ end
262
+ end
data/lib/migrator.rb ADDED
@@ -0,0 +1,46 @@
1
+ require_relative 'storage_room_exporter'
2
+ require_relative 'contentful_importer'
3
+ require 'contentful/management'
4
+ require 'fileutils'
5
+
6
+ class Migrator
7
+ attr_reader :storage_room_exporter, :contentful_importer
8
+
9
+ MESSAGE = <<-eoruby
10
+ Actions:
11
+ 1. Export data from StorageRoom to JSON files.
12
+ 2. Convert StorageRoom field types to Contentful.
13
+ 3. Import collections to Contentful.
14
+ 4. Import entries to Contentful.
15
+ 5. Publish all entries on Contentful.
16
+ -> Choose on of the options:
17
+ eoruby
18
+
19
+ def run
20
+ puts MESSAGE
21
+ action_choice = gets.to_i
22
+ case action_choice
23
+ when 1
24
+ storage_room_exporter.export_collections
25
+ storage_room_exporter.export_entries
26
+ when 2
27
+ storage_room_exporter.mapping_collections_input_types
28
+ when 3
29
+ contentful_importer.create_space
30
+ contentful_importer.import_content_types
31
+ contentful_importer.find_symbol_type_in_collection
32
+ when 4
33
+ contentful_importer.import_entries
34
+ when 5
35
+ contentful_importer.publish_all_entries
36
+ end
37
+ end
38
+
39
+ def storage_room_exporter
40
+ @storage_room_exporter ||= StorageRoomExporter.new
41
+ end
42
+
43
+ def contentful_importer
44
+ @contentful_importer ||= ContentfulImporter.new
45
+ end
46
+ end
@@ -0,0 +1,562 @@
1
+ class MimeContentType
2
+ EXTENSION_LIST = {'.323' => 'text/h323',
3
+ '.3g2' => 'video/3gpp2',
4
+ '.3gp' => 'video/3gpp',
5
+ '.3gp2' => 'video/3gpp2',
6
+ '.3gpp' => 'video/3gpp',
7
+ '.7z' => 'application/x-7z-compressed',
8
+ '.aa' => 'audio/audible',
9
+ '.AAC' => 'audio/aac',
10
+ '.aaf' => 'application/octet-stream',
11
+ '.aax' => 'audio/vnd.audible.aax',
12
+ '.ac3' => 'audio/ac3',
13
+ '.aca' => 'application/octet-stream',
14
+ '.accda' => 'application/msaccess.addin',
15
+ '.accdb' => 'application/msaccess',
16
+ '.accdc' => 'application/msaccess.cab',
17
+ '.accde' => 'application/msaccess',
18
+ '.accdr' => 'application/msaccess.runtime',
19
+ '.accdt' => 'application/msaccess',
20
+ '.accdw' => 'application/msaccess.webapplication',
21
+ '.accft' => 'application/msaccess.ftemplate',
22
+ '.acx' => 'application/internet-property-stream',
23
+ '.AddIn' => 'text/xml',
24
+ '.ade' => 'application/msaccess',
25
+ '.adobebridge' => 'application/x-bridge-url',
26
+ '.adp' => 'application/msaccess',
27
+ '.ADT' => 'audio/vnd.dlna.adts',
28
+ '.ADTS' => 'audio/aac',
29
+ '.afm' => 'application/octet-stream',
30
+ '.ai' => 'application/postscript',
31
+ '.aif' => 'audio/x-aiff',
32
+ '.aifc' => 'audio/aiff',
33
+ '.aiff' => 'audio/aiff',
34
+ '.air' => 'application/vnd.adobe.air-application-installer-package+zip',
35
+ '.amc' => 'application/x-mpeg',
36
+ '.application' => 'application/x-ms-application',
37
+ '.art' => 'image/x-jg',
38
+ '.asa' => 'application/xml',
39
+ '.asax' => 'application/xml',
40
+ '.ascx' => 'application/xml',
41
+ '.asd' => 'application/octet-stream',
42
+ '.asf' => 'video/x-ms-asf',
43
+ '.ashx' => 'application/xml',
44
+ '.asi' => 'application/octet-stream',
45
+ '.asm' => 'text/plain',
46
+ '.asmx' => 'application/xml',
47
+ '.aspx' => 'application/xml',
48
+ '.asr' => 'video/x-ms-asf',
49
+ '.asx' => 'video/x-ms-asf',
50
+ '.atom' => 'application/atom+xml',
51
+ '.au' => 'audio/basic',
52
+ '.avi' => 'video/x-msvideo',
53
+ '.axs' => 'application/olescript',
54
+ '.bas' => 'text/plain',
55
+ '.bcpio' => 'application/x-bcpio',
56
+ '.bin' => 'application/octet-stream',
57
+ '.bmp' => 'image/bmp',
58
+ '.c' => 'text/plain',
59
+ '.cab' => 'application/octet-stream',
60
+ '.caf' => 'audio/x-caf',
61
+ '.calx' => 'application/vnd.ms-office.calx',
62
+ '.cat' => 'application/vnd.ms-pki.seccat',
63
+ '.cc' => 'text/plain',
64
+ '.cd' => 'text/plain',
65
+ '.cdda' => 'audio/aiff',
66
+ '.cdf' => 'application/x-cdf',
67
+ '.cer' => 'application/x-x509-ca-cert',
68
+ '.chm' => 'application/octet-stream',
69
+ '.class' => 'application/x-java-applet',
70
+ '.clp' => 'application/x-msclip',
71
+ '.cmx' => 'image/x-cmx',
72
+ '.cnf' => 'text/plain',
73
+ '.cod' => 'image/cis-cod',
74
+ '.config' => 'application/xml',
75
+ '.contact' => 'text/x-ms-contact',
76
+ '.coverage' => 'application/xml',
77
+ '.cpio' => 'application/x-cpio',
78
+ '.cpp' => 'text/plain',
79
+ '.crd' => 'application/x-mscardfile',
80
+ '.crl' => 'application/pkix-crl',
81
+ '.crt' => 'application/x-x509-ca-cert',
82
+ '.cs' => 'text/plain',
83
+ '.csdproj' => 'text/plain',
84
+ '.csh' => 'application/x-csh',
85
+ '.csproj' => 'text/plain',
86
+ '.css' => 'text/css',
87
+ '.csv' => 'text/csv',
88
+ '.cur' => 'application/octet-stream',
89
+ '.cxx' => 'text/plain',
90
+ '.dat' => 'application/octet-stream',
91
+ '.datasource' => 'application/xml',
92
+ '.dbproj' => 'text/plain',
93
+ '.dcr' => 'application/x-director',
94
+ '.def' => 'text/plain',
95
+ '.deploy' => 'application/octet-stream',
96
+ '.der' => 'application/x-x509-ca-cert',
97
+ '.dgml' => 'application/xml',
98
+ '.dib' => 'image/bmp',
99
+ '.dif' => 'video/x-dv',
100
+ '.dir' => 'application/x-director',
101
+ '.disco' => 'text/xml',
102
+ '.dll' => 'application/x-msdownload',
103
+ '.dll.config' => 'text/xml',
104
+ '.dlm' => 'text/dlm',
105
+ '.doc' => 'application/msword',
106
+ '.docm' => 'application/vnd.ms-word.document.macroEnabled.12',
107
+ '.docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
108
+ '.dot' => 'application/msword',
109
+ '.dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
110
+ '.dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
111
+ '.dsp' => 'application/octet-stream',
112
+ '.dsw' => 'text/plain',
113
+ '.dtd' => 'text/xml',
114
+ '.dtsConfig' => 'text/xml',
115
+ '.dv' => 'video/x-dv',
116
+ '.dvi' => 'application/x-dvi',
117
+ '.dwf' => 'drawing/x-dwf',
118
+ '.dwp' => 'application/octet-stream',
119
+ '.dxr' => 'application/x-director',
120
+ '.eml' => 'message/rfc822',
121
+ '.emz' => 'application/octet-stream',
122
+ '.eot' => 'application/octet-stream',
123
+ '.eps' => 'application/postscript',
124
+ '.etl' => 'application/etl',
125
+ '.etx' => 'text/x-setext',
126
+ '.evy' => 'application/envoy',
127
+ '.exe' => 'application/octet-stream',
128
+ '.exe.config' => 'text/xml',
129
+ '.fdf' => 'application/vnd.fdf',
130
+ '.fif' => 'application/fractals',
131
+ '.filters' => 'Application/xml',
132
+ '.fla' => 'application/octet-stream',
133
+ '.flr' => 'x-world/x-vrml',
134
+ '.flv' => 'video/x-flv',
135
+ '.fsscript' => 'application/fsharp-script',
136
+ '.fsx' => 'application/fsharp-script',
137
+ '.generictest' => 'application/xml',
138
+ '.gif' => 'image/gif',
139
+ '.group' => 'text/x-ms-group',
140
+ '.gsm' => 'audio/x-gsm',
141
+ '.gtar' => 'application/x-gtar',
142
+ '.gz' => 'application/x-gzip',
143
+ '.h' => 'text/plain',
144
+ '.hdf' => 'application/x-hdf',
145
+ '.hdml' => 'text/x-hdml',
146
+ '.hhc' => 'application/x-oleobject',
147
+ '.hhk' => 'application/octet-stream',
148
+ '.hhp' => 'application/octet-stream',
149
+ '.hlp' => 'application/winhlp',
150
+ '.hpp' => 'text/plain',
151
+ '.hqx' => 'application/mac-binhex40',
152
+ '.hta' => 'application/hta',
153
+ '.htc' => 'text/x-component',
154
+ '.htm' => 'text/html',
155
+ '.html' => 'text/html',
156
+ '.htt' => 'text/webviewhtml',
157
+ '.hxa' => 'application/xml',
158
+ '.hxc' => 'application/xml',
159
+ '.hxd' => 'application/octet-stream',
160
+ '.hxe' => 'application/xml',
161
+ '.hxf' => 'application/xml',
162
+ '.hxh' => 'application/octet-stream',
163
+ '.hxi' => 'application/octet-stream',
164
+ '.hxk' => 'application/xml',
165
+ '.hxq' => 'application/octet-stream',
166
+ '.hxr' => 'application/octet-stream',
167
+ '.hxs' => 'application/octet-stream',
168
+ '.hxt' => 'text/html',
169
+ '.hxv' => 'application/xml',
170
+ '.hxw' => 'application/octet-stream',
171
+ '.hxx' => 'text/plain',
172
+ '.i' => 'text/plain',
173
+ '.ico' => 'image/x-icon',
174
+ '.ics' => 'application/octet-stream',
175
+ '.idl' => 'text/plain',
176
+ '.ief' => 'image/ief',
177
+ '.iii' => 'application/x-iphone',
178
+ '.inc' => 'text/plain',
179
+ '.inf' => 'application/octet-stream',
180
+ '.inl' => 'text/plain',
181
+ '.ins' => 'application/x-internet-signup',
182
+ '.ipa' => 'application/x-itunes-ipa',
183
+ '.ipg' => 'application/x-itunes-ipg',
184
+ '.ipproj' => 'text/plain',
185
+ '.ipsw' => 'application/x-itunes-ipsw',
186
+ '.iqy' => 'text/x-ms-iqy',
187
+ '.isp' => 'application/x-internet-signup',
188
+ '.ite' => 'application/x-itunes-ite',
189
+ '.itlp' => 'application/x-itunes-itlp',
190
+ '.itms' => 'application/x-itunes-itms',
191
+ '.itpc' => 'application/x-itunes-itpc',
192
+ '.IVF' => 'video/x-ivf',
193
+ '.jar' => 'application/java-archive',
194
+ '.java' => 'application/octet-stream',
195
+ '.jck' => 'application/liquidmotion',
196
+ '.jcz' => 'application/liquidmotion',
197
+ '.jfif' => 'image/pjpeg',
198
+ '.jnlp' => 'application/x-java-jnlp-file',
199
+ '.jpb' => 'application/octet-stream',
200
+ '.jpe' => 'image/jpeg',
201
+ '.jpeg' => 'image/jpeg',
202
+ '.jpg' => 'image/jpeg',
203
+ '.js' => 'application/x-javascript',
204
+ '.jsx' => 'text/jscript',
205
+ '.jsxbin' => 'text/plain',
206
+ '.latex' => 'application/x-latex',
207
+ '.library-ms' => 'application/windows-library+xml',
208
+ '.lit' => 'application/x-ms-reader',
209
+ '.loadtest' => 'application/xml',
210
+ '.lpk' => 'application/octet-stream',
211
+ '.lsf' => 'video/x-la-asf',
212
+ '.lst' => 'text/plain',
213
+ '.lsx' => 'video/x-la-asf',
214
+ '.lzh' => 'application/octet-stream',
215
+ '.m13' => 'application/x-msmediaview',
216
+ '.m14' => 'application/x-msmediaview',
217
+ '.m1v' => 'video/mpeg',
218
+ '.m2t' => 'video/vnd.dlna.mpeg-tts',
219
+ '.m2ts' => 'video/vnd.dlna.mpeg-tts',
220
+ '.m2v' => 'video/mpeg',
221
+ '.m3u' => 'audio/x-mpegurl',
222
+ '.m3u8' => 'audio/x-mpegurl',
223
+ '.m4a' => 'audio/m4a',
224
+ '.m4b' => 'audio/m4b',
225
+ '.m4p' => 'audio/m4p',
226
+ '.m4r' => 'audio/x-m4r',
227
+ '.m4v' => 'video/x-m4v',
228
+ '.mac' => 'image/x-macpaint',
229
+ '.mak' => 'text/plain',
230
+ '.man' => 'application/x-troff-man',
231
+ '.manifest' => 'application/x-ms-manifest',
232
+ '.map' => 'text/plain',
233
+ '.master' => 'application/xml',
234
+ '.mda' => 'application/msaccess',
235
+ '.mdb' => 'application/x-msaccess',
236
+ '.mde' => 'application/msaccess',
237
+ '.mdp' => 'application/octet-stream',
238
+ '.me' => 'application/x-troff-me',
239
+ '.mfp' => 'application/x-shockwave-flash',
240
+ '.mht' => 'message/rfc822',
241
+ '.mhtml' => 'message/rfc822',
242
+ '.mid' => 'audio/mid',
243
+ '.midi' => 'audio/mid',
244
+ '.mix' => 'application/octet-stream',
245
+ '.mk' => 'text/plain',
246
+ '.mmf' => 'application/x-smaf',
247
+ '.mno' => 'text/xml',
248
+ '.mny' => 'application/x-msmoney',
249
+ '.mod' => 'video/mpeg',
250
+ '.mov' => 'video/quicktime',
251
+ '.movie' => 'video/x-sgi-movie',
252
+ '.mp2' => 'video/mpeg',
253
+ '.mp2v' => 'video/mpeg',
254
+ '.mp3' => 'audio/mpeg',
255
+ '.mp4' => 'video/mp4',
256
+ '.mp4v' => 'video/mp4',
257
+ '.mpa' => 'video/mpeg',
258
+ '.mpe' => 'video/mpeg',
259
+ '.mpeg' => 'video/mpeg',
260
+ '.mpf' => 'application/vnd.ms-mediapackage',
261
+ '.mpg' => 'video/mpeg',
262
+ '.mpp' => 'application/vnd.ms-project',
263
+ '.mpv2' => 'video/mpeg',
264
+ '.mqv' => 'video/quicktime',
265
+ '.ms' => 'application/x-troff-ms',
266
+ '.msi' => 'application/octet-stream',
267
+ '.mso' => 'application/octet-stream',
268
+ '.mts' => 'video/vnd.dlna.mpeg-tts',
269
+ '.mtx' => 'application/xml',
270
+ '.mvb' => 'application/x-msmediaview',
271
+ '.mvc' => 'application/x-miva-compiled',
272
+ '.mxp' => 'application/x-mmxp',
273
+ '.nc' => 'application/x-netcdf',
274
+ '.nsc' => 'video/x-ms-asf',
275
+ '.nws' => 'message/rfc822',
276
+ '.ocx' => 'application/octet-stream',
277
+ '.oda' => 'application/oda',
278
+ '.odc' => 'text/x-ms-odc',
279
+ '.odh' => 'text/plain',
280
+ '.odl' => 'text/plain',
281
+ '.odp' => 'application/vnd.oasis.opendocument.presentation',
282
+ '.ods' => 'application/oleobject',
283
+ '.odt' => 'application/vnd.oasis.opendocument.text',
284
+ '.one' => 'application/onenote',
285
+ '.onea' => 'application/onenote',
286
+ '.onepkg' => 'application/onenote',
287
+ '.onetmp' => 'application/onenote',
288
+ '.onetoc' => 'application/onenote',
289
+ '.onetoc2' => 'application/onenote',
290
+ '.orderedtest' => 'application/xml',
291
+ '.osdx' => 'application/opensearchdescription+xml',
292
+ '.p10' => 'application/pkcs10',
293
+ '.p12' => 'application/x-pkcs12',
294
+ '.p7b' => 'application/x-pkcs7-certificates',
295
+ '.p7c' => 'application/pkcs7-mime',
296
+ '.p7m' => 'application/pkcs7-mime',
297
+ '.p7r' => 'application/x-pkcs7-certreqresp',
298
+ '.p7s' => 'application/pkcs7-signature',
299
+ '.pbm' => 'image/x-portable-bitmap',
300
+ '.pcast' => 'application/x-podcast',
301
+ '.pct' => 'image/pict',
302
+ '.pcx' => 'application/octet-stream',
303
+ '.pcz' => 'application/octet-stream',
304
+ '.pdf' => 'application/pdf',
305
+ '.pfb' => 'application/octet-stream',
306
+ '.pfm' => 'application/octet-stream',
307
+ '.pfx' => 'application/x-pkcs12',
308
+ '.pgm' => 'image/x-portable-graymap',
309
+ '.pic' => 'image/pict',
310
+ '.pict' => 'image/pict',
311
+ '.pkgdef' => 'text/plain',
312
+ '.pkgundef' => 'text/plain',
313
+ '.pko' => 'application/vnd.ms-pki.pko',
314
+ '.pls' => 'audio/scpls',
315
+ '.pma' => 'application/x-perfmon',
316
+ '.pmc' => 'application/x-perfmon',
317
+ '.pml' => 'application/x-perfmon',
318
+ '.pmr' => 'application/x-perfmon',
319
+ '.pmw' => 'application/x-perfmon',
320
+ '.png' => 'image/png',
321
+ '.pnm' => 'image/x-portable-anymap',
322
+ '.pnt' => 'image/x-macpaint',
323
+ '.pntg' => 'image/x-macpaint',
324
+ '.pnz' => 'image/png',
325
+ '.pot' => 'application/vnd.ms-powerpoint',
326
+ '.potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
327
+ '.potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
328
+ '.ppa' => 'application/vnd.ms-powerpoint',
329
+ '.ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
330
+ '.ppm' => 'image/x-portable-pixmap',
331
+ '.pps' => 'application/vnd.ms-powerpoint',
332
+ '.ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
333
+ '.ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
334
+ '.ppt' => 'application/vnd.ms-powerpoint',
335
+ '.pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
336
+ '.pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
337
+ '.prf' => 'application/pics-rules',
338
+ '.prm' => 'application/octet-stream',
339
+ '.prx' => 'application/octet-stream',
340
+ '.ps' => 'application/postscript',
341
+ '.psc1' => 'application/PowerShell',
342
+ '.psd' => 'application/octet-stream',
343
+ '.psess' => 'application/xml',
344
+ '.psm' => 'application/octet-stream',
345
+ '.psp' => 'application/octet-stream',
346
+ '.pub' => 'application/x-mspublisher',
347
+ '.pwz' => 'application/vnd.ms-powerpoint',
348
+ '.qht' => 'text/x-html-insertion',
349
+ '.qhtm' => 'text/x-html-insertion',
350
+ '.qt' => 'video/quicktime',
351
+ '.qti' => 'image/x-quicktime',
352
+ '.qtif' => 'image/x-quicktime',
353
+ '.qtl' => 'application/x-quicktimeplayer',
354
+ '.qxd' => 'application/octet-stream',
355
+ '.ra' => 'audio/x-pn-realaudio',
356
+ '.ram' => 'audio/x-pn-realaudio',
357
+ '.rar' => 'application/octet-stream',
358
+ '.ras' => 'image/x-cmu-raster',
359
+ '.rat' => 'application/rat-file',
360
+ '.rc' => 'text/plain',
361
+ '.rc2' => 'text/plain',
362
+ '.rct' => 'text/plain',
363
+ '.rdlc' => 'application/xml',
364
+ '.resx' => 'application/xml',
365
+ '.rf' => 'image/vnd.rn-realflash',
366
+ '.rgb' => 'image/x-rgb',
367
+ '.rgs' => 'text/plain',
368
+ '.rm' => 'application/vnd.rn-realmedia',
369
+ '.rmi' => 'audio/mid',
370
+ '.rmp' => 'application/vnd.rn-rn_music_package',
371
+ '.roff' => 'application/x-troff',
372
+ '.rpm' => 'audio/x-pn-realaudio-plugin',
373
+ '.rqy' => 'text/x-ms-rqy',
374
+ '.rtf' => 'application/rtf',
375
+ '.rtx' => 'text/richtext',
376
+ '.ruleset' => 'application/xml',
377
+ '.s' => 'text/plain',
378
+ '.safariextz' => 'application/x-safari-safariextz',
379
+ '.scd' => 'application/x-msschedule',
380
+ '.sct' => 'text/scriptlet',
381
+ '.sd2' => 'audio/x-sd2',
382
+ '.sdp' => 'application/sdp',
383
+ '.sea' => 'application/octet-stream',
384
+ '.searchConnector-ms' => 'application/windows-search-connector+xml',
385
+ '.setpay' => 'application/set-payment-initiation',
386
+ '.setreg' => 'application/set-registration-initiation',
387
+ '.settings' => 'application/xml',
388
+ '.sgimb' => 'application/x-sgimb',
389
+ '.sgml' => 'text/sgml',
390
+ '.sh' => 'application/x-sh',
391
+ '.shar' => 'application/x-shar',
392
+ '.shtml' => 'text/html',
393
+ '.sit' => 'application/x-stuffit',
394
+ '.sitemap' => 'application/xml',
395
+ '.skin' => 'application/xml',
396
+ '.sldm' => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
397
+ '.sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
398
+ '.slk' => 'application/vnd.ms-excel',
399
+ '.sln' => 'text/plain',
400
+ '.slupkg-ms' => 'application/x-ms-license',
401
+ '.smd' => 'audio/x-smd',
402
+ '.smi' => 'application/octet-stream',
403
+ '.smx' => 'audio/x-smd',
404
+ '.smz' => 'audio/x-smd',
405
+ '.snd' => 'audio/basic',
406
+ '.snippet' => 'application/xml',
407
+ '.snp' => 'application/octet-stream',
408
+ '.sol' => 'text/plain',
409
+ '.sor' => 'text/plain',
410
+ '.spc' => 'application/x-pkcs7-certificates',
411
+ '.spl' => 'application/futuresplash',
412
+ '.src' => 'application/x-wais-source',
413
+ '.srf' => 'text/plain',
414
+ '.SSISDeploymentManifest' => 'text/xml',
415
+ '.ssm' => 'application/streamingmedia',
416
+ '.sst' => 'application/vnd.ms-pki.certstore',
417
+ '.stl' => 'application/vnd.ms-pki.stl',
418
+ '.sv4cpio' => 'application/x-sv4cpio',
419
+ '.sv4crc' => 'application/x-sv4crc',
420
+ '.svc' => 'application/xml',
421
+ '.swf' => 'application/x-shockwave-flash',
422
+ '.t' => 'application/x-troff',
423
+ '.tar' => 'application/x-tar',
424
+ '.tcl' => 'application/x-tcl',
425
+ '.testrunconfig' => 'application/xml',
426
+ '.testsettings' => 'application/xml',
427
+ '.tex' => 'application/x-tex',
428
+ '.texi' => 'application/x-texinfo',
429
+ '.texinfo' => 'application/x-texinfo',
430
+ '.tgz' => 'application/x-compressed',
431
+ '.thmx' => 'application/vnd.ms-officetheme',
432
+ '.thn' => 'application/octet-stream',
433
+ '.tif' => 'image/tiff',
434
+ '.tiff' => 'image/tiff',
435
+ '.tlh' => 'text/plain',
436
+ '.tli' => 'text/plain',
437
+ '.toc' => 'application/octet-stream',
438
+ '.tr' => 'application/x-troff',
439
+ '.trm' => 'application/x-msterminal',
440
+ '.trx' => 'application/xml',
441
+ '.ts' => 'video/vnd.dlna.mpeg-tts',
442
+ '.tsv' => 'text/tab-separated-values',
443
+ '.ttf' => 'application/octet-stream',
444
+ '.tts' => 'video/vnd.dlna.mpeg-tts',
445
+ '.txt' => 'text/plain',
446
+ '.u32' => 'application/octet-stream',
447
+ '.uls' => 'text/iuls',
448
+ '.user' => 'text/plain',
449
+ '.ustar' => 'application/x-ustar',
450
+ '.vb' => 'text/plain',
451
+ '.vbdproj' => 'text/plain',
452
+ '.vbk' => 'video/mpeg',
453
+ '.vbproj' => 'text/plain',
454
+ '.vbs' => 'text/vbscript',
455
+ '.vcf' => 'text/x-vcard',
456
+ '.vcproj' => 'Application/xml',
457
+ '.vcs' => 'text/plain',
458
+ '.vcxproj' => 'Application/xml',
459
+ '.vddproj' => 'text/plain',
460
+ '.vdp' => 'text/plain',
461
+ '.vdproj' => 'text/plain',
462
+ '.vdx' => 'application/vnd.ms-visio.viewer',
463
+ '.vml' => 'text/xml',
464
+ '.vscontent' => 'application/xml',
465
+ '.vsct' => 'text/xml',
466
+ '.vsd' => 'application/vnd.visio',
467
+ '.vsi' => 'application/ms-vsi',
468
+ '.vsix' => 'application/vsix',
469
+ '.vsixlangpack' => 'text/xml',
470
+ '.vsixmanifest' => 'text/xml',
471
+ '.vsmdi' => 'application/xml',
472
+ '.vspscc' => 'text/plain',
473
+ '.vss' => 'application/vnd.visio',
474
+ '.vsscc' => 'text/plain',
475
+ '.vssettings' => 'text/xml',
476
+ '.vssscc' => 'text/plain',
477
+ '.vst' => 'application/vnd.visio',
478
+ '.vstemplate' => 'text/xml',
479
+ '.vsto' => 'application/x-ms-vsto',
480
+ '.vsw' => 'application/vnd.visio',
481
+ '.vsx' => 'application/vnd.visio',
482
+ '.vtx' => 'application/vnd.visio',
483
+ '.wav' => 'audio/wav',
484
+ '.wave' => 'audio/wav',
485
+ '.wax' => 'audio/x-ms-wax',
486
+ '.wbk' => 'application/msword',
487
+ '.wbmp' => 'image/vnd.wap.wbmp',
488
+ '.wcm' => 'application/vnd.ms-works',
489
+ '.wdb' => 'application/vnd.ms-works',
490
+ '.wdp' => 'image/vnd.ms-photo',
491
+ '.webarchive' => 'application/x-safari-webarchive',
492
+ '.webtest' => 'application/xml',
493
+ '.wiq' => 'application/xml',
494
+ '.wiz' => 'application/msword',
495
+ '.wks' => 'application/vnd.ms-works',
496
+ '.WLMP' => 'application/wlmoviemaker',
497
+ '.wlpginstall' => 'application/x-wlpg-detect',
498
+ '.wlpginstall3' => 'application/x-wlpg3-detect',
499
+ '.wm' => 'video/x-ms-wm',
500
+ '.wma' => 'audio/x-ms-wma',
501
+ '.wmd' => 'application/x-ms-wmd',
502
+ '.wmf' => 'application/x-msmetafile',
503
+ '.wml' => 'text/vnd.wap.wml',
504
+ '.wmlc' => 'application/vnd.wap.wmlc',
505
+ '.wmls' => 'text/vnd.wap.wmlscript',
506
+ '.wmlsc' => 'application/vnd.wap.wmlscriptc',
507
+ '.wmp' => 'video/x-ms-wmp',
508
+ '.wmv' => 'video/x-ms-wmv',
509
+ '.wmx' => 'video/x-ms-wmx',
510
+ '.wmz' => 'application/x-ms-wmz',
511
+ '.wpl' => 'application/vnd.ms-wpl',
512
+ '.wps' => 'application/vnd.ms-works',
513
+ '.wri' => 'application/x-mswrite',
514
+ '.wrl' => 'x-world/x-vrml',
515
+ '.wrz' => 'x-world/x-vrml',
516
+ '.wsc' => 'text/scriptlet',
517
+ '.wsdl' => 'text/xml',
518
+ '.wvx' => 'video/x-ms-wvx',
519
+ '.x' => 'application/directx',
520
+ '.xaf' => 'x-world/x-vrml',
521
+ '.xaml' => 'application/xaml+xml',
522
+ '.xap' => 'application/x-silverlight-app',
523
+ '.xbap' => 'application/x-ms-xbap',
524
+ '.xbm' => 'image/x-xbitmap',
525
+ '.xdr' => 'text/plain',
526
+ '.xht' => 'application/xhtml+xml',
527
+ '.xhtml' => 'application/xhtml+xml',
528
+ '.xla' => 'application/vnd.ms-excel',
529
+ '.xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
530
+ '.xlc' => 'application/vnd.ms-excel',
531
+ '.xld' => 'application/vnd.ms-excel',
532
+ '.xlk' => 'application/vnd.ms-excel',
533
+ '.xll' => 'application/vnd.ms-excel',
534
+ '.xlm' => 'application/vnd.ms-excel',
535
+ '.xls' => 'application/vnd.ms-excel',
536
+ '.xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
537
+ '.xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
538
+ '.xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
539
+ '.xlt' => 'application/vnd.ms-excel',
540
+ '.xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
541
+ '.xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
542
+ '.xlw' => 'application/vnd.ms-excel',
543
+ '.xml' => 'text/xml',
544
+ '.xmta' => 'application/xml',
545
+ '.xof' => 'x-world/x-vrml',
546
+ '.XOML' => 'text/plain',
547
+ '.xpm' => 'image/x-xpixmap',
548
+ '.xps' => 'application/vnd.ms-xpsdocument',
549
+ '.xrm-ms' => 'text/xml',
550
+ '.xsc' => 'application/xml',
551
+ '.xsd' => 'text/xml',
552
+ '.xsf' => 'text/xml',
553
+ '.xsl' => 'text/xml',
554
+ '.xslt' => 'text/xml',
555
+ '.xsn' => 'application/octet-stream',
556
+ '.xss' => 'application/xml',
557
+ '.xtp' => 'application/octet-stream',
558
+ '.xwd' => 'image/x-xwindowdump',
559
+ '.z' => 'application/x-compress',
560
+ '.zip' => 'application/x-zip-compressed'
561
+ }
562
+ end