I'm developing an application with ASP.NET MVC 4 and I wanna my URLs look like below :
http://www.mysite.com/Product/Category/2/دربهای-چوبی
to do that I'm using this kind of URL pattern in Global.asax :
routes.MapRoute(
name:"ViewProduct",
url:"Product/Category/{Id}/{productName}",
defaults: new { controller = "Product", action = "Category", id = UrlParameter.Optional, productName = "" }
);
and in my view I'm using generating my URLs with this way :
#item.Name
as you can see I'm create extension method called ToFriendlyUrl :
public static string ToFriendlyUrl(this UrlHelper helper,
string urlToEncode)
{
urlToEncode = (urlToEncode ?? "").Trim().ToLower();
StringBuilder url = new StringBuilder();
foreach (char ch in urlToEncode)
{
switch (ch)
{
case ' ':
url.Append('-');
break;
case '&':
url.Append("and");
break;
case '\'':
break;
default:
if ((ch >= '0' && ch <= '9') ||
(ch >= 'a' && ch <= 'z'))
{
url.Append(ch);
}
else
{
url.Append('-');
}
break;
}
}
return url.ToString();
}
above way works correctly for English URLs(productName's segment) :
http://www.mysite.com/Product/Category/3/category-type-one
but for Persian URLs I'm getting this URL for example :
http://www.mysite.com/Product/Category/1/--------------
for example second one should have be:
http://www.mysite.com/Product/Category/2/دربهای-چوبی
How can I change the code to work with Persian URLs?
PS : I know in Persian language we don't have lower and upper cases and I omitted this line of code :
urlToEncode = (urlToEncode ?? "").Trim().ToLower();
But still I've same problem.
Thanks
This if
if ((ch >= '0' && ch <= '9') ||
(ch >= 'a' && ch <= 'z'))
means only English numbers and characters are allowed. But Persian characters have a different range:
public static bool ContainsFarsi(string txt)
{
return !string.IsNullOrEmpty(txt) &&
Regex.IsMatch(txt, #"[\u0600-\u06FF]");
}
+
Don't use that ToFriendlyUrl method. Create an extension method to apply your filtering and then use the standard Html.ActionLink method and pass your parameters as new route values or it's better to use the T4MVC HTML helper methods.
By constructing your links manulally, you will lose a lot of features like adjusting the root path based on the current domain or sub domain and also encoding the special characters and many outer built-in features.
Related
I am using umbraco version 7.2.6 . I want to add macro parameter of type Dropdownlist .
How can I set source (data come from database ) of dropdownlist ??
thanks
Had the same situation, I am aware that the 'proper' way to do it would be like described in http://www.richardsoeteman.net/2010/01/04/createacustommacroparametertype.aspx, but for my purpose this would have been too much fuss. What I suggest here instead is not elegant, but it's easy to implement.
Create a macro parameter type Numeric and explain in the description which number stands for which result. In the Macro Partial View assign the number to the respective result.
Example
Description of the Macro parameter:
Alias: dimension
Description: 1: 300x225 2:400x300 3:600x450 4:800x600
Type: Numeric
Code in the Macro Partial View:
var defaultdim = "medium";
if (Model.MacroParameters["dimension"] != null)
{
var dim = Convert.ToInt32( Model.MacroParameters["dimension"] );
if(dim == 1) { defaultdim = "small"; }
else if(dim == 2) { defaultdim = "medium"; }
else if(dim == 3) { defaultdim = "large"; }
else if(dim == 4) { defaultdim = "xlarge"; }
}
"small", "medium" ... are crop names and stand for the dimensions shown in the parameter description.
This one has kept me stumped for a couple of days now.
It's my first dabble with CLR & UDF ...
I have created a user defined function that takes a multiline String as input, scans it and replaces a certain line in the string with an alternative if found. If it is not found, it simply appends the desired line at the end. (See code)
The problem, it seems, comes when the final String (or Stringbuilder) is converted to an SqlString or SqlChars. The converted, returned String always contains the Nul character as every second character (viewing via console output, they are displayed as spaces).
I'm probably missing something fundamental on UDF and/or CLR.
Please Help!!
Code (I leave in the commented Stringbuilder which was my initial attempt... changed to normal String in a desperate attempt to find the issue):
[Microsoft.SqlServer.Server.SqlFunction]
[return: SqlFacet(MaxSize = -1, IsFixedLength = false)]
//public static SqlString udf_OmaChangeJob(String omaIn, SqlInt32 jobNumber) {
public static SqlChars udf_OmaChangeJob(String omaIn, SqlInt32 jobNumber) {
if (omaIn == null || omaIn.ToString().Length <= 0) return new SqlChars("");
String[] lines = Regex.Split(omaIn.ToString(), "\r\n");
Regex JobTag = new Regex(#"^JOB=.+$");
//StringBuilder buffer = new StringBuilder();
String buffer = String.Empty;
bool matched = false;
foreach (var line in lines) {
if (!JobTag.IsMatch(line))
//buffer.AppendLine(line);
buffer += line + "\r\n";
else {
//buffer.AppendLine("JOB=" + jobNumber);
buffer += ("JOB=" + jobNumber + "\r\n");
matched = true;
}
}
if (!matched) //buffer.AppendLine("JOB=" + jobNumber);
buffer += ("JOB=" + jobNumber) + "\r\n";
//return new SqlString(buffer.ToString().Replace("\0",String.Empty)) + "blablabla";
// buffer = buffer.Replace("\0", "|");
return new SqlChars(buffer + "\r\nTheEnd");
}
I know in my experiences, the omaIn parameter should be of type SqlString and when you go to collect its value/process it, set a local variable:
string omaString = omaIn != SqlString.Null ? omaIn.Value : string.empty;
Then when you return on any code path, to rewrap the string in C#, you'd need to set
return omaString == string.empty ? new SqlString.Null : new SqlString(omaString);
I have had some fun wrestling matches learning the intricate hand-off between local and outbound types, especially with CLR TVFs.
Hope that can help!
I'm creating an application which will create a large number of folders on a web server, with files inside of them.
I need the folder name to be unique. I can easily do this with a GUID, but I want something more user friendly. It doesn't need to be speakable by users, but should be short and standard characters (alphas is best).
In short: i'm looking to do something like Bit.ly does with their unique names:
www.mydomain.com/ABCDEF
Is there a good reference on how to do this? My platform will be .NET/C#, but ok with any help, references, links, etc on the general concept, or any overall advice to solve this task.
Start at 1. Increment to 2, 3, 4, 5, 6, 7,
8, 9, a, b...
A, B, C...
X, Y, Z, 10, 11, 12, ... 1a, 1b,
You get the idea.
You have a synchronized global int/long "next id" and represent it in base 62 (numbers, lowercase, caps) or base 36 or something.
I'm assuming that you know how to use your web server's redirect capabilities. If you need help, just comment :).
The way I would do it would be generating a random integer (between the integer values of 'a' and 'z'); converting it into a char; appending it to a string; and repeating until we reach the needed length. If it generates a value already in the database, repeat the process. If it was unique, store it in the database with the name of the actual location and the name of the alias.
This is a bit hack-like because it assumes that 'a' through 'z' are actually in sequence in their integer values.
Best I could think of :(.
In Perl, without modules so you can translate more easly.
sub convert_to_base {
my ($n, $b) = #_;
my #digits;
while ($n) {
my $digits = $n % $b;
unshift #digits, $digit;
$n = ($n - $digit) / $b;
}
unshift #digits, 0 if !#digits;
return #digits;
}
# Whatever characters you want to use.
my #digit_set = ( '0'..'9', 'a'..'z', 'A'..'Z' );
# The id of the record in the database,
# or one more than the last id you generated.
my $id = 1;
my $converted =
join '',
map { $digit_set[$_] }
convert_to_base($id, 0+#digits_set);
I needed something similar to what you're trying to accomplish. I retooled my code to generate folders so try this. It's setup for a console app, but you can use it in a website also.
private static void genRandomFolders()
{
string basepath = "C:\\Users\\{username here}\\Desktop\\";
int count = 5;
int length = 8;
List<string> codes = new List<string>();
int total = 0;
int i = count;
Random rnd = new Random();
while (i-- > 0)
{
string code = RandomString(rnd, length);
if (!codes.Exists(delegate(string c) { return c.ToLower() == code.ToLower(); }))
{
//Create directory here
System.IO.Directory.CreateDirectory(basepath + code);
}
total++;
if (total % 100 == 0)
Console.WriteLine("Generated " + total.ToString() + " random folders...");
}
Console.WriteLine();
Console.WriteLine("Generated " + total.ToString() + " total random folders.");
}
public static string RandomString(Random r, int len)
{
//string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; //uppercase only
//string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; //All
string str = "abcdefghjkmnpqrstuvwxyz123456789"; //Lowercase only
StringBuilder sb = new StringBuilder();
while ((len--) > 0)
sb.Append(str[(int)(r.NextDouble() * str.Length)]);
return sb.ToString();
}
I am writing code to ingest the IOR file generated by the team responsible for the server and use it to bind my client to their object. Sounds easy, right?
For some reason a bit beyond my grasp (having to do with firewalls, DMZs, etc.), the value for the server inside the IOR file is not something we can use. We have to modify it. However, the IOR string is encoded.
What does Visibroker provide that will let me decode the IOR string, change one or more values, then re-encode it and continue on as normal?
I've already looked into IORInterceptors and URL Naming but I don't think either will do the trick.
Thanks in advance!
When you feel like you need to hack an IOR, resist the urge to do so by writing code and whatnot to mangle it to your liking. IORs are meant to be created and dictated by the server that contains the referenced objects, so the moment you start mucking around in there, you're kinda "voiding your warranty".
Instead, spend your time finding the right way to make the IOR usable in your environment by having the server use an alternative hostname when it generates them. Most ORBs offer such a feature. I don't know Visibroker's particular configuration options at all, but a quick Google search revealed this page that shows a promising value:
vbroker.se.iiop_ts.host
Specifies the host name used by this server engine.
The default value, null, means use the host name from the system.
Hope that helps.
Long time ago I wrote IorParser for GNU Classpath, the code is available. It is a normal parser written being aware about the format, should not "void a warranty" I think. IOR contains multiple tagged profiles that are encapsulated very much like XML so we could parse/modify profiles that we need and understand and leave the rest untouched.
The profile we need to parse is TAG_INTERNET_IOP. It contains version number, host, port and object key. Code that reads and writes this profile can be found in gnu.IOR class. I am sorry this is part of the system library and not a nice piece of code to copy paste here but it should not be very difficult to rip it out with a couple of dependent classes.
This question has been repeatedly asked as CORBA :: Get the client ORB address and port with use of IIOP
Use the FixIOR tool (binary) from jacORB to patch the address and port of an IOR. Download the binary (unzip it) and run:
fixior <new-address> <new-port> <ior-file>
The tool will override the content of the IOR file with the 'patched' IOR
You can use IOR Parser to check the resulting IOR and compare it to your original IOR
Use this function to change the IOR. pass stringified IOR as first argument.
void hackIOR(const char* str, char* newIOR )
{
size_t s = (str ? strlen(str) : 0);
char temp[1000];
strcpy(newIOR,"IOR:");
const char *p = str;
s = (s-4)/2; // how many octets are there in the string
p += 4;
int i;
for (i=0; i<(int)s; i++) {
int j = i*2;
char v=0;
if (p[j] >= '0' && p[j] <= '9') {
v = ((p[j] - '0') << 4);
}
else if (p[j] >= 'a' && p[j] <= 'f') {
v = ((p[j] - 'a' + 10) << 4);
}
else if (p[j] >= 'A' && p[j] <= 'F') {
v = ((p[j] - 'A' + 10) << 4);
}
else
cout <<"invalid octet"<<endl;
if (p[j+1] >= '0' && p[j+1] <= '9') {
v += (p[j+1] - '0');
}
else if (p[j+1] >= 'a' && p[j+1] <= 'f') {
v += (p[j+1] - 'a' + 10);
}
else if (p[j+1] >= 'A' && p[j+1] <= 'F') {
v += (p[j+1] - 'A' + 10);
}
else
cout <<"invalid octet"<<endl;
temp[i]=v;
}
temp[i] = 0;
// Now temp has decoded IOR string. print it.
// Replace the object ID in temp.
// Encoded it back, with following code.
int temp1,temp2;
int l,k;
for(k = 0, l = 4 ; k < s ; k++)
{
temp1=temp2=temp[k];
temp1 &= 0x0F;
temp2 = temp2 & 0xF0;
temp2 = temp2 >> 4;
if(temp2 >=0 && temp2 <=9)
{
newIOR[l++] = temp2+'0';
}
else if(temp2 >=10 && temp2 <=15)
{
newIOR[l++] = temp2+'A'-10;
}
if(temp1 >=0 && temp1 <=9)
{
newIOR[l++] = temp1+'0';
}
else if(temp1 >=10 && temp1 <=15)
{
newIOR[l++] = temp1+'A'-10;
}
}
newIOR[l] = 0;
//new IOR is present in new variable newIOR.
}
Hope this works for you.
I have a database driven menu Helper that gets called from within my master page:
<div class="topBar">
<%= Html.MenuTree(39, false, "first", "last") %>
<div class="clear"></div>
</div>
Below is the code that outputs my HTML unordered list. The problem is that sometimes the output of the menu is completely wrong and all over the place ie. sub menu items appear as equal as top menu items.
I cannot find any pattern to why it does it so thought I'd post the code to see if anyone can spot the problem. My only other thought is that somehow its half cached half called and mixes the output.
This is what it should look like Correct http://img718.imageshack.us/img718/9317/screenshot20100328at120.png
Sometimes it comes out like this:alt text http://img413.imageshack.us/img413/9317/screenshot20100328at120.png
Here's the code (the boolean IsAdmin is false in this scenario):
public static string MenuTree(this HtmlHelper helper, int MenuCategoryID, bool Admin, string firstCssClass, string lastCssClass)
{
//TODO: Check for Subsonic fix for UNION bug
IOrderedQueryable<Menu> menuItems;
if (Admin)
{
menuItems = (from menu2 in Menu.All()
join pages in WebPage.All() on menu2.PageID equals pages.ID
join pagesRoles in PageRole.All() on pages.ID equals pagesRoles.PageID
join roles in aspnet_Role.All() on pagesRoles.RoleId equals roles.RoleId
where Roles.GetRolesForUser().Contains(roles.RoleName) && menu2.CategoryID == MenuCategoryID && menu2.Visible
select menu2).Distinct().OrderBy(f => f.OrderID);
}
else
{
menuItems = (from menu2 in Menu.All()
join pages in WebPage.All() on menu2.PageID equals pages.ID
where menu2.CategoryID == MenuCategoryID && menu2.Visible
select menu2).Distinct().OrderBy(f => f.OrderID);
}
var nonlinkedmenuItems = (from menu in Menu.All().Where(x => x.PageID == null && x.CategoryID == MenuCategoryID && x.Visible).OrderBy(f => f.OrderID) select menu);
var allCategories = menuItems.ToList().Concat<Menu>(nonlinkedmenuItems.ToList()).OrderBy(p => p.OrderID).ToList();
allCategories.ForEach(x => x.Children = allCategories.Where(y => y.ParentID == x.ID).OrderBy(f => f.OrderID));
Menu home = null;
if (Admin)
{
home = (from menu in Menu.All()
join pages in WebPage.All() on menu.PageID equals pages.ID
where pages.MenuName == "Home" && pages.IsAdmin
select menu).SingleOrDefault();
}
IEnumerable<Menu> topLevelItems;
if (Admin)
topLevelItems = allCategories.Where(f => f.ParentID == 0 && (f.Children.Count() > 0 || f.ID == home.ID));
else
topLevelItems = allCategories.Where(f => f.ParentID == 0);
var topLevelItemList = topLevelItems.ToList();
sbMenu.Length = 0;
sbMenu.AppendLine("<ul>");
LoopChildren(helper, Admin, topLevelItemList, 0, firstCssClass, lastCssClass);
sbMenu.AppendLine("</ul>");
string menuString = sbMenu.ToString();
//if ((menuString.IndexOf("<li>")) > 0)
// menuString = menuString.Insert((menuString.IndexOf("<li>") + 3), " class='first'");
//if (menuString.LastIndexOf("<li>\r\n") > 0)
// menuString = menuString.Insert((menuString.LastIndexOf("<li>\r\n") + 3), " class='last'");
return sbMenu.ToString();
}
private static void LoopChildren(this HtmlHelper helper, bool Admin, List<Menu> CurrentNode, int TabIndents, string firstCssClass, string lastCssClass)
{
for (int i = 0; i < CurrentNode.Count; i++)
{
sbMenu.Append(Tabs(TabIndents + 1));
string linkUrl = "";
string urlTitle = "";
if (CurrentNode[i].PageID != null)
{
WebPage item = WebPage.SingleOrDefault(x => x.ID == CurrentNode[i].PageID);
linkUrl = item.URL;
urlTitle = item.MenuName;
}
else
{
linkUrl = CurrentNode[i].URL;
urlTitle = CurrentNode[i].Title;
}
//Specify a RouteLink so that when in Error 404 page for example the links don't become /error/homepage
//If in admin we can manually write the <a> tag as it has the controller and action in it
bool selected = false;
if (helper.ViewContext.RouteData.Values["pageName"] != null && helper.ViewContext.RouteData.Values["pageName"].ToString() == linkUrl)
selected = true;
string anchorTag = Admin ? "<a href='" + linkUrl + "'>" + urlTitle + "</a>" : helper.RouteLink(urlTitle, new { controller = "WebPage", action = "Details", pageName = linkUrl });
if (TabIndents == 0 && i == 0 && firstCssClass != null)
sbMenu.AppendLine("<li class='" + firstCssClass + "'>" + anchorTag);
else if (TabIndents == 0 && i == (CurrentNode.Count - 1) && lastCssClass != null)
sbMenu.AppendLine("<li class='" + lastCssClass + "'>" + anchorTag);
else if (selected)
sbMenu.AppendLine("<li class='selected'>" + anchorTag);
else
sbMenu.AppendLine("<li>" + anchorTag);
if (CurrentNode[i].Children != null && CurrentNode[i].Children.Count() > 0)
{
sbMenu.Append(Tabs(TabIndents + 2));
sbMenu.AppendLine("<ul>");
LoopChildren(helper, Admin, CurrentNode[i].Children.ToList(), TabIndents + 2, "", "");
sbMenu.Append(Tabs(TabIndents + 2));
sbMenu.AppendLine("</ul>");
}
sbMenu.Append(Tabs(TabIndents + 1));
sbMenu.AppendLine("</li>");
}
}
private static string Tabs(int n)
{
return new String('\t', n);
}
I agree with the comments that string concatenation for this is painful. TagBuilder is a lot less painful for you.
I didn't check your code for problems, but I imagine that what I would do is basically to take the text output from your helper in a good case and a bad case and run them through a diff tool. Leave some markers before and after the point where you call Html.MenuTree() for debugging purposes - this way you will know exactly where the output starts and stops.
The diff tool will tell you what the differences in the two outputs are. Then you can go looking for the cause of these differences.
Another way I would seriously consider approaching this is through unit testing. Start with a simple unit test giving the MenuTree() method a very simple structure to work with. Verify that the output is sane. Then test more complex scenarios. If you during testing, debugging or in production discover a certain combination of input that causes the problem, write a unit test that tests for the correct output. Then fix it. When the test passes, you'll know that you are finished. Also, if you run your tests whenever you change something, you will know that this particular bug will never creep back in.
New bug? New unit test. And so on. Unit tests never solve the problem for you, but they give you the confidence to know that what used to work still works, even when you refactor and come up with cool new stuff.