Goal: To host thumbnails and images on the internet
Benefits:
1. Reduce the storage on the server where space is limited
2. Put the images geographically close your target audience - shorter image load time
3. Give the user freedom of choice and flexibility as where to host images
Assumptions:
1. All external objects on the site are images (external or on-server)
2. User can easily obtain the thumbnail and image urls
3. These urls never expire
Story:
I host my site for $30 a year including 500MB storage and 5GB bandwidth. The 500MB storage
really can't host many photos. A Flickr Pro account gives you unlimited storage and bandwidth
for $25 a year. I thought: why don't I put my photos on Flickr and display on my web site?
The design of this fix is not limited to Flickr but to any http urls where you can put your
photos. I just use Flickr for now.
Brief of the uploading SOP [Flickr], for example:
1. Upload photos to your Flickr account with specified tags or other methods that let you search for them later.
2. Get the title, thumbnail urls and the image urls from any software (I use Friendly.Flickr)
3. Put thse three set of info as three columns in excel
4. Create an album in GSP
5. Pick the album you created and copy&past the three columns (title, thumbnail url, image url) onto the page. Be careful with the format. The script doesn't do very well to detect errors in your data. Please see the example excell file.
6. The script obtain the widths and heights of these thumbnails and images and put all information in the database
7. The page turns to the newly created album page
Requirements:
1. Use Gallery Server Pro v2.2.3286 source version to build new dlls
2. Create a new table and link to GSP's gs_MediaObject table
Modifications to Gallery Server Pro v2.2.3286:
A. \Website\CodeFiles\GalleryPage.cs [GalleryServerPro.Web.dll]
1. Include marty1101 namespace
using marty1101;
2. Display thumbnails for external images
Code:
public string GetThumbnailUrl(IGalleryObject galleryObject)
{
/* Original codes
if (galleryObject is Album)
return GetAlbumThumbnailUrl(galleryObject);
else
return GetMediaObjectUrl(galleryObject, DisplayObjectType.Thumbnail);
*/
#region marty1101
marty1101_GSP m = new marty1101_GSP();
if (galleryObject is Album)
{
if (galleryObject.Thumbnail.MediaObjectId > 0)
{
IGalleryObject ThumbnailGalleryObject = Factory.LoadMediaObjectInstance(galleryObject.Thumbnail.MediaObjectId);
if (ThumbnailGalleryObject.Original.DisplayType == DisplayObjectType.External)
{
// Get thumbnail images hosted on Flickr. HostUid=1
return m.GetExternalThumbnailUrl(ThumbnailGalleryObject.Original.MediaObjectId, 1);
}
else
{ return GetAlbumThumbnailUrl(galleryObject); }
}
else
{ return GetAlbumThumbnailUrl(galleryObject); }
}
else if (galleryObject.Original.DisplayType == DisplayObjectType.External)
{
// Get thumbnail images hosted on Flickr. HostUid=1
return m.GetExternalThumbnailUrl(galleryObject.Original.MediaObjectId, 1);
}
else
{
return GetMediaObjectUrl(galleryObject, DisplayObjectType.Thumbnail);
}
#endregion marty1101
}
3.Include external object in slideshow
Code:
private static int GetNextMediaObjectIdForSlideshow(int mediaObjectIndex, IGalleryObjectCollection siblings)
{
int nextMediaObjectId = 0;
while (mediaObjectIndex < (siblings.Count - 1))
{
IGalleryObject nextMediaObject = siblings[mediaObjectIndex + 1];
/* Origional Code
* if (nextMediaObject is GalleryServerPro.Business.Image)
*/
#region marty1101
if (nextMediaObject is GalleryServerPro.Business.Image || nextMediaObject is GalleryServerPro.Business.ExternalMediaObject)
#endregion marty1101
{
nextMediaObjectId = nextMediaObject.Id;
break;
}
mediaObjectIndex += 1;
}
return nextMediaObjectId;
}
B. \TIS.GSP.Business\SynchronizationManager.cs [GalleryServerPro.Business.dll]
1. To avoid "Synchronization" updating gs_MediaObject.ThumbnailWidth and gs_MediaObject.ThumbnailHeight to generic values
Code:
private void SynchronizeExternalMediaObjects(IAlbum album)
{
/* Original Codes
foreach (IGalleryObject mediaObject in album.GetChildGalleryObjects(GalleryObjectType.External))
{
// Check for existence of thumbnail.
if (this.OverwriteThumbnail || !File.Exists(mediaObject.Thumbnail.FileNamePhysicalPath))
{
mediaObject.RegenerateThumbnailOnSave = true;
HelperFunctions.UpdateAuditFields(mediaObject, this._userName);
mediaObject.Save();
mediaObject.IsSynchronized = true;
}
}
*/
#region marty1101
// Do nothing here. The above codes generate the default thumbnail for external objects when there is no default thumbnail existed.
// Since the thumbnail for an external object is assigned in gs_ExternalThumbnail table, there is no need for the default thumbnail.
#endregion
}
C. \Website\CodeFiles\Util.cs [GalleryServerPro.Web.dll]
1. Solve the conflicts between the global.asax files of CommunityServer and GSP. Make GSP not relying on global.asax
Code:
public static void InitializeApplication()
{
try
{
// Set application key so ComponentArt knows it is properly licensed.
#region marty1101
HttpContext.Current.Application["ComponentArtWebUI_AppKey"] = "This edition of ComponentArt Web.UI is licensed for Gallery Server Pro application only.";
#endregion marty1101
// Add a dummy value to session so that the session ID remains constant. (This is required by Util.GetRolesForUser().)
HttpContext.Current.Session.Add("1", "1");
// Set web-related variables in the business layer and initialize the data store.
InitializeBusinessLayer();
// Delete anonymous profiles, if they exist, to minimize the database clutter.
DeleteAnonymousProfiles();
// Validate users, roles and profiles.
ValidateMembership();
}
catch (System.Threading.ThreadAbortException) { }
catch (Exception ex)
{
// Let the error handler deal with it. It will decide whether to transfer the user to a friendly error page.
// If the function returns, that means it didn't redirect, so we should re-throw the exception.
HandleGalleryException(ex);
throw;
}
}
D. Issues:
1. "Rearrange" function updates gs_MediaObject.ThumbnailWidth and gs_MediaObject.ThumbnailHeight, gs_MediaObject.OriginalWidth and gs_MediaObject.OriginalHeight to generic values.
2. "Rearrange" function generate generic image files on disk
\TIS.GSP.Business\MediaObjectSaveBehavior.cs [GalleryServerPro.Business.dll]
Code:
public void Save()
{
// If the user requested a rotation, then rotate and save the original. If no rotation is requested,
// the following line does nothing.
/* Original Code
this._galleryObject.Original.GenerateAndSaveFile();
*/
#region marty1101
if (!(this._galleryObject.Original.DisplayType == DisplayObjectType.External && this._galleryObject.Original.ExternalType == MimeTypeCategory.Image))
{ this._galleryObject.Original.GenerateAndSaveFile(); }
#endregion
// Generate the thumbnail and optimized versions. These must run after the previous statement because when
// the image is rotated, these methods assume the original has already been rotated.
try
{
/* Original Code
this._galleryObject.Thumbnail.GenerateAndSaveFile();
*/
#region marty1101
if ( !(this._galleryObject.Original.DisplayType == DisplayObjectType.External && this._galleryObject.Original.ExternalType == MimeTypeCategory.Image) )
{ this._galleryObject.Thumbnail.GenerateAndSaveFile(); }
#endregion
this._galleryObject.Optimized.GenerateAndSaveFile();
}
catch (UnsupportedImageTypeException)
{
// We'll get here when there is a corrupt image or the server's memory is not sufficient to process the image.
// When this happens, replace the thumbnail creator object with a GenericThumbnailCreator. That one uses a
// hard-coded thumbnail image rather than trying to generate a thumbnail from the original image.
// Also, null out the Optimized object and don't bother to try to create an optimized image.
this._galleryObject.Thumbnail.DisplayObjectCreator = new GenericThumbnailCreator(this._galleryObject);
this._galleryObject.Thumbnail.GenerateAndSaveFile();
this._galleryObject.Optimized = new NullObjects.NullDisplayObject();
}
// Update the metadata if required.
if (this._galleryObject is Image)
{
UpdateMetadata();
}
// Save the data to the data store
Factory.GetDataProvider().MediaObject_Save(this._galleryObject);
}
\TIS.GSP.Business\GalleryObject.cs [GalleryServerPro.Business.dll]
Code:
private void ValidateSave()
{
if ((!this.IsNew) && (!this.IsInflated))
{
throw new System.InvalidOperationException(Resources.GalleryObject_ValidateSave_Ex_Msg);
}
ValidateSequence();
// Set RegenerateThumbnailOnSave to true if thumbnail image doesn't exist.
/* Original Code
CheckForThumbnailImage();
*/
#region marty1101
if ( !(this.Original.DisplayType == DisplayObjectType.External && this.Original.ExternalType == MimeTypeCategory.Image) )
{ CheckForThumbnailImage(); }
else
{ RegenerateThumbnailOnSave = false; }
#endregion
// Set RegenerateOptimizedOnSave to true if optimized image doesn't exist. This is an empty virtual method
// that is overridden in the Image class. That is, this method does nothing for non-images.
CheckForOptimizedImage();
// Make sure the audit fields have been set.
ValidateAuditFields();
}
\TIS.GSP.Business\ExternalMediaObject.cs [GalleryServerPro.Business.dll]
Code:
internal ExternalMediaObject(int id, int parentId, IAlbum parentAlbum, string title, string hashKey, string thumbnailFilename,
int thumbnailWidth, int thumbnailHeight, int thumbnailSizeKb, string originalFilename,
int originalWidth, int originalHeight, int originalSizeKb, string externalHtmlSource, MimeTypeCategory mimeType, int sequence,
string createdByUsername, DateTime dateAdded, string lastModifiedByUsername, DateTime dateLastModified,
bool isPrivate, bool isInflated)
{
...................
...................
switch (mimeType)
{
case MimeTypeCategory.Audio:
this.Original.Width = GalleryServerPro.Configuration.ConfigManager.GetGalleryServerProConfigSection().Core.DefaultAudioPlayerWidth;
this.Original.Height = GalleryServerPro.Configuration.ConfigManager.GetGalleryServerProConfigSection().Core.DefaultAudioPlayerHeight;
break;
case MimeTypeCategory.Video:
this.Original.Width = GalleryServerPro.Configuration.ConfigManager.GetGalleryServerProConfigSection().Core.DefaultVideoPlayerWidth;
this.Original.Height = GalleryServerPro.Configuration.ConfigManager.GetGalleryServerProConfigSection().Core.DefaultVideoPlayerHeight;
break;
case MimeTypeCategory.Image:
#region marty1101
this.Original.Width = originalWidth;
this.Original.Height = originalHeight;
this.Original.FileName = originalFilename;
this.Original.FileSizeKB = originalSizeKb;
break;
#endregion
case MimeTypeCategory.Other:
this.Original.Width = GalleryServerPro.Configuration.ConfigManager.GetGalleryServerProConfigSection().Core.DefaultGenericObjectWidth;
this.Original.Height = GalleryServerPro.Configuration.ConfigManager.GetGalleryServerProConfigSection().Core.DefaultGenericObjectHeight;
break;
}
...................
...................
}
Added codes:
A. Create a new project in the source solution: marty1101 and add related references; and include these files:
marty1101_Image.cs
marty1101_GSP.cs
B. The page that let you add photos to GSP: (you can put wherever in your site)
add_external_image.aspx
add_external_image.aspx.cs
These files are in the attached zip
Steps of modifications:
1. Create a new table in the database called gs_ExternalThumbnail. The SQL command:
Create_gs_ExternalThumbnail.sql
The SQL command also creates a constraint to link gs_ExternalThumbnail.MediaObjectId to gs_MediaObject.MediaObjectId, so that when a media object was deleted in gs_MediaObject, the related rows in gs_ExternalThumbnail would be deleted.
2. Once all the above are in place, compile the solution and get these new dlls:
GalleryServerPro.Web.dll
GalleryServerPro.Business.dll
GalleryServerPro.Web.dll
marty1101.dll
3. Put these in your web \bin and replace the old ones
4. browse to add_external_image.aspx and you're ready to go!
These codes were written to make it work asap. I've tried the best to make it easy to do for future versions of GSP. There're still more can be done and you're welcome to modify. Please comment.
If there were mistakes in this document, please let me know.