I'm using the FlickrNet c# library that so far is proving a massive hit.
However, I've got one problem. I can get all photos for a user id but I can't get the set name that a specific photo belongs to without doing another api call. This separate call is giving me massive performance issues. The homepage takes 30 seconds to load 40 images. This will only increase.
I need to get the set name for each photo as I'm using isotope to display the images.
I'm simply grabbing the images from the api and binding them to a repeater.
Here's my code.
C#
Flickr f = FlickrManager.GetAuthInstance();
protected void Page_Load(object sender, EventArgs e)
{
string userID = f.PeopleFindByEmail("me#yahoo.com").UserId;
PhotoSearchOptions options = new PhotoSearchOptions
{
UserId = userID,
Extras = PhotoSearchExtras.AllUrls | PhotoSearchExtras.Description | PhotoSearchExtras.Tags,
SortOrder = PhotoSearchSortOrder.Relevance,
};
var photos = f.PhotosSearch(options);
rptPhotos.DataSource = photos;
rptPhotos.DataBind();
}
protected string GetSetNameForImageID(string imageID)
{
var sets = f.PhotosGetAllContexts(imageID).Sets;
return sets[0].Title.ToLower().Replace(" ", "-");
}
HTML
<asp:Repeater runat="server" ID="rptPhotos">
<ItemTemplate>
<section class="<%# GetSetNameForImageID( Convert.ToString( Eval("PhotoID") ) ) %> item">
<%--<a href="#Url.Action("Image", "Home", new {id = item.PhotoId})">--%>
<a href="/View.aspx?pid=<%# DataBinder.Eval(Container.DataItem, "PhotoID") %>">
<div>
<div class="item_hover">
<header>
<span>D</span>
<%--<p title="#item.Description" class="tiptip">_</p> --%>
<p title="<%# DataBinder.Eval(Container.DataItem, "Description") %>" class="tiptip">_</p>
<hgroup>
<%--<h2>#item.Title</h2>
<h3>#item.Tags[0]</h3>--%>
<h2><%# DataBinder.Eval(Container.DataItem, "Title") %></h2>
<h3><%# DataBinder.Eval(Container.DataItem, "Tags[0]") %></h3>
</hgroup>
</header>
</div>
<%--<img src="#item.Small320Url" alt="Video sit amet consectetur" />--%>
<img src="<%# DataBinder.Eval(Container.DataItem, "Small320Url") %>" alt="Video sit amet consectetur" />
</div>
</a>
</section>
</ItemTemplate>
</asp:Repeater>
Is there a quicker way of getting the set name for each image without the GetSetNameForImageID and subsequent api call?
One thing I noticed about the PhotosGetAllContexts is that the Sets property you are using is a collection of ContextSet objects, which in turn store a PhotoSetId and a Title.
What about combining the PhotosetsGetList and PhotosetsGetPhotos methods? I think you could use these two to avoid the overhead you are currently experiencing with the PhotosGetAllContexts method.
Disclaimer: not tested code.
var photoSets = f.PhotosetsGetList(userId);
Dictionary<string, List<Photo>> photoSetPhotoMap = new Dictionary<string, List<Photo>>();
foreach (var photoSet in photoSets)
{
var photoSetPhotos = f.PhotosetsGetPhotos(photoSet.PhotosetId,
PhotoSearchExtras.AllUrls | PhotoSearchExtras.Description | PhotoSearchExtras.Tags);
photoSetPhotoMap.Add(photoSet.PhotosetId, photoSetPhotos.ToList());
}
var photos =
from kvp in photoSetPhotoMap
from photo in kvp.Value
select new
{
SetTitle = kvp.Key, PhotoId = photo.PhotoId, Description = photo.Description,
PhotoTitle = photo.Title, Tags = photo.Tags[0], Small320Url = photo.Small320Url
};
rptPhotos.DataSource = photos;
rptPhotos.DataBind();
I think the main difficulty you will have with this approach is implementing paging effectively - depending on the size of a photoset (you can determine this by the NumberOfPhotos property) you will likely run across scenarios where a page cuts off halfway through a photoset.
To solve this I would suggest using the NumberOfPhotos property to calculate the total number of photos and then using your page size multiplied by the page number to figure out how many and which PhotoSets you need to get to fulfill the request.
Related
I am experimenting with CodedUI using C# and am having some trouble. I have got a <div> containing an ID, and then a non-uniquely identified <ul> with several <li> tags like the below:
<div id="uniqueIdValue" class"long_class" activetabindex="0" sfwtabcontainer="true">
<ul class="long_class2">::before
<li class="sharedListItemClass" sfwtabheader="true">...</li>
<li class="sharedListItemClass" sfwtabheader="true">
History
</li>
<li class="sharedListItemClass" sfwtabheader="true">...</li>
::after
</ul>
How can I setup a Mouse.Click(...) on the the second list item (or the <a> within) containing my <a> tag with test of "History"?
I have tried every combination I can think of to specify this tag by itself, all the way up to specifying the parent and trying to get the "TagInstance":
var container = new HtmlControl(_bw);
HtmlDiv nameForDiv = new HtmlDiv(container);
nameForDiv.SearchProperties[HtmlDiv.PropertyNames.Id] = "uniqueDivIdentifier";
nameForDiv.SearchProperties[HtmlDiv.PropertyNames.Class] = "long Multiple_Classes";
var historyTab = new HtmlHyperlink(nameForDiv);
historyTab.SearchProperties[HtmlHyperlink.PropertyNames.Href] = "#someValue";
historyTab.SearchProperties[HtmlHyperlink.PropertyNames.TagInstance] = "2";
Mouse.Click(historyTab)
Resulting in the control cannot be found. Can anyone help me with this?
Something like this:
var browser = BrowserWindow.Launch("url");
private void ClickLink()
{
var parent = GetPaneID(browser, "uniqueIdValue");
var child = parent.GetChildren()[0];
var child2 = child.GetChildren()[1];
var child3 = child2.GetChildren()[0];
Mouse.Click(child3);
}
where method GetPaneId() looks like this:
public HtmlDiv GetPaneID(UITestControl parent, string id)
{
var gpane = new HtmlDiv(parent);
gpane.SearchProperties.Add(HtmlDiv.PropertyNames.Id, id);
return gpane;
}
I have created a property under a MemberType called "testField" which is using a custom DataType dropdown list with 2 values in it (Employee and Subcontractor). I would like for a member to be able to select one, submit a form and to store their selection under this property, but I can't seem to figure it out.
Is it possible to do this whilst being able to change their selection manually in the back end?
Here's my code...
ASCX
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="Register.ascx.cs" Inherits="nForum.usercontrols.nForum.membership.Register" %>
<div id="forumregistration" class="validate">
<dl class="form">
<dt><label for="<%= tbLoginName.ClientID %>">Login/Username:</label></dt>
<dd><asp:TextBox ToolTip="Enter username" CssClass="required" ID="tbLoginName" runat="server" /></dd>
<dt><label for="<%= tbName.ClientID %>">Full Name:</label></dt>
<dd><asp:TextBox ToolTip="Enter name" CssClass="required" ID="tbName" runat="server" /></dd>
<dt><label for="<%= tbEmail.ClientID %>">Email:</label></dt>
<dd><asp:TextBox ToolTip="Enter email address" CssClass="required email" ID="tbEmail" runat="server" /></dd>
<dt><label for="<%= tbPassword.ClientID %>">Password:</label></dt>
<dd><asp:TextBox ToolTip="Enter a password" CssClass="required password" ID="tbPassword" TextMode="Password" runat="server" /></dd>
<dt><label for="">Status:</label></dt>
<asp:dropdownlist ID="tbOrganisation" runat="server">
<asp:ListItem Text="Choose" Value="0"></asp:ListItem>
<asp:ListItem Text="Employee" Value="1"></asp:ListItem>
<asp:ListItem Text="Subcontractor" Value="2"></asp:ListItem>
</asp:dropdownlist>
<dt> </dt>
<dd><asp:Button ID="btnSubmit" CssClass="textarea" runat="server" Text="Create Account" onclick="BtnSubmitClick" /></dd>
</dl>
</div>
CS
using System;
using System.Text;
using System.Web.Security;
using nForum.BusinessLogic;
using umbraco;
using umbraco.cms.businesslogic.member;
namespace nForum.usercontrols.nForum.membership
{
public partial class Register : BaseForumUsercontrol
{
protected void BtnSubmitClick(object sender, EventArgs e)
{
var redirecturl = Settings.Url;
// Check the user isn't already registered
if (Member.GetMemberFromEmail(Helpers.GetSafeHtml(tbEmail.Text)) == null & Member.GetMemberFromLoginName(Helpers.GetSafeHtml(tbLoginName.Text)) == null)
{
// Set the member type and group
var mt = MemberType.GetByAlias(MembershipHelper.ForumUserRoleName);
var addToMemberGroup = MemberGroup.GetByName(MembershipHelper.ForumUserRoleName);
//create a member
var m = Member.MakeNew(Helpers.GetSafeHtml(tbName.Text), mt, new umbraco.BusinessLogic.User(0));
//var mstatus = new MembershipCreateStatus();
//var mp = Membership.CreateUser(tbName.Text, tbPassword.Text, tbEmail.Text, string.Empty, string.Empty, true, out mstatus);
// Set the other properties
m.Email = Helpers.GetSafeHtml(tbEmail.Text);
m.LoginName = Helpers.GetSafeHtml(tbLoginName.Text);
m.Password = Helpers.GetSafeHtml(tbPassword.Text);
// Add 0 Karma to user, helps us later in the site
m.getProperty("forumUserKarma").Value = 0;
m.getProperty("forumUserAllowPrivateMessages").Value = 1;
m.getProperty("forumUserLastPrivateMessage").Value = DateTime.Now;
// Take selected dropdown value and store
m.getProperty("testField").Value = tbOrganisation;
//##### Manual Member Authorisation #####
// If this is not enabled, mark the member as authorised
if (!Settings.ManuallyAuthoriseNewMembers)
{
m.getProperty("forumUserIsAuthorised").Value = 1;
}
m.AddGroup(addToMemberGroup.Id);
//Save member
m.Save();
//Generate member Xml Cache
m.XmlGenerate(new System.Xml.XmlDocument());
if (!Settings.ManuallyAuthoriseNewMembers)
{
//Login the user so they can be redirected to their profile page
FormsAuthentication.SetAuthCookie(tbLoginName.Text, false);
}
else
{
redirecturl = string.Concat(CurrentPageAbsoluteUrl, "?m=", library.GetDictionaryItem("NotifiedWhenAccountAuth"));
}
// If admins wants email notification, then send it here
if (Settings.EmailAdminOnNewMemberSignUp)
{
SendAdminNotification(m);
}
}
else
{
redirecturl = string.Concat(CurrentPageAbsoluteUrl, "?m=", library.GetDictionaryItem("UserAlreadyExists"));
}
// Now redirect to the correct page
Response.Redirect("/discuss-it");
}
private void SendAdminNotification(Member newmember)
{
var sb = new StringBuilder();
sb.AppendFormat(library.GetDictionaryItem("MemberSignUpEmailText"),
Settings.Name,
newmember.LoginName,
newmember.Text,
newmember.Email);
Helpers.SendMail(Settings.EmailNotification, Settings.EmailAdmin, library.GetDictionaryItem("NewMemberSignUp"), sb.ToString());
}
}
}
Your specific issue is two fold:
1) You are setting m.getProperty("testField") to the instance of the DropDownList
rather than to it's SelectedValue
2) As noted in the comments on the question you had a mis-match in your hardcoded DropDownList values
and the values stored in the Umbraco DataType for the testField property.
You can address the second issue more robustly by binding your DropDownList with what Umbraco calls
the PreValues stored on the testField DataType is will ensure the available options in the DropDownList
match the options defined on the DataType
As of writing this PreValues are stored as an XML fragment on the datatype definition in
the following format:
<preValues>
<preValue id="1">Option 1</preValue>
<preValue id="2">Option 2</preValue>
<preValue id="3">Option 3</preValue>
</preValues>
To retrieve these programmatically is a little clunky (IMO); You first you have to obtain the Id of DataType by
mousing over the DataType in the umbraco interface and looking in the status bar (you should see something like javascript:openDataType(1111) where 1111 is the Id.
Next call the umbraco.library.GetPreValues() method, passing in the Id obtained above. This method
returns an XPathNodeIterator which you can use to get the values in whichever way suits your need such as creation of a Dictionary<string,string> which you can bind to your DropDownList. The following example is taken from Damiaan Peeters blog
and adapted ever so slightly.
private static Dictionary<string, string> GetPreValues(int dataTypeId)
{
XPathNodeIterator preValueRootElementIterator = umbraco.library.GetPreValues(dataTypeId);
preValueRootElementIterator.MoveNext(); //move to first
XPathNodeIterator preValueIterator = preValueRootElementIterator.Current.SelectChildren("preValue", "");
var retVal = new Dictionary<int, object>();
while (preValueIterator.MoveNext())
retVal.Add(preValueIterator.Current.GetAttribute("id", ""), preValueIterator.Current.Value);
return retVal;
}
Then in your code behind you can do the following:
tbOrganisation.DataSource = GetPreValues(1111);
tbOrganisation.DataTextField = "Value";
tbOrganisation.DataValueField = "Key";
tbOrganisation.DataBind();
I need to keep using a list box to do this as there are a number of other dependencies on the control being a list box.
Ultimately I want each list item to consist of 3 distinct URL's so:
Url1 Url2 Url3
which would be HTML of:
<ul>
<li>Url1Url2Url3</li>
</ul>
So in my ASPX page I am maintaing there is already:
<asp:BulletedList ID="lstDashboards" runat="server" DisplayMode="Text"</asp:BulletedList>
And in code behind I have the following to populate the list:
private void GuiSideMenuBuild(Int64 aUserId)
{
//Always clear any dashboards
lstDashboards.Items.Clear();
//Get the dashboards from the database
DashboardsForUserGetDto userDashboards = DashboardBL.Instance.DashboardsForUserGet(Convert.ToInt32(aUserId));
const string HREF_DASHBOARD_EDIT = "Edit...";
const string HREF_DASHBOARD_DELETE = "Delete...";
foreach (TableDashboardDashboardDtoBase userDashboard in userDashboards.Dashboards)
{
string listItemText = userDashboard.Title;
if (userDashboard.DashboardId == DashboardId)
listItemText += HREF_DASHBOARD_EDIT + HREF_DASHBOARD_DELETE;
ListItem listItem = new ListItem
{
Text = listItemText,
Value = Convert.ToString(userDashboard.DashboardId),
Selected = userDashboard.DashboardId == DashboardId
};
lstDashboards.Items.Add(listItem);
}
}
However the HTML that the control is producing has escaped all the supplied HTML so that I end up with something like:
<<a href="#" id="...
which means that the resulting HTML is "broken" and the URL's don't work. So my question is:
How can I keep using the existing list box (ie. asp:BulletedList) but supply it Text for the item so that it will produce valid HTML so that I can have 3 separate URL's?
I have already tried HTML Encoding the string before adding it to listItem.Text but that makes no difference.
I don't see any need for a UserControl here - you don't use any server-side components, you're just generating markup. Why not just do this in your .aspx page?
<ul>
<% foreach (TableDashboardDashboardDtoBase userDashboard in userDashboards.Dashboards)
{
%>
<li>Url1Url2Url3</li>
<%
}
%>
</ul>
Either that, or use a repeater:
HTML
<asp:Repeater ID="Repeater1" runat="server">
<HeaderTemplate><ul></HeaderTemplate>
<ItemTemplate><li>Url1Url2Url3</li></ItemTemplate>
<FooterTemplate></ul></FooterTemplate>
</asp:Repeater>
Code Behind
Repeater1.DataSource = userDashboards.Dashboards
Repeater1.DataBind()
First problem is the list of what pops up is far below the actual textbox. See pic.
2nd problem is it takes a full second or two to get any results using auto completion. I doubt its database/retrieval related, because using a local string array of names, gives me the same results.
.aspx
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:TextBox ID="txtFrom" runat="server">
</asp:TextBox>
<ajaxToolkit:AutoCompleteExtender ID="AutoCompleteExtender1" runat="server" TargetControlID="txtFrom" MinimumPrefixLength="1" ServiceMethod="GetSuggestions" EnableCaching="true">
</ajaxToolkit:AutoCompleteExtender>
My web method is in my cs class
[System.Web.Services.WebMethod]
[System.Web.Script.Services.ScriptMethod]
public static string[] GetSuggestions(string prefixText, int count)
{
IList<String> donors = new List<String>();
NHibernateSessionManager sessionManager = new NHibernateSessionManager();
NHibernate.ISession session = sessionManager.GetSession();
NHibernateDataProvider2 provider = new BT4SGWebApplication.NHibernateDataProvider2(session);
ExtraUserInfo user = provider.CRIT_GetDistinctExtraUserInfoByUserName(System.Web.HttpContext.Current.User.Identity.Name)[0];
return user.GroupTable.PayorDonors.Where(x => x.Name.FullName.StartsWith(prefixText)).OrderBy(x => x.Name.FullName)
.Select<PayorDonor, string>(x => x.Name.FullName).ToArray();
}
PS - hit isn't my last name.
Try setting a low CompletionInterval on your AutoCompleteExtender. A big part of the delay you're running into is probably a result of this (1 second default) delay.
I want to display time in textbox or in something like a numericupdownextender used in AJAX so that the user can change time as he desires..
i have used the control to show numbers and increase accordingly..
is there a way to do this..
new code but not what is desired...
<asp:TextBox runat="server" ID="txtHour"></asp:TextBox>
<ajaxToolkit:NumericUpDownExtender ID="txtHour_NumericUpDownExtender" runat="server" Enabled="True" Maximum="12" Minimum="1" TargetControlID="txtHour" Width="70"></ajaxToolkit:NumericUpDownExtender>
<asp:TextBox runat="server" ID="txtMinute"></asp:TextBox>
<ajaxToolkit:NumericUpDownExtender ID="txtMinute_NumericUpDownExtender" runat="server" Enabled="True" Maximum="60" Minimum="1" TargetControlID="txtMinute" Width="70"></ajaxToolkit:NumericUpDownExtender>
<asp:TextBox runat="server" ID="txtDayPart"></asp:TextBox>
<ajaxToolkit:NumericUpDownExtender ID="txtDayPart_NumericUpDownExtender" runat="server" Enabled="True" RefValues="AM;PM" TargetControlID="txtDayPart" Width="70"></ajaxToolkit:NumericUpDownExtender>
the code behind is:
private void ParseTime(string TimeString)
{
// Validation of input
if (TimeString.IndexOf(":") == -1)
{
return;
}
if ((TimeString.IndexOf("PM") == -1) && (TimeString.IndexOf("AM") == -1))
{
return;
}
// Good to go with format
int ColonPos = TimeString.IndexOf(":");
int AMPos = TimeString.IndexOf("AM");
int PMPos = TimeString.IndexOf("PM");
string sHour = TimeString.Substring(0, ColonPos);
string sMinutes = TimeString.Substring(ColonPos, 3); string sDayPart = (TimeString.IndexOf("AM") != -1) ? TimeString.Substring(AMPos, 2) : TimeString.Substring(PMPos, 2);
txtHour.Text = sHour;
txtMinute.Text = sMinutes;
txtDayPart.Text = sDayPart;
}
Yes this should be pretty simple to achieve using the updownextender. Just attach web service methods to the serviceupmethod and servicedownmethod which increment/decrement your datetime by the required timespan. you haven't posted any code so it's difficult to know where you are stuck.
UPDATE: ok, so having thought about this, I don't think there is any real reason to use an updownextender with server call backs. A quick google discovered that javascript already has some basic date manipulation functions, so it's easy enough to do everything client side.
I'm not a javascript expert, so the following code is possibly of questionable quality, but it seems to work ok and hopefully will get you set on the right track. Let me know if you still get stuck.
<head runat="server">
<title></title>
<script type="text/javascript" language="javascript">
<!--
var date;
function initDateObject()
{
date = new Date ( "January 1, 2000 12:00:00" );
showTimePortion();
}
function showTimePortion()
{
document.getElementById('timeDisplay').value = padToMinimumLength(date.getHours(),2) + ':' + padToMinimumLength(date.getMinutes(),2) + ':' + padToMinimumLength(date.getSeconds(),2);
}
function padToMinimumLength(number, requiredLength)
{
var pads = requiredLength - (number + '').length;
while (pads > 0)
{
number = '0' + number;
pads--;
}
return number;
}
function addMinutes(n)
{
date.setMinutes(date.getMinutes() + n);
showTimePortion();
}
function setTodaysTime()
{
var d = new Date();
d.setHours(date.getHours());
d.setMinutes(date.getMinutes());
d.setSeconds(date.getSeconds());
alert('the time is now ' + d.toString());
}
-->
</script>
</head>
<body onload="initDateObject();">
<form id="form1" runat="server">
<div>
<input type="text" id="timeDisplay" readonly="readonly"/>
+
-
<br />
Submit
</div>
</form>
</body>
Hrmm, I recommend you spend some time playing around with jQuery if you haven't. Hope it helps.
jQuery UI
John Resig
lovemore-world has a good article that hopefuly can inspire you and get the creative juices going.