SimpleAddress from PropertyUrl - c#

I have certain page having both long (Page1/Page2/MyPage) and simple (MyPage) addresses.
Then I want to reference it in certain place via PropertyUrl:
[CultureSpecific]
[Required]
[BackingType(typeof(PropertyUrl))]
[Display(
Name = "Link",
Description = "Link to the page",
GroupName = SystemTabNames.Content,
Order = 1)]
public virtual Url Link { get; set; }
I want the simple address (if it exists) to be used for the routing or url rendering but not the long one.
I am looking for some elegant solution for it if it exists

Got an answer from Brad McDavid
Modified it a bit to fit better to my task:
public static string GetExternalUrl(this Url url)
{
var content = UrlResolver.Service.Route(new UrlBuilder(url));
return GetExternalUrl(content);
}
public static string GetExternalUrl(this ContentReference contentReference)
{
if (ContentReference.IsNullOrEmpty(contentReference)) return null;
var content = ServiceLocator.Current.GetInstance<IContentLoader>().Get<IContent>(contentReference);
return GetExternalUrl(content);
}
public static string GetExternalUrl(this IContent content)
{
var externalProperty = content?.Property["PageExternalURL"];
return !string.IsNullOrWhiteSpace(externalProperty?.ToString()) ? $"/{externalProperty.ToString().Trim('/')}/" : null;
}

Related

Dynamic Model Properties, get model property by name

Not sure how to word this...
I have a Model, here is a section of it:
public class AnswerSheet
{
public string Q1 { get; set; }
public string Q2 { get; set; }
public string Q3 { get; set; }
public string Q4 { get; set; }
I am using a Viewmodel to reuse the same view to answer each question separately. It is almost working. Is there any way I can use my controller as follows to dynamically assign the model.q#, ex:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateNextQ([Bind(Include = "ID, qCounter, Question,Comment")] AnswerSheetCreateVM answerVM)
{
if (ModelState.IsValid)
{
string questionAns = answerVM.Question + answerVM.Comment;
AnswerSheet answer= db.AnswerSheets.Find(answerVM.ID);
//THIS PART HERE IS WHERE I HAVE A PROBLEM
answer.Q(answerVM.qCounter) = questionAns;
//That one line above
db.AnswerSheets.Add(answer);
db.SaveChanges();
So basically can I get data from my controller variable (qCounter in this case) and assign it to my model like Model.Q(qcounter)
As a side note I am open to suggestion on how to word this question or what tags to assign to it.
I found this post useful:
Set object property using reflection
This is what I ended up doing, still have to test performance:
string test = string.Format("Q" + answerVM.qCounter);
string questionAns = (answerVM.Question + " - " + answerVM.NoComment);
AnswerSheet answer= db.AnswerSheets.Find(answerVM.ID);
if (answer== null)
{
return HttpNotFound();
}
answer.GetType().GetProperty(test).SetValue(answer, questionAns, null);
db.Entry(answer).State = EntityState.Modified;
db.SaveChanges();
Maybe that will help someone down the line....NOt sure if this counts as using reflection....

Umbraco 7 Rendering media type images

Am trying to render a list of media images.
In my view I have:
View
#section pageSpecificJsBody {
<script src="/scripts/casestudieslist.js"></script>
<script>
$(function () { pdms.caseStudiesList.init(); });
</script>
}
Which is used to call a js file
The js file calls the following controller
Controller
[HttpGet]
public JsonResult List()
{
var CaseStudyContentTypeId = Services.ContentTypeService.GetContentType("CaseStudy").Id;
var CaseStudies = Services.ContentService.GetContentOfContentType(CaseStudyContentTypeId).Select(x => new CaseStudy {
BannerImage = Umbraco.Content(x.Id).GetPropertyValue("bannerimage"),
Url = Umbraco.Content(x.Id).Url.ToString(),
SectorName = Umbraco.Content(x.GetValue("selectSector")).Name, //x.GetValue("selectSector").ToString(),
BodyTextHeading = x.GetValue("bodyTextHeading").ToString(),
BannerHeading = x.GetValue("bannerheading").ToString()
});
Model
public class CaseStudy
{
public string SectorName { get; set; }
//public int Id { get; set; }
public string Url { get; set; }
public string BannerHeading { get; set; }
public string BannerImage { get; set; }
public string BodyTextHeading { get; set; }
}
Previously the Banner Image was using a media picker so the images could be accessed through Umbraco.Content, but I have now set them all to use a custom cropper which set them to media types
My question is... how can I now set the BannerImage property to get the relevant media image?
normally I could do something similar to this in a view.
var BannerImage = Model.Content.GetPropertyValue("bannerimage");
var MediaImage = Umbraco.TypedMedia((int)BannerImage);
<source srcset="#MediaImage.GetCropUrl("desktopMax")" />
But I don't have access to the model since am in the controller, and am really stuck, am still new to Umbraco and don't yet fully understand everything, so sorry if things are not clear.
Thanks in advance
You can get the #Umbraco helper in a lot of places (including the controller) by doing:
UmbracoHelper umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
I would probably rewrite your code to look like this:
var caseStudies = from caseStudy in Services.ContentService.GetContentOfContentType(CaseStudyContentTypeId)
let content = umbracoHelper.TypedContent(caseStudy.Id)
let bannerImage = umbracoHelper.TypedMedia(caseStudy.GetPropertyValue("bannerimage"))
let sector = umbracoHelper.TypedContent("selectSector")
select new CaseStudy {
BannerImage = bannerImage.Url,
Url = content.Url,
SectorName = sector.Name,
BannerHeading = caseStudy.GetPropertyValue<string>("bannerheading"),
BodyTextHeading = caseStudy.GetPropertyValue<string>("bodyTextHeading"
};
I found this post Here, which has a good suggestion on obtaining the crop url for an image.
Here's what I've wrote to solve the issue:
Controller
var CaseStudyContentTypeId = Services.ContentTypeService.GetContentType("CaseStudy").Id;
var CaseStudies = Services.ContentService.GetContentOfContentType(CaseStudyContentTypeId).Select(x => new CaseStudy
{
BannerImage = Umbraco.TypedMedia(x.GetValue<int>("bannerimage")).GetCropUrl("umbracoFile", "mobile"),
Url = Umbraco.Content(x.Id).Url.ToString(),
SectorName = Umbraco.Content(x.GetValue("selectSector")).Name, //x.GetValue("selectSector").ToString(),
BodyTextHeading = x.GetValue("bodyTextHeading").ToString(),
BannerHeading = x.GetValue("bannerheading").ToString()
});
Am going to debug and test out Sams method so I can work out the difference between both examples. If anyone (or Sam) could offer suggestions as to why they believe one way could potentially be more beneficial than the other please could you give an explanation.
Thanks in advance.

Refactoring Many Methods into One

I dont know how to name the question properly, so fell free to change it. My question is, I have around 10 methods that look like:
[WebMethod(EnableSession = true)]
public string ReadUserAdditional()
{
EUser user = (EUser)Session["user"];
var json = new { result = true, user.Image, user.Biography };
return new JavaScriptSerializer().Serialize(json);
}
[WebMethod(EnableSession = true)]
public string ReadUserBasicInformation()
{
EUser user = (EUser)Session["user"];
var json = new { result = true, user.Name, user.Username};
return new JavaScriptSerializer().Serialize(json);
}
The methods are very similar, but they return different fields. Im thinking about refactoring all methods into one, receveing the fields to return as parameters. Is it a good idea? How can I do that? Reflection?
First of all you need to know that object and dictionary are presented in json simmilar.
[WebMethod(EnableSession = true)]
public string ReadUserAdditional()
{
return GetUserInfo(new []
{
new FieldInfo {Name = "Image", u => u.Image},
new FieldInfo {Name = "Biography", u => u.Biography}
});
}
private string GetUserInfo(FieldInfo[] infos)
{
EUser user = (EUser)Session["user"];
var dict = new Dictionary<string, object>{ { "result", true } };
foreach(var info in infos)
{
dictionary.Add(info.Name, info.Accessor(user));
}
return new JavaScriptSerializer().Serialize(dict );
}
public class FieldInfo
{
public Func<EUser, object> Accessor { get; set; }
public string Name { get; set;}
}
I don't think it's a terrible idea, especially if you have tons of these methods and want to simplify your API.
A few downsides:
1) Reflection comes at a perf cost. This probably doesn't matter a whole lot unless you're the size of Twitter.
2) There would potentially be security concerns if the data had any properties you do NOT wanting users getting access to, such as some sort of internal database keys or what not. Make sure every property on your class is one you're totally okay becoming public information.
You can use a lambda to refactor away the duplication:. This would reduce all your methods to a single line of code:
[WebMethod(EnableSession = true)]
public string ReadUserAdditional()
{
return GetUserJSON(x => new { result = true, x.Image, x.Biography });
}
[WebMethod(EnableSession = true]
public string ReadUserBasicInformation()
{
return GetUserJSON(x => new { result = true, x.Name, x.UserName });
}
private string GetUserJSON(Func<EUser, string> jsonFields)
{
EUser user = (EUser)Session["user"];
var json = jsonFields(user);
return new JavaScriptSerializer().Serialize(json);
}
Another approach is to use Automapper or similar library to project your data.
[WebMethod(EnableSession = true)]
public string ReadUserAdditional()
{
return GetUserInfo<UserAdditionalDto>();
}
private string GetUserInfo<TDto>(FieldInfo[] infos)
{
EUser user = (EUser)Session["user"];
var dto = Mapper.Map<TDto>(user); // Mapper is Automapper entry class.
return new JavaScriptSerializer().Serialize(dto );
}
public class UserAdditionalDto
{
public string Image { get; set; }
public string Biography { get; set;}
}

C# problem with object array

I am fairly new to arrays in C# and am used to storing a mass of data in a string and in INI files and then breaking it down into basic arrays using delimiters...so yeh, my knowledge is almost none existent.
My main form class begin this definition:
public CAirportData[] _AirportData; //size not known
This is the method I am using to create the array:
...string[] airports = possibleAirports.Split(','); //size is known
foreach (string airport in airports)
{
string[] rwys = inif.Read(airport, "rwys").Split(':'); //size is known (2)
_AirportData = new CAirportData[] { new CAirportData() { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] } };
}
I know this just boils down to my limited knowledge of objects and arrays. But I can't seem to find anything on the internet that uses this sort of thing. I have tried to combine other peoples code with little success.
I need the _AirportData array to be available outside of the form hence public and declared outside of any methods. I supose the main problem is that I am overwriting array and foreach airport I am creating a new array hence loosing the previous. I had tried moving the ..= new CAirportData[] to all sorts of places but Visual Studio doesn't like it.
Below is the class definition for CAirportData:
public class CAirportData
{
public string icao { get; set; }
public string depRwy { get; set; }
public string arrRwy { get; set; }
public override string ToString()
{
string result = string.Format("ICAO: {0}, Dep: {1}, Arr: {2}", this.icao, this.depRwy, this.arrRwy);
return result;
}
}
public class CMRunways
{
public string icao { get; set; }
public string depRwy { get; set; }
public string arrRwy { get; set; }
}
Many thanks in advance for any help!
What you're looking for is generic List. Change the definition to:
public List<CAirportData> _AirportData = new List<CAirportData>();
Then the code in the loop to:
_AirportData.Add(new CAirportData { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] });
This is what I would do...Create a static class, with a static property (airports) and add a static constructor to load the airports from file at the begining.
public static class Session
{
public static CAirportData[] _AirportData;
static Session()
{
string airports = possibleAirports.Split(",");
foreach (string airport in airports)
{
string[] rwys = inif.Read(airport, "rwys").Split(':'); //size is known (2)
_AirportData = new CAirportData[] { new CAirportData() { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] } };
}
}
}
Now you can access the array anywhere in the project like
MessageBox.Show(Session.CAirportData[0].depRwy);

Writing a Workflow Foundation workflow with C#

I'm trying to write some activities with C# instead of the designer and XAML. VS2010 has been buggy and very slow for that, and it also has very poor compilation support (for variables names, properties and so on).
So I'm trying to create activities by inheriting from the Activity class directly, but I'm encountering a snag.
Here's my code:
public class TestActivity : Activity
{
public InArgument<string> Username { get; set; }
public InArgument<string> Password { get; set; }
public OutArgument<bool> ValidCredential { get; set; }
public OutArgument<ProvisioningRole> Role { get; set; }
public OutArgument<Guid> Guid { get; set; }
protected override Func<Activity> Implementation
{
get
{
return () =>
{
return new Sequence()
{
Activities =
{
new AuthenticateUserActivity()
{
Username = this.Username,
Password = this.Password,
Guid = this.Guid,
Result = this.ValidCredential
},
new If()
{
Condition = this.ValidCredential,
Then = new GetUserRoleActivity()
{
Username = this.Username,
Password = this.Password,
Result = this.Role
}
},
}
};
};
}
set { base.Implementation = value; }
}
}
The problem is with the If(), the condition. It's supposed to be an InArgument, but this.ValidCredential is an OutArgument. I've tried creating a Variable, assign the value of ValidCredential to it. I also tried to put the result of AuthenticateUserActivity in the variable and then assign it to ValidCredential, but I get an error saying the To property of Assign needs to be specified.
I've looked around for proper tutorials, but all I found was an MSDN article that had a quick and dirty code implementation, and it used literals instead of the passed arguments, so no help from there.
I found out how to do it. You just need to create new InArgument from the original one. There is a constructor that takes an expression for it.
Username = new InArgument<bool>((ActivityContext c) => this.ValidCredential.Get(c))
So I changed my whole activity to
return new CompensableActivity()
{
Body = new Sequence()
{
Activities =
{
new AuthenticateUserActivity()
{
Username = this.Username.In(),
Password = this.Password.In(),
Guid = this.Guid.Out(),
Result = this.ValidCredential.Out()
},
new If(this.ValidCredential.In())
{
Then = new GetUserRoleActivity()
{
Username = this.Username.In(),
Password = this.Password.In(),
Result = this.Role.Out()
},
Else = new Assign<ProvisioningRole>()
{
To = this.Role.Out(),
Value = ProvisioningRole.User
}
}
}
},
};
In and Out being extension methods I wrote:
public static class WorkflowExtensions
{
#region In
public static InArgument<T> In<T>(this InArgument<T> self)
{
return new InArgument<T>(context => self.Get(context));
}
public static InArgument<T> In<T>(this OutArgument<T> self)
{
return new InArgument<T>(context => self.Get(context));
}
#endregion
#region Out
public static OutArgument<T> Out<T>(this InArgument<T> self)
{
return new OutArgument<T>(context => self.Get(context));
}
public static OutArgument<T> Out<T>(this OutArgument<T> self)
{
return new OutArgument<T>(context => self.Get(context));
}
#endregion
}
And now all is well!
You should be able to get this to work. The basic approach should be to use a Variable to store data, use an OutArgument to get data out of activities into the Variable and InArguments to get data from a Variable into an activity.
Also note that the expressions to tie InArguments to Variables are VisualBasicValue expressions. So something like:
Condition = new VisualBasicValue("System.DateTime.Now.Hour < 12")
This blog post isn't about using arguments and variables but shows a couple of examples.
Going to shamelessly plug my own library that I ended up making for this:
http://code.google.com/p/system-transactions/
Allows basic compensation of code without the ginormous hassle of WF. Also, compiles properly and is easily debuggable.

Categories

Resources