Hi Roger,
I'll try to give a brief overview how I did it.
First of all I've added a new function to the IDataProvider and its abstract implementation DataProvider:
IDataProvider.cs:Code:
/// <summary>
/// Gets the latest <paramref name="countMediaObjects"/> from the whole gallery (no specific album).
/// </summary>
/// <param name="countMediaObjects">The count of media objects to return.</param>
/// <returns></returns>
System.Data.IDataReader MediaObject_GetDataReaderLatestMediaObjects(int countMediaObjects);
DataProvider.cs:Code:
/// <inheritdoc />
public abstract IDataReader MediaObject_GetDataReaderLatestMediaObjects(int countMediaObjects);
Then I've implemented it in the SQLite provider like this:
SQLiteGalleryServerProProvider.cs:Code:
public override System.Data.IDataReader MediaObject_GetDataReaderLatestMediaObjects(int countMediaObjects)
{
SQLiteConnection cn = GetDBConnectionForGallery();
using (SQLiteCommand cmd = cn.CreateCommand())
{
const string sql = @"
SELECT [gs_MediaObject].MediaObjectId
FROM [gs_MediaObject]
ORDER BY DateAdded DESC
LIMIT @Count;";
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("@Count", countMediaObjects);
if (cn.State == ConnectionState.Closed)
cn.Open();
if (IsTransactionInProgress())
return cmd.ExecuteReader();
else
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
}
Note that I only retrieve the ID's for the newest N items; not the whole records. This is because later on I reuse your existing methods to retrieve, cache and materialize whole instances of media-objects.
Next I use the above dataprovider method in the factory like this:
Factory.cs:Code:
/// <summary>
/// Gets the newest <paramref name="countMediaObjects"/> media objects (images etc.) from the database.
/// </summary>
/// <param name="countMediaObjects">The count newest media objects.</param>
/// <returns>The newest <paramref name="countMediaObjects"/> media objects (images etc.)</returns>
public static IGalleryObjectCollection GetLatestMediaObjects(int countMediaObjects)
{
IGalleryObjectCollection result = new GalleryObjectCollection();
IDataReader dr = null;
try
{
using (dr = Factory.GetDataProvider().MediaObject_GetDataReaderLatestMediaObjects(countMediaObjects))
{
while (dr.Read())
{
int mediaObjectId = Int32.Parse(dr["MediaObjectId"].ToString(), CultureInfo.InvariantCulture);
// Retrieve the media objects each by each using the cache if possible.
// Don't duplicate the logic of RetrieveMediaObject() here.
IGalleryObject mediaObject = RetrieveMediaObject(mediaObjectId);
if (mediaObject != null)
{
result.Add(mediaObject);
}
}
}
}
finally
{
if (dr != null) dr.Close();
}
return result;
}
The factory then gets called by the Utils method:
Util.cs:Code:
/// <summary>
/// Gets the newest/latest media objects.
/// </summary>
/// <param name="countMediaObjectd">The count media objectd.</param>
/// <returns>The newest/latest media objects.</returns>
public IGalleryObjectCollection GetLatestMediaObjects(int countMediaObjectd)
{
return Factory.GetLatestMediaObjects(countMediaObjectd);
}
which gets called by the GalleryPage:
GalleryPage.cs:Code:
/// <summary>
/// Gets the latest media objects.
/// </summary>
/// <param name="countMediaObjects">The count media objects.</param>
/// <returns>The latest media objects.</returns>
public IGalleryObjectCollection GetLatestMediaObjects(int countMediaObjects)
{
return this._util.GetLatestMediaObjects(countMediaObjects);
}
On the UI part I've did mainly what you suggested above by cloning the ThumbnailView control and adding it to the Album page.
As you see in the following album markup the user control is always added to the album markup. The decision if needs to be rendered or not is done within the control not the album page:
Album.aspx:
Code:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="album.ascx.cs" Inherits="GalleryServerPro.Web.gs.pages.album" %>
<%@ Register tagPrefix="ComponentArt" namespace="ComponentArt.Web.UI" assembly="ComponentArt.Web.UI" %>
<%@ Register Src="../controls/albumheader.ascx" TagName="albumheader" TagPrefix="gs" %>
<%@ Register Src="../controls/thumbnailview.ascx" TagName="thumbnailview" TagPrefix="gs" %>
<%@ Register Src="../controls/latestthumbnailview.ascx" TagName="latestthumbnailview" TagPrefix="gs" %>
<gs:albumheader ID="ah" runat="server" EnableInlineEditing="true" />
<asp:PlaceHolder ID="phMessage" runat="server" EnableViewState="False" />
<gs:thumbnailview ID="tv" runat="server" />
<gs:latestthumbnailview ID="latest" runat="server" />
The LatestThumbnailView's decides on load if it needs to be rendered or not with the following code:
Code:
protected void Page_Load(object sender, EventArgs e)
{
var album = this.GalleryPage.GetAlbum();
Visible = (album != null) && (album.IsRootAlbum);
}
Loading / binding the data in that control is done using the method GetLatestMediaObjects() from the GalleryPage. Here is where the hardcoded value "10" is in. This should be replaced by a configuration setting:
Code:
private void BindData()
{
DisplayThumbnails(this.GalleryPage.GetLatestMediaObjects(10));
}
Finally the markup of the control is no rocket science but a clone of the ThumbnailView just without the callback / AJAX stuff:
Code:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="LatestThumbnailview.ascx.cs"
Inherits="GalleryServerPro.Web.Controls.latestthumbnailview" EnableViewState="false" %>
<%@ Register Namespace="ComponentArt.Web.UI" Assembly="ComponentArt.Web.UI" TagPrefix="ComponentArt" %>
<%@ Import Namespace="GalleryServerPro.Business.Interfaces" %>
<asp:PlaceHolder ID="phMsg" runat="server" />
<h3>
Newest media</h3>
<asp:PlaceHolder ID="phPagerTop" runat="server" EnableViewState="false" />
<asp:Repeater ID="rptr" runat="server" EnableViewState="false">
<HeaderTemplate>
<div id="thmbCtnr" class="gsp_floatcontainer">
</HeaderTemplate>
<ItemTemplate>
<div class="<%# GetThumbnailCssClass(Container.DataItem.GetType()) %>">
<div class="op0" style="width: <%# (Convert.ToInt32(DataBinder.Eval(Container.DataItem, "Thumbnail.Width")) + 15).ToString() %>px;
height: <%# (Convert.ToInt32(DataBinder.Eval(Container.DataItem, "Thumbnail.Height")) + 10).ToString() %>px;">
<div class="op1">
<div class="op2">
<div class="sb">
<div class="ib">
<a href="<%# GenerateUrl((IGalleryObject) Container.DataItem) %>" title="<%# GetHovertip((IGalleryObject) Container.DataItem) %>">
<img src="<%# GetThumbnailUrl((IGalleryObject)Container.DataItem) %>" alt="<%# GetHovertip((IGalleryObject) Container.DataItem) %>"
style="width: <%# DataBinder.Eval(Container.DataItem, "Thumbnail.Width").ToString() %>px;
height: <%# DataBinder.Eval(Container.DataItem, "Thumbnail.Height").ToString() %>px;" /></a></div>
</div>
</div>
</div>
</div>
<%# GetGalleryObjectText(Eval("Title").ToString(), Container.DataItem.GetType())%>
</div>
</ItemTemplate>
<FooterTemplate>
</div>
</FooterTemplate>
</asp:Repeater>
<asp:PlaceHolder ID="phPagerBtm" runat="server" EnableViewState="false" />
Hope this explains how I did it. No really big deal yet but quite useful for my gallery visitors looking for the latest images.
It would be very nice if this feature makes it into one of the next releases so I am not on a dead end with my GSP installation but still can upgrade ;-)
What do you think about this?
Cheers,
Marc