-
Notifications
You must be signed in to change notification settings - Fork 2
/
MimeType.cs
496 lines (435 loc) · 20.3 KB
/
MimeType.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using GalleryServerPro.Business.Interfaces;
using System.Globalization;
using GalleryServerPro.Business.Properties;
using GalleryServerPro.Data;
using GalleryServerPro.Events.CustomExceptions;
using System.Linq;
namespace GalleryServerPro.Business
{
/// <summary>
/// Represents a mime type associated with a file's extension.
/// </summary>
[DebuggerDisplay("{_majorType}/{_subtype} ({_extension}, Gallery ID = {_galleryId})")]
public class MimeType : IMimeType
{
#region Private Fields
private int _mimeTypeId;
private int _mimeTypeGalleryId;
private int _galleryId;
private readonly string _extension;
private readonly MimeTypeCategory _typeCategory;
private readonly string _majorType;
private readonly string _subtype;
private bool _allowAddToGallery;
private readonly string _browserMimeType;
private readonly IMediaTemplateCollection _mediaTemplates = new MediaTemplateCollection();
private static readonly object _sharedLock = new object();
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="MimeType"/> class.
/// </summary>
/// <param name="mimeTypeId">The value that uniquely identifies the MIME type.</param>
/// <param name="mimeTypeGalleryId">The value that uniquely identifies the MIME type that applies to a particular gallery.</param>
/// <param name="galleryId">The gallery ID. Specify <see cref="Int32.MinValue"/> if creating an instance that is not
/// specific to a particular gallery.</param>
/// <param name="fileExtension">A string representing the file's extension, including the period (e.g. ".jpg", ".avi").
/// It is not case sensitive.</param>
/// <param name="mimeTypeValue">The full mime type. This is the <see cref="MajorType"/> concatenated with the <see cref="Subtype"/>,
/// with a '/' between them (e.g. image/jpeg, video/quicktime).</param>
/// <param name="browserMimeType">The MIME type that can be understood by the browser for displaying this media object. Specify null or
/// <see cref="String.Empty"/> if the MIME type appropriate for the browser is the same as <paramref name="mimeTypeValue"/>.</param>
/// <param name="allowAddToGallery">Indicates whether a file having this MIME type can be added to Gallery Server Pro.
/// This parameter is only relevant when a valid <paramref name="galleryId"/> is specified.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="fileExtension" /> or <paramref name="mimeTypeValue" /> is
/// null or an empty string.</exception>
private MimeType(int mimeTypeId, int mimeTypeGalleryId, int galleryId, string fileExtension, string mimeTypeValue, string browserMimeType, bool allowAddToGallery)
{
#region Validation
if (String.IsNullOrEmpty(fileExtension))
throw new ArgumentOutOfRangeException("fileExtension", "Parameter cannot be null or empty.");
if (String.IsNullOrEmpty(mimeTypeValue))
throw new ArgumentOutOfRangeException("mimeTypeValue", "Parameter cannot be null or empty.");
// If browserMimeType is specified, it better be valid.
if (!String.IsNullOrEmpty(browserMimeType))
{
ValidateMimeType(browserMimeType);
}
// Validate fullMimeType and separate it into its major and sub types.
string majorType;
string subType;
ValidateMimeType(mimeTypeValue, out majorType, out subType);
#endregion
MimeTypeCategory mimeTypeCategory = MimeTypeCategory.Other;
try
{
mimeTypeCategory = (MimeTypeCategory)Enum.Parse(typeof(MimeTypeCategory), majorType, true);
}
catch (ArgumentException) { /* Swallow exception so that we default to MimeTypeCategory.Other */ }
this._mimeTypeId = mimeTypeId;
this._mimeTypeGalleryId = mimeTypeGalleryId;
this._galleryId = galleryId;
this._extension = fileExtension;
this._typeCategory = mimeTypeCategory;
this._majorType = majorType;
this._subtype = subType;
this._browserMimeType = (String.IsNullOrEmpty(browserMimeType) ? mimeTypeValue : browserMimeType);
this._allowAddToGallery = allowAddToGallery;
}
/// <summary>
/// Initializes a new instance of the <see cref="MimeType"/> class with the specified MIME type category. The <see cref="MajorType" /> property is
/// assigned the string representation of the <paramref name="mimeType"/>. Remaining properties are set to empty strings or false
/// (<see cref="AllowAddToGallery" />). This constructor is intended to be used to help describe an external media object, which is
/// not represented by a locally stored file but for which it is useful to describe its general type (audio, video, etc).
/// </summary>
/// <param name="mimeType">Specifies the category to which this mime type belongs. This usually corresponds to the first portion of
/// the full mime type description. (e.g. "image" if the full mime type is "image/jpeg").</param>
private MimeType(MimeTypeCategory mimeType)
{
this._galleryId = Int32.MinValue;
this._typeCategory = mimeType;
this._majorType = mimeType.ToString();
this._extension = String.Empty;
this._subtype = String.Empty;
this._browserMimeType = String.Empty;
this._allowAddToGallery = false;
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the value that uniquely identifies this MIME type. Each application has a master list of MIME types it works with;
/// this value identifies that MIME type.
/// </summary>
/// <value>The MIME type ID.</value>
public int MimeTypeId
{
get { return _mimeTypeId; }
set { _mimeTypeId = value; }
}
/// <summary>
/// Gets or sets the value that uniquely identifies the MIME type that applies to a particular gallery. This value is <see cref="Int32.MinValue" />
/// when the current instance is an application-level MIME type and not associated with a particular gallery. In this case,
/// <see cref="IMimeType.GalleryId" /> will also be <see cref="Int32.MinValue" />.
/// </summary>
/// <value>The value that uniquely identifies the MIME type that applies to a particular gallery.</value>
public int MimeTypeGalleryId
{
get { return _mimeTypeGalleryId; }
set { _mimeTypeGalleryId = value; }
}
/// <summary>
/// Gets or sets the gallery ID this MIME type is associated with. May be <see cref="Int32.MinValue"/> when the instance is not
/// assocated with a particular gallery.
/// </summary>
/// <value>The gallery ID this MIME type is associated with.</value>
public int GalleryId
{
get
{
return this._galleryId;
}
set
{
this._galleryId = value;
}
}
/// <summary>
/// Gets the file extension this mime type is associated with, including the period (e.g. ".jpg", ".avi").
/// </summary>
/// <value>The file extension this mime type is associated with.</value>
public string Extension
{
get
{
return this._extension;
}
}
/// <summary>
/// Gets the type category this mime type is associated with (e.g. image, video, other).
/// </summary>
/// <value>
/// The type category this mime type is associated with (e.g. image, video, other).
/// </value>
public MimeTypeCategory TypeCategory
{
get
{
return this._typeCategory;
}
}
/// <summary>
/// Gets the MIME type that should be sent to the browser. In most cases this is the same as the <see cref="IMimeType.FullType" />,
/// but in some cases is different. For example, the MIME type for a .wav file is audio/wav, but the browser requires a
/// value of application/x-mplayer2.
/// </summary>
/// <value>The MIME type that should be sent to the browser.</value>
public string BrowserMimeType
{
get
{
return this._browserMimeType;
}
}
/// <summary>
/// Gets the major type this mime type is associated with (e.g. image, video).
/// </summary>
/// <value>
/// The major type this mime type is associated with (e.g. image, video).
/// </value>
public string MajorType
{
get
{
return this._majorType;
}
}
/// <summary>
/// Gets the subtype this mime type is associated with (e.g. jpeg, quicktime).
/// </summary>
/// <value>
/// The subtype this mime type is associated with (e.g. jpeg, quicktime).
/// </value>
public string Subtype
{
get
{
return this._subtype;
}
}
/// <summary>
/// Gets the full mime type. This is the <see cref="MajorType"/> concatenated with the <see cref="Subtype"/>, with a '/' between them
/// (e.g. image/jpeg, video/quicktime).
/// </summary>
/// <value>The full mime type.</value>
public string FullType
{
get
{
return String.Format(CultureInfo.CurrentCulture, "{0}/{1}", this._majorType.ToString().ToLower(CultureInfo.CurrentCulture), this._subtype);
}
}
/// <summary>
/// Gets a value indicating whether objects of this MIME type can be added to Gallery Server Pro.
/// </summary>
/// <value>
/// <c>true</c> if objects of this MIME type can be added to Gallery Server Pro; otherwise, <c>false</c>.
/// </value>
public bool AllowAddToGallery
{
get
{
return this._allowAddToGallery;
}
set
{
this._allowAddToGallery = value;
}
}
/// <summary>
/// Gets the collection of media templates for the current MIME type.
/// </summary>
/// <value>The media templates for the current MIME type.</value>
public IMediaTemplateCollection MediaTemplates
{
get { return _mediaTemplates; }
}
#endregion
#region Public Methods
/// <summary>
/// Creates a deep copy of this instance.
/// </summary>
/// <returns>Returns a deep copy of this instance.</returns>
public IMimeType Copy()
{
IMimeType copy = new MimeType(this.MimeTypeId, this.MimeTypeGalleryId, this.GalleryId, this.Extension, this.FullType, this.BrowserMimeType, this.AllowAddToGallery);
if (this.MediaTemplates.Count > 0)
{
copy.MediaTemplates.AddRange(this.MediaTemplates.Copy());
}
return copy;
}
/// <summary>
/// Persist the gallery-specific properties of this instance to the data store. Currently, only the <see cref="IMimeType.AllowAddToGallery" />
/// property is unique to the gallery identified in <see cref="IMimeType.GalleryId" />; the other properties are application-wide and at
/// present there is no API to modify them. In other words, this method saves whether a particular MIME type is enabled or disabled for a
/// particular gallery.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when the current instance is an application-level MIME type. Only gallery-specific
/// MIME types can be persisted to the data store. Specifically, the exception is thrown when <see cref="IMimeType.GalleryId" /> or
/// <see cref="IMimeType.MimeTypeGalleryId" /> is <see cref="Int32.MinValue" />.</exception>
public void Save()
{
if ((GalleryId == int.MinValue) || (MimeTypeGalleryId == int.MinValue))
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "Cannot save. This MIME type instance is an application-level MIME type and cannot be persisted to the data store. Only gallery-specific MIME types can be saved. (GalleryId={0}, MimeTypeId={1}, MimeTypeGalleryId={2}, FileExtension={3}", GalleryId, MimeTypeId, MimeTypeGalleryId, Extension));
}
//Factory.GetDataProvider().MimeType_Save(this);
using (var repo = new MimeTypeGalleryRepository())
{
var mtDto = repo.Find(MimeTypeGalleryId);
if (mtDto != null)
{
mtDto.IsEnabled = AllowAddToGallery;
repo.Save();
}
}
}
/// <summary>
/// Gets the most specific <see cref="IMediaTemplate" /> item that matches one of the <paramref name="browserIds" />. This
/// method loops through each of the browser IDs in <paramref name="browserIds" />, starting with the most specific item, and
/// looks for a match in the current collection. This method is guaranteed to return a <see cref="IMediaTemplate" /> object,
/// provided the collection, at the very least, contains a browser element with id = "default".
/// </summary>
/// <param name="browserIds">A <see cref="System.Array"/> of browser ids for the current browser. This is a list of strings,
/// ordered from most general to most specific, that represent the various categories of browsers the current
/// browser belongs to. This is typically populated by calling ToArray() on the Request.Browser.Browsers property.
/// </param>
/// <returns>The <see cref="IMediaTemplate" /> that most specifically matches one of the <paramref name="browserIds" />;
/// otherwise, a null reference.</returns>
/// <example>During a request where the client is Firefox, the Request.Browser.Browsers property returns an ArrayList with these
/// five items: default, mozilla, gecko, mozillarv, and mozillafirefox. This method starts with the most specific item
/// (mozillafirefox) and looks in the current collection for an item with this browser ID. If a match is found, that item
/// is returned. If no match is found, the next item (mozillarv) is used as the search parameter. This continues until a match
/// is found. Since there should always be a browser element with id="default", there will always - eventually - be a match.
/// </example>
public IMediaTemplate GetMediaTemplate(Array browserIds)
{
return MediaTemplates.Find(browserIds);
}
#endregion
#region Public static methods
/// <summary>
/// Initializes a new instance of the <see cref="MimeType"/> class with the specified MIME type category. The <see cref="MajorType" /> property is
/// assigned the string representation of the <paramref name="mimeType"/>. Remaining properties are set to empty strings or false
/// (<see cref="AllowAddToGallery" />). This method is intended to be used to help describe an external media object, which is
/// not represented by a locally stored file but for which it is useful to describe its general type (audio, video, etc).
/// </summary>
/// <param name="mimeType">Specifies the category to which this mime type belongs. This usually corresponds to the first portion of
/// the full mime type description. (e.g. "image" if the full mime type is "image/jpeg").</param>
/// <returns>Returns a new instance of <see cref="IMimeType"/>.</returns>
public static IMimeType CreateInstance(MimeTypeCategory mimeType)
{
return new MimeType(mimeType);
}
/// <summary>
/// Loads the collection of MIME types for the specified <paramref name="galleryId" /> from the data store.
/// When <paramref name="galleryId" /> is <see cref="Int32.MinValue" />, a generic collection that is not
/// specific to a particular gallery is returned.
/// </summary>
/// <param name="galleryId">The gallery ID. Specify <see cref="Int32.MinValue" /> to retrieve a generic
/// collection that is not specific to a particular gallery.</param>
/// <returns>Returns a <see cref="IMimeTypeCollection" /> containing MIME types for the specified
/// <paramref name="galleryId" /></returns>
public static IMimeTypeCollection LoadMimeTypes(int galleryId)
{
var mimeTypes = LoadMimeTypesFromDataStore();
if (galleryId == Int32.MinValue)
{
// User wants the master list. Load from data store and return (this also adds it to the static var for next time).
return mimeTypes;
}
// User wants the MIME types for a specific gallery that we haven't yet loaded from disk. Do so now.
if (ConfigureMimeTypesForGallery(mimeTypes, galleryId))
{
return mimeTypes;
}
// If we get here then no records existed in the data store for the gallery MIME types (gs_MimeTypeGallery). Create
// the gallery, which will create these records while not harming any pre-existing records that may exist in other
// tables such as gs_GallerySettings.
Factory.LoadGallery(galleryId).Configure();
// Note: If CreateGallery() fails to create records in gs_MimeTypeGallery, we will end up in an infinite loop.
// But that should never happen, right?
return LoadMimeTypes(galleryId);
}
#endregion
#region Private methods
private static void ValidateMimeType(string fullMimeType)
{
string majorType;
string subType;
ValidateMimeType(fullMimeType, out majorType, out subType);
}
private static void ValidateMimeType(string fullMimeType, out string majorType, out string subType)
{
int slashLocation = fullMimeType.IndexOf("/", StringComparison.Ordinal);
if (slashLocation < 0)
{
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Resources.MimeType_Ctor_Ex_Msg, fullMimeType), fullMimeType);
}
majorType = fullMimeType.Substring(0, slashLocation);
subType = fullMimeType.Substring(slashLocation + 1);
if ((String.IsNullOrEmpty(majorType)) || (String.IsNullOrEmpty(subType)))
{
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Resources.MimeType_Ctor_Ex_Msg, fullMimeType), fullMimeType);
}
}
/// <summary>
/// Updates the <paramref name="baseMimeTypes" /> with configuration data for the <paramref name="galleryId" />.
/// Returns <c>true</c> when at least one record in the <see cref="MimeTypeGalleryDto" /> table exists for the
/// <paramref name="galleryId" />; otherwise returns <c>false</c>.
/// </summary>
/// <param name="baseMimeTypes">A collection of MIME types to be updated with gallery-specific data.</param>
/// <param name="galleryId">The gallery ID.</param>
/// <returns>Returns <c>true</c> when at least one record in the <see cref="MimeTypeGalleryDto" /> table exists for the
/// <paramref name="galleryId" />; otherwise returns <c>false</c>.</returns>
private static bool ConfigureMimeTypesForGallery(IMimeTypeCollection baseMimeTypes, int galleryId)
{
//IMimeTypeCollection baseMimeTypes = LoadMimeTypes(Int32.MinValue);
//IMimeTypeCollection newMimeTypes = new MimeTypeCollection();
var mediaTemplates = Factory.LoadMediaTemplates();
var foundRows = false;
using (var repo = new MimeTypeGalleryRepository())
{
foreach (var mtgDto in repo.Where(m => m.FKGalleryId == galleryId, m => m.MimeType))
{
foundRows = true;
var mimeType = baseMimeTypes.Find(mtgDto.MimeType.FileExtension);
if (mimeType == null)
{
throw new BusinessException(String.Format(CultureInfo.CurrentCulture, "Could not find a IMimeType with file extension \"{0}\" in the list of base MIME types.", mtgDto.MimeType.FileExtension));
}
mimeType.GalleryId = galleryId;
mimeType.MimeTypeGalleryId = mtgDto.MimeTypeGalleryId;
mimeType.AllowAddToGallery = mtgDto.IsEnabled;
// Populate the media template collection.
mimeType.MediaTemplates.AddRange(mediaTemplates.Find(mimeType));
// Validate the media templates. There may not be any, which is OK (for example, there isn't one defined for 'application/msword').
// But if there *IS* one defined, there must be one with a browser ID of "default".
if ((mimeType.MediaTemplates.Count > 0) && (mimeType.MediaTemplates.Find("default") == null))
{
throw new BusinessException(String.Format(CultureInfo.CurrentCulture, "No default media template. Could not find a media template for MIME type \"{0}\" or \"{1}\" with browser ID = \"default\".", mimeType.FullType, String.Concat(mimeType.MajorType, "/*")));
}
}
}
return foundRows;
}
/// <summary>
/// Loads the set of MIME types from the data store. These MIME types are the master list of MIME types and are not
/// specific to a particular gallery. That is, the <see cref="IMimeType.GalleryId" /> property is set to <see cref="Int32.MinValue" />
/// and the <see cref="IMimeType.AllowAddToGallery" /> property is <c>false</c> for all items.
/// </summary>
/// <returns>Returns a <see cref="IMimeTypeCollection" /> containing MIME types..</returns>
/// <exception cref="BusinessException">Thrown when no records were found in the master list of MIME types in the data store.</exception>
private static IMimeTypeCollection LoadMimeTypesFromDataStore()
{
IMimeTypeCollection baseMimeTypes = new MimeTypeCollection();
using (var repo = new MimeTypeRepository())
{
foreach (var mimeTypeDto in repo.GetAll().OrderBy(m => m.FileExtension))
{
baseMimeTypes.Add(new MimeType(mimeTypeDto.MimeTypeId, Int32.MinValue, Int32.MinValue, mimeTypeDto.FileExtension.Trim(), mimeTypeDto.MimeTypeValue.Trim(), mimeTypeDto.BrowserMimeTypeValue.Trim(), false));
}
}
if (baseMimeTypes.Count == 0)
{
throw new BusinessException("No records were found in the master list of MIME types in the data store.");
}
return baseMimeTypes;
}
#endregion
}
}