ImageResizer  3.4.0
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Events
yrl.cs
1 /**
2  * Written by Nathanael Jones
3  * http://nathanaeljones.com
4  * nathanael.jones@gmail.com
5  *
6  *
7  **/
8 
9 using System;
10 using System.Configuration;
11 using System.Web;
12 using System.Web.Security;
13 using System.Web.UI;
14 using System.Web.UI.WebControls;
15 using System.Web.UI.WebControls.WebParts;
16 using System.Web.UI.HtmlControls;
17 using System.Collections.Specialized;
18 using System.Text;
19 using System.IO;
20 using System.Text.RegularExpressions;
21 using System.Web.Hosting;
22 
23 //System.Web.VirtualPathUtility
24 
25 namespace ImageResizer.Util
26 {
27 
28 
29  /*
30  * Notes:
31  *
32  * This class is designed to standardize path interpretation and conversion.
33 
34  * This class will convert any fully qualified url to a relative path, even if the domain doesn't match.
35  * So: If you are going to validate and use path parameters, convert to yrl form BEFORE performing validation, not
36  * after. If you use the yrl class all the way through, you shouldn't have problems.
37  *
38  * This class will throw an exception if passed invalid input.
39  *
40  * TODO: Remember to account for &AspxAutoDetectCookieSupport=1
41  *
42  */
43 
44  /// <summary>
45  /// Enapsulates a mutable (changable) site-relative URL. Note that "" is equivalent to the application root directory in YRL notation (the ~/ is implicit, always).
46  /// QueryFindYrlVerifyID can be removed if external dependencies aren't allowed. It uses fbs.Articles.Index.FindPathByID()
47  /// This class is designed to standardize path interpretation and conversion.
48  /// </summary>
49  [Serializable()]
50  public class yrl
51  {
52  /// <summary>
53  /// The base file, usually a .aspx page. Ex. 'home.aspx' or 'admin/new.aspx'. Can also be a base directory, such as articles
54  /// </summary>
55  public string BaseFile = "";
56  /// <summary>
57  /// A collection of name/value query paramaters. Do NOT UrlEncode/Decode these! That step will be done for you.
58  /// </summary>
59  public NameValueCollection QueryString = new NameValueCollection();
60 
61  // Summary:
62  // Gets the entry at the specified index of the System.Collections.Specialized.NameValueCollection.
63  //
64  // Parameters:
65  // index:
66  // The zero-based index of the entry to locate in the collection.
67  //
68  // Returns:
69  // A System.String that contains the comma-separated list of values at the specified
70  // index of the collection.
71  //
72  // Exceptions:
73  // System.ArgumentOutOfRangeException:
74  // index is outside the valid range of indexes for the collection.
75  public string this[int index] { get { return QueryString[index]; } }
76  //
77  // Summary:
78  // Gets or sets the entry with the specified key in the System.Collections.Specialized.NameValueCollection.
79  //
80  // Parameters:
81  // name:
82  // The System.String key of the entry to locate. The key can be null.
83  //
84  // Returns:
85  // A System.String that contains the comma-separated list of values associated
86  // with the specified key, if found; otherwise, null.
87  //
88  // Exceptions:
89  // System.NotSupportedException:
90  // The collection is read-only and the operation attempts to modify the collection.
91  public string this[string name] { get { return QueryString[name]; } set { QueryString[name] = value; } }
92 
93 
94 
95 
96  #region Constructor
97  /// <summary>
98  /// Creates a default instance of yrl which points to the application root directory.
99  /// </summary>
100  public yrl()
101  {
102  }
103  public yrl(string path)
104  {
105  if (path == null) throw new ArgumentNullException("path", "Cannot create a yrl from a null string");
106  yrl temp = FromString(path);
107  if (temp == null) throw new ArgumentException("The specified path (" + path + ") could not be parsed! It may be outside the bounds of the application, or it may contain an invalid character, such as a tilde in the middle of the path.", "path");
108  this.BaseFile = temp.BaseFile;
109  this.QueryString = temp.QueryString;
110  }
111 
112  #endregion
113 
114  #region yrl Constructor components
115  /// <summary>
116  /// Returns a yrl object from a path. Returns null if the path cannot be parsed.
117  /// (Out of application bounds, or an invalid character). The tilde is an invalid character unless used as the app-relative specifier.
118  /// </summary>
119  /// <param name="path">A path
120  /// Each query paramater will be UrlDecoded.
121  /// </param>
122  /// <returns></returns>
123  public static yrl FromString(string path)
124  {
125  //crashes on ~$lioFlatFile.rtf
126  PathType pathtype = GetPathType(path);
127  if (pathtype == PathType.Root) return yrl.Root;
128 
129  //OSX Mono uses physical paths in the unix style - fools the comparison. So, to handle physical paths right, we have to try that first.
130 
131  yrl test = FromPhysicalString(path);
132  if (test != null) return test;
133 
134  if (pathtype == PathType.File || pathtype == PathType.PathForwardslashPath)
135  {
136  return FromYrlString(path);
137  }
138 
139  if (pathtype == PathType.ForwardslashPath)
140  {
141  return FromYrlString(TrimStartAppFolder(path).TrimStart(new char[] { '/' }));
142  }
143  if (pathtype == PathType.ServerForwardslashPath)
144  {
145  int tilde = path.IndexOf('~');
146  if (tilde > 0)
147  {
148  return FromYrlString(TrimStartAppFolder(TrimStartServer(path).TrimStart(new char[] { '~', '/' })).TrimStart(new char[] { '/' }));
149  }
150  else
151  return FromYrlString(TrimStartAppFolder(TrimStartServer(path)).TrimStart(new char[] { '/' }));
152  }
153  if (pathtype == PathType.TildeForwardslashPath)
154  {
155  //return FromYrlString(TrimStartAppFolder(path.TrimStart(new char[] { '~', '/' })).TrimStart(new char[] { '/' }));
156  return FromYrlString(path.TrimStart(new char[] { '~', '/' }));
157  }
158  if (pathtype == PathType.DriveletterColonBackslashPath)
159  {
160  return FromPhysicalString(path);
161  }
162  if (pathtype == PathType.BackslashPath || pathtype == PathType.PathBackslashPath)
163  {
164  return FromRelativePhysicalString(path);
165  }
166  return null;
167  }
168  /// <summary>
169  /// Creates an instance from a yrl-syntax string (virtual, but without ~/). Use FromString if you're not sure what type of syntax is used.
170  /// </summary>
171  /// <param name="path"></param>
172  /// <returns></returns>
173  public static yrl FromYrlString(string path)
174  {
175  if (path.Length == 0) return yrl.Root;
176  yrl temp = new yrl();
177  int firstdelimiter = path.IndexOfAny(new char[] { '?', '&' });
178  if (firstdelimiter < 0) firstdelimiter = path.Length;
179  if (path.IndexOfAny(new char[] { '/', '\\', '~', ':' }) == 0) return null;
180 
181  //throw new Exception("The specified path \"" + path + "\" starts with an invalid character! yrl paths cannot begin with any sort of slash, tilde, or colon!");
182  temp.BaseFile = path.Substring(0, firstdelimiter);
183 
184  string querystring = "";
185  if (firstdelimiter < path.Length) querystring = path.Substring(firstdelimiter, path.Length - firstdelimiter);
186  if (querystring.Length > 0)
187  {
188  string[] pairs = querystring.Split(new char[] { '?', '&' }, StringSplitOptions.RemoveEmptyEntries);
189  foreach (string s in pairs)
190  {
191  string[] namevalue = s.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
192  if (namevalue.Length == 2)
193  {
194  temp.QueryString[HttpUtility.UrlDecode(namevalue[0])] =
195  HttpUtility.UrlDecode(namevalue[1]);
196  }
197  else
198  {
199  //No value, so we set a blank value
200  //Setting a null value would be confusing, as that is how we determine
201  //whether a certain paramater exists
202  temp.QueryString[HttpUtility.UrlDecode(namevalue[0])] = "";
203  //throw new Exception("The specified path \"" + path + "\" contains a query paramater pair \"" + s + "\" that does not parse!");
204  }
205  }
206  }
207  return temp;
208 
209  }
210  /// <summary>
211  ///
212  /// </summary>
213  /// <param name="path"></param>
214  /// <returns></returns>
215  public static yrl FromRelativePhysicalString(string path)
216  {
217  string temp = path;
218  if (temp[0] != '\\') temp = "\\" + temp;
219  temp = TrimStartAppFolder(temp);
220  string newpath = HostingEnvironment.MapPath("~") + temp;
221  return FromPhysicalString(newpath);
222  }
223  /// <summary>
224  /// Creates a yrl Instance from the specified physical path. Throws an exception if the path is outside the application structure.
225  /// </summary>
226  /// <param name="path"></param>
227  /// <returns></returns>
228  public static yrl FromPhysicalString(string path)
229  {
230  string temp = path;
231  string localapproot = HostingEnvironment.MapPath("~").TrimEnd(new char[] { '/', '\\', '~' });
232 
233  if (temp.StartsWith(localapproot, StringComparison.OrdinalIgnoreCase))
234  {
235  temp = temp.Remove(0, localapproot.Length);
236  }
237  else
238  {
239  return null;
240  //throw new Exception("The specified path \"" + path + "\" is outside the bounds of the application (\"" + localapproot + "\")and cannot be handled!");
241  }
242  temp = temp.TrimStart(new char[] { '/', '\\', '~' }).Replace('\\', '/');
243 
244  return FromYrlString(temp);
245  }
246  /// <summary>
247  /// either '/' or '/folder' like '/yf' or sometimes '/folder/folder'
248  /// </summary>
249  /// <returns></returns>
250  public static string GetAppFolderName()
251  {
252 
253  return HostingEnvironment.ApplicationVirtualPath;
254  }
255  /// <summary>
256  /// Removes the application folder from the specified path. Leaves the leading forwardslash.
257  /// Assuming the Application path is /yf, this function will transform /yf/home.aspx to /home.aspx and /yf/css/john.css to /css/john.css
258  /// If the application is located in '/', nothing will be done to the path. Transforms yf/home.aspx to /home.aspx, yf\home.aspx to \home.aspx, \yf\home.aspx to \home.aspx
259  /// Won't work if the app folder has a child directory named the same!
260  /// </summary>
261  /// <param name="path"></param>
262  /// <returns></returns>
263  public static string TrimStartAppFolder(string path)
264  {
265  string appfoldername = GetAppFolderName();
266  if (appfoldername.Length <= 1) return path;
267 
268  //Remove /appfolder
269  if (path.StartsWith(appfoldername, StringComparison.InvariantCultureIgnoreCase))
270  return path.Remove(0, appfoldername.Length);
271 
272  //remove apppfolder
273  string appfolderwithoutslash = appfoldername.TrimStart(new char[] { '/' });
274  if (path.StartsWith(appfolderwithoutslash, StringComparison.InvariantCultureIgnoreCase))
275  return path.Remove(0, appfolderwithoutslash.Length);
276 
277  //remove \appfolder
278  string appfolderwithbackslash = appfoldername.Replace('/', '\\');
279  if (path.StartsWith(appfolderwithbackslash, StringComparison.InvariantCultureIgnoreCase))
280  return path.Remove(0, appfolderwithbackslash.Length);
281 
282  //remove ~/appfolder
283  string appfolderwithtilde = '~' + appfoldername;
284  if (path.StartsWith(appfolderwithtilde, StringComparison.InvariantCultureIgnoreCase))
285  return path.Remove(0, appfolderwithtilde.Length);
286  //return result
287  return path;
288  }
289 
290  public static string TrimStartServer(string path)
291  {
292  int firstdoubleforwardslash = path.IndexOf("//");
293  if (firstdoubleforwardslash < 0) return path;
294  if (path.Length < firstdoubleforwardslash + 3) return path;
295 
296  int nextforwardslash = path.IndexOf("/", firstdoubleforwardslash + 2);
297  if (nextforwardslash < 0)
298  {
299  //It's all server
300  return "";
301  }
302  return path.Remove(0, nextforwardslash);
303  }
304  #endregion
305 
306  #region Path Type Analysis
307  /// <summary>
308  /// Path syntaxes, determined by patterns.
309  /// </summary>
310  public enum PathType
311  {
312  /// <summary>
313  /// \\server\share\
314  /// </summary>
315  UNCPath,
316  /// <summary>
317  /// A path like '\temp\file.htm'
318  /// </summary>
319  BackslashPath,
320  /// <summary>
321  /// A path like 'c:\program files\temp'
322  /// </summary>
323  DriveletterColonBackslashPath,
324  /// <summary>
325  /// a path like 'img\file.img'
326  /// </summary>
327  PathBackslashPath,
328  /// <summary>
329  /// a path like '~/home.aspx'
330  /// </summary>
331  TildeForwardslashPath,
332  /// <summary>
333  /// a path like '/home.aspx'
334  /// </summary>
335  ForwardslashPath,
336  /// <summary>
337  /// a path like 'img/banner.jpg'
338  /// </summary>
339  PathForwardslashPath,
340  /// <summary>
341  /// a path like http://www.branham.org/home.aspx
342  /// </summary>
343  ServerForwardslashPath,
344  /// <summary>
345  /// A filename with no path, like 'test.exe' or 'home.aspx'
346  /// </summary>
347  File,
348  /// <summary>
349  /// Specifies the application root
350  /// </summary>
351  Root,
352  /// <summary>
353  /// Indeterminant form
354  /// </summary>
355  Invalid
356  }
357  /// <summary>
358  /// Returns the type of path
359  /// </summary>
360  /// <param name="path"></param>
361  /// <returns></returns>
362  public static PathType GetPathType(string path)
363  {
364  if (path == null) return PathType.Invalid;
365  if (path.Trim(new char[] { '\\', '/', '~' }).Length == 0) return PathType.Root;
366  int firstbackslash = path.IndexOf('\\');
367  int firstforwardslash = path.IndexOf('/');
368  int firsttilde = path.IndexOf('~');
369  int firstcolon = path.IndexOf(':');
370  int firstdoubleforwardslash = path.IndexOf("//");
371  if (firstbackslash == 0 && path[1] == '\\')
372  {
373  return PathType.UNCPath;
374  }
375  if (firstbackslash == 0 && firstforwardslash < 0 && firstcolon < 0)
376  {
377  return PathType.BackslashPath;
378  }
379  if (firstcolon == 1 && firstbackslash == 2)
380  {
381  return PathType.DriveletterColonBackslashPath;
382  }
383  if (firstcolon < 0 && firstbackslash > 0 && firstforwardslash < 0)
384  {
385  return PathType.PathBackslashPath;
386  }
387  if (firsttilde == 0 && firstforwardslash == 1)
388  {
389  return PathType.TildeForwardslashPath;
390  }
391  if (firstforwardslash == 0 && firsttilde != 0 && firstdoubleforwardslash < 0)
392  {
393  return PathType.ForwardslashPath;
394  }
395  if (firstcolon > 0 && firstdoubleforwardslash > 0 && firsttilde != 0)
396  {
397  return PathType.ServerForwardslashPath;
398  }
399  if (firsttilde != 0 && firstforwardslash < 0 && firstbackslash < 0 && firstcolon < 0)
400  {
401  return PathType.File;
402  }
403  if (firstforwardslash > 0)
404  {
405  return PathType.PathForwardslashPath;
406  }
407  return PathType.Invalid;
408  }
409  #endregion
410 
411  #region Properties and methods
412 
413  /// <summary>
414  /// yrl.QueryString["id"] Returns -1 if not found, -2 if unparseable
415  /// </summary>
416  public int QueryID
417  {
418  get
419  {
420 
421  if (QueryString["id"] == null) return -1;
422  int temp = 0;
423  if (!int.TryParse(QueryString["id"], out temp))
424  {
425  return -2;
426  }
427  return temp;
428  }
429  set
430  {
431  QueryString["id"] = value.ToString();
432  }
433  }
434  /// <summary>
435  /// yrl.QueryString["path"]. Returns null if not found, throws exception if not parseable.
436  /// </summary>
437  public yrl QueryPath
438  {
439  get
440  {
441  if (QueryString["path"] == null) return null;
442  return new yrl(QueryString["path"]);
443  }
444  set
445  {
446  QueryString["path"] = value.ToString();
447  }
448  }
449  /// <summary>
450  /// yrl.QueryString["dir"]. Returns null if not found, throws exception if not parseable.
451  /// </summary>
452  public yrl QueryDir
453  {
454  get
455  {
456  if (QueryString["dir"] == null) return null;
457  return new yrl(QueryString["dir"]);
458  }
459  set
460  {
461  if (value == null) QueryString.Remove("dir");
462  else
463  QueryString["dir"] = value.ToString();
464  }
465  }
466  /// <summary>
467  /// Tries to track down the ID by looking at QueryString["id"], then QueryString["path"]. Returns -1 if unsuccessful
468  /// </summary>
469  public int QueryFindID
470  {
471  get
472  {
473  int try1 = QueryID;
474  if (try1 > 0) return try1;
475 
476  yrl try2 = QueryPath;
477  if (try2 != null)
478  {
479  if (try2.ID > 0) return try2.ID;
480  }
481  return -1;
482 
483  }
484  }
485 
486  /// <summary>
487  /// Creates a deep copy of the yrl
488  /// </summary>
489  /// <returns></returns>
490  public yrl Copy()
491  {
492  yrl temp = new yrl();
493  temp.BaseFile = string.Copy(this.BaseFile);
494  foreach (string s in QueryString.Keys)
495  {
496  string value = QueryString[s];
497  temp.QueryString.Add(string.Copy(s), string.Copy(value));
498  }
499  return temp;
500  }
501 
502  /// <summary>
503  /// Returns a site-root-relative path along with query paramaters
504  /// </summary>
505  /// <returns></returns>
506  public override string ToString()
507  {
508  StringBuilder path = new StringBuilder();
509  path.Append(BaseFile);
510  if (QueryString.Count > 0)
511  {
512  path.Append('?');
513  foreach (string key in QueryString.Keys)
514  {
515  string value = QueryString[key];
516 
517  path.Append(HttpUtility.UrlEncode(key));
518  path.Append('=');
519  path.Append(HttpUtility.UrlEncode(value));
520  path.Append('&');
521  }
522  if (path[path.Length - 1] == '&') path.Remove(path.Length - 1, 1);
523  }
524  return path.ToString();
525  }
526 
527  /// <summary>
528  /// Returns the physical filesystem path for this yrl
529  /// </summary>
530  /// <returns></returns>
531  public string Local
532  {
533  get
534  {
535  if (BaseFile.Length == 0) return HostingEnvironment.MapPath("~");
536  return HostingEnvironment.MapPath("~/" + HttpUtility.UrlDecode(BaseFile));
537  }
538  }
539  /// <summary>
540  /// Returns the virtual path(~/...)of the base file without query paramaters.
541  /// </summary>
542  public string Virtual
543  {
544  get
545  {
546  if (BaseFile.Length == 0) return "~";
547  return "~/" + this.BaseFile;
548  }
549  }
550 
551  /// <summary>
552  /// Returns the virtual path (~/...) of the base file with query paramaters appended.
553  /// </summary>
554  public string VirtualURL
555  {
556  get
557  {
558  if (this.URL.Length == 0) return "~";
559  return "~/" + this.URL;
560  }
561  }
562  /// <summary>
563  /// Returns a ~/ path designed for the Hyperlink.NavigateUrl property
564  /// </summary>
565  public string NavigateURL
566  {
567  get
568  {
569  StringBuilder path = new StringBuilder();
570  path.Append(BaseFile);
571  if (QueryString.Count > 0)
572  {
573  path.Append('?');
574  foreach (string key in QueryString.Keys)
575  {
576  string value = QueryString[key];
577 
578  path.Append(NavigateUrlEncode(key));
579  path.Append('=');
580  path.Append(NavigateUrlEncode(value));
581  path.Append('&');
582  }
583  if (path[path.Length - 1] == '&') path.Remove(path.Length - 1, 1);
584  }
585  return "~/" + path.ToString();
586  }
587 
588  }
589  /// <summary>
590  ///
591  /// </summary>
592  /// <param name="text"></param>
593  /// <returns></returns>
594  public static string NavigateUrlEncode(string text)
595  {
596 
597  return text.Replace("/", "%2f").Replace("?","%3f").Replace("&","&amp;").Replace(" ","%20");
598  }
599 
600  /// <summary>
601  /// Returns root-relative URL with query paramaters in the form 'articles/00001002 article.aspx'
602  /// </summary>
603  public string URL
604  {
605  get
606  {
607  return this.ToString();
608  }
609 
610  }
611  /// <summary>
612  /// Returns root-relative URL with query paramaters in the form 'articles/00001002%20article.aspx'
613  /// </summary>
614  /// <returns></returns>
615  public string URLEncoded
616  {
617  get { return HttpUtility.UrlEncode(this.ToString()); }
618  }
619 
620  /// <summary>
621  /// Returns root-relative URL with query paramaters in the form 'articles/00001002%20article.aspx'.
622  /// Encodes special characters into HTML entities using Server.URLPathEncode
623  /// </summary>
624  public string URLHtmlEncoded
625  {
626  get
627  {
628  return HttpUtility.UrlPathEncode(this.ToString());
629  //.Replace("&amp;", "&").Replace(" ", "%20").Replace("&", "&amp;");
630  }
631  }
632  /// <summary>
633  /// Returns a domain-relative path in the form '/yf/articles/00001002%20article.aspx'
634  /// </summary>
635  public string URLAnchorTarget
636  {
637  get
638  {
639  return yrl.GetAppFolderName().TrimEnd(new char[] { '/' }) + "/" + HttpUtility.HtmlAttributeEncode(this.URL);
640  }
641  }
642 
643  /// <summary>
644  /// Returns relaitve URL with query paramaters in the form '../articles/00001002 article.aspx'
645  /// </summary>
646  public string RelativeURL
647  {
648  get
649  {
650  return this.ToString();
651  }
652 
653  }
654 
655 
656 
657  /// <summary>
658  /// Returns the ID if a leading 8-digit ID is found in the base file. Returns -1 otherwise
659  /// </summary>
660  public int ID
661  {
662  get
663  {
664  int id = -1;
665  //GetFileName works with URIs and paths.
666  //Had errors using this.Local - WebResource.Axd isn't mappable, but would trigger an ID check.
667  string filename = System.IO.Path.GetFileName(BaseFile);
668 
669  if (filename.Length < 8) return -1;
670  if (!int.TryParse(filename.Substring(0, 8), out id))
671  {
672  //It doesn't start with a 10-digit number.
673  return -1;
674  }
675  return id;
676  }
677 
678  }
679 
680 
681  /// <summary>
682  /// Returns first leading ID found in a segment of the path.
683  /// </summary>
684  public int FirstID
685  {
686  get
687  {
688  string[] segments = BaseFile.Split(new char[] { '/' }, StringSplitOptions.None);
689 
690  for (int i = 0; i < segments.Length; i++)
691  {
692  if (segments[i].Length >= 8)
693  {
694  int tempid = 0;
695  if (int.TryParse(segments[i].Substring(0, 8), out tempid))
696  {
697  return tempid;
698  }
699  }
700 
701  }
702  return -1;
703  }
704  }
705 
706 
707  /// <summary>
708  /// Returns true if BaseFile exists as a local file.
709  /// </summary>
710  public bool FileExists
711  {
712  get
713  {
714  return System.IO.File.Exists(Local);
715  }
716  }
717 
718  /// <summary>
719  /// Returns true if BaseFile exists as a local Directory.
720  /// </summary>
721  public bool DirExists
722  {
723  get
724  {
725  return System.IO.Directory.Exists(Local);
726  }
727  }
728  /// <summary>
729  /// Returns a DirectoryInfo object for BaseFile if it is a directory. If not, it returns the parent directory
730  /// </summary>
731  public DirectoryInfo DirInfo
732  {
733  get
734  {
735  if (DirExists)
736  return new DirectoryInfo(Local);
737  else
738  return new DirectoryInfo(System.IO.Path.GetDirectoryName(Local));
739  }
740  }
741  /// <summary>
742  /// Returns a yrl of the parent directory. If the current yrl is already the root, returns null;
743  /// Use
744  /// </summary>
745  public yrl Parent
746  {
747  get
748  {
749  if (IsRoot) return null;
750  if (IsAspx) return new yrl(System.IO.Path.GetDirectoryName(Local) );
751  if (!DirExists) return new yrl(System.IO.Path.GetDirectoryName(Local) );
752  else
753  {
754  DirectoryInfo d = new DirectoryInfo(Local);
755  return new yrl(d.Parent.FullName);
756  }
757  }
758  }
759  /// <summary>
760  /// Returns the directory portion of this yrl.
761  /// </summary>
762  public yrl Directory
763  {
764  get
765  {
766  if (IsRoot) return this;
767  if (IsAspx) return new yrl(System.IO.Path.GetDirectoryName(Local));
768  if (!DirExists) return new yrl(System.IO.Path.GetDirectoryName(Local));
769  else
770  {
771  return this;
772  }
773  }
774  }
775 
776  /// <summary>
777  /// Returns an instance of yrl that points to the application root.
778  /// </summary>
779  public static yrl Root
780  {
781  get { return new yrl(); }
782  }
783 
784  //public class RequestHelper
785  //{
786  // public static yrl TrueYrl { get { return yrl.FromString(TruePath); } }
787  // /// <summary>
788  // /// Attempts to retrieve the destination virtual path if it was rewritten. If not, it returns
789  // /// Request.Url.PathAndQuery. Paths are in the form "/default.aspx"
790  // /// </summary>
791  // public static string TruePath
792  // {
793  // get
794  // {
795  // object o = HttpContext.Current.Items[UrlRewritingNet.Web.UrlRewriteModule.PhysicalPath];
796  // if (o != null)
797  // {
798  // return (string)o;
799  // }
800  // return HttpContext.Current.Request.Url.PathAndQuery;
801 
802  // }
803  // }
804  //}
805  /// <summary>
806  /// Returns the REAL file that is being executed. Falls back to CurrentBrowserUrl if unavailable.
807  /// Unavailable if UrlRewritingNet isn't in use
808  /// </summary>
809  public static yrl Current
810  {
811  get
812  {
813  if (HttpContext.Current == null) return null;
814  //This should us UrlRewritingNet.Web.UrlRewriteModule.PhysicalPath
815  //instead of the magic string. Done for dependency elimination
816 
817  object o = HttpContext.Current.Items["UrlRewritingNet.UrlRewriter.CachedPathAfterRewrite"];
818  if (o != null)
819  {
820  return yrl.FromString((string)o);
821  }
822  return CurrentBrowserURL;
823  }
824  }
825  /// <summary>
826  /// Retreieves the current URL from HttpContext.Current.Request.Url (pre-rewrite path)
827  /// </summary>
828  public static yrl CurrentBrowserURL
829  {
830  get
831  {
832  if (HttpContext.Current == null) return null;
833  if (HttpContext.Current.Request == null) return null;
834  return FromUri(HttpContext.Current.Request.Url);
835  //return FromString(HttpContext.Current.Request.RawUrl);
836  }
837  }
838  public static yrl FromUri(Uri url)
839  {
840  if (url == null) return null;
841  return new yrl(url.AbsoluteUri);
842  }
843 
844  /// <summary>
845  /// Returns null if unavailable
846  /// </summary>
847  public static yrl Referrer
848  {
849  get
850  {
851  if (HttpContext.Current == null) return null;
852  if (HttpContext.Current.Request == null) return null;
853  if (HttpContext.Current.Request.UrlReferrer == null) return null;
854  if (HttpContext.Current.Request.UrlReferrer.OriginalString == null) return null;
855  return new yrl(HttpContext.Current.Request.UrlReferrer.OriginalString);
856  }
857  }
858 
859 
860  /// <summary>
861  /// Compares the hash codes of the two instances.
862  /// </summary>
863  /// <param name="obj"></param>
864  /// <returns></returns>
865  public override bool Equals(object obj)
866  {
867  if (obj == null) return false;
868  return (obj.GetHashCode() == this.GetHashCode());
869  }
870  /// <summary>
871  /// Returns a unique hash code derived from the URL property of this instance
872  /// </summary>
873  /// <returns></returns>
874  public override int GetHashCode()
875  {
876  return URL.ToLower().GetHashCode();
877  }
878 
879 
880  ///// <summary>
881  ///// Returns true if BaseFile contains '_vti' or should be ignored (the discard bin, subversion, etc)
882  ///// </summary>
883  //public bool IsExcluded
884  //{
885  // get
886  // {
887  // return ExclusiBaseFile.Contains("_vti");
888  // }
889  //}
890 
891  /// <summary>
892  /// An empty yrl signifies the application root. This verifies both the path and the querystring are empty.
893  /// </summary>
894  public bool IsEmpty
895  {
896  get
897  {
898  return (BaseFile.Length == 0 && QueryString.Count == 0);
899  }
900  }
901 
902 
903  /// <summary>
904  /// Returns true if the basefile is empty (the root) (the QueryString can have data). Use IsEmpty to check that both are empty.
905  /// </summary>
906  public bool IsRoot
907  {
908  get
909  {
910  return (BaseFile.Length == 0);
911  }
912  }
913  /// <summary>
914  /// Returns true if this path has no parent directory (the parent directory is the root).
915  /// </summary>
916  public bool IsInRoot
917  {
918  get
919  {
920  if (Parent == null) return true;
921  return (Parent.IsRoot);
922  }
923  }
924 
925  /// <summary>
926  /// Returns the name of the directory (like 'archives') if it is one, and the name of the file (like 'universal.css') if it is a file.
927  /// </summary>
928  public string Name
929  {
930  get
931  {
932  if (IsAspx) return System.IO.Path.GetFileName(Local);
933  if (DirExists) return new DirectoryInfo(Local).Name;
934  else return System.IO.Path.GetFileName(Local);
935  }
936  }
937  #region File Extension stuff & associated .aspx/.cs file lookup. Also contains FilenameWithout.... properties
938  /// <summary>
939  /// Returns the name of BaseFile without the extension.
940  /// </summary>
941  public string NameWithoutExtension
942  {
943  get
944  {
945  return System.IO.Path.GetFileNameWithoutExtension(Local);
946  }
947  }
948 
949  /// <summary>
950  /// Returns the filename without the ID or extension, converts underscores to spaces, and trims the string.
951  /// </summary>
952  public string PrettyTitle
953  {
954  get
955  {
956  return FilenameWithoutIdAndExtension.Replace('_', ' ').Trim();
957  }
958  }
959  /// <summary>
960  /// Returns the filename minus path information, extensions, or Article ID.
961  /// </summary>
962  public string FilenameWithoutIdAndExtension
963  {
964  get
965  {
966  int id = 0;
967  string filename = this.NameWithoutExtension;
968  if (filename.Length >= 8)
969  {
970  if (int.TryParse(filename.Substring(0, 8), out id))
971  {
972  //ID now refers to the id on the filename.
973  //Remove ID
974  return filename.Substring(8, filename.Length - 8);
975  }
976  }
977  //It doesn't start with a 10-digit number.
978  //Set the title to the entire filename
979  return filename;
980  }
981  }
982  /// <summary>
983  /// Returns the extension of BaseFile in the form '.aspx'
984  /// </summary>
985  public string Extension
986  {
987  get
988  {
989  return System.IO.Path.GetExtension(Local);
990  }
991  }
992  /// <summary>
993  /// returns true if BaseFile ends with .aspx
994  /// </summary>
995  public bool IsAspx
996  {
997  get
998  {
999  return BaseFile.EndsWith(".aspx", StringComparison.InvariantCultureIgnoreCase);
1000  }
1001  }
1002 
1003  public bool HasExtension
1004  {
1005  get
1006  {
1007  return (Extension.Length > 0);
1008  }
1009  }
1010  /// <summary>
1011  /// returns true if BaseFile ends with .cs or .vb
1012  /// </summary>
1013  public bool IsCodeFile
1014  {
1015  get
1016  {
1017  return BaseFile.EndsWith(".cs", StringComparison.InvariantCultureIgnoreCase) |
1018  BaseFile.EndsWith(".vb", StringComparison.InvariantCultureIgnoreCase);
1019  }
1020  }
1021  /// <summary>
1022  /// If this is .aspx.cs or .aspx.vb file, attempts to find and return the associated .aspx file.
1023  /// If this is a .aspx file, returns the current instance.
1024  /// Otherwise, this returns null;
1025  /// </summary>
1026  public yrl FindAssociatedMarkupFile
1027  {
1028  get
1029  {
1030  if (IsCodeFile)
1031  {
1032  yrl baseAspx = new yrl(this.Local.ToLower().Replace(".cs", "").Replace(".vb", ""));
1033  if (baseAspx.FileExists) return baseAspx;
1034  else return null;
1035  }
1036  if (IsAspx)
1037  {
1038  return this;
1039  }
1040  return null;
1041  }
1042  }
1043  /// <summary>
1044  /// If this is an .aspx file, attempts to find and return an associated .aspx.cs or .aspx.vb file.
1045  /// If this is .aspx.cs or .aspx.vb file, returns the current instance.
1046  /// Otherwise, this returns null;
1047  /// </summary>
1048  public yrl FindAssociatedCodeFile
1049  {
1050  get
1051  {
1052  if (IsAspx)
1053  {
1054  yrl codeCs = new yrl(this.Local + ".cs");
1055  yrl codeVb = new yrl(this.Local + ".vb");
1056  if (codeCs.FileExists) return codeCs;
1057  else if (codeVb.FileExists) return codeVb;
1058  else return null;
1059  }
1060  if (IsCodeFile)
1061  {
1062  return this;
1063  }
1064  return null;
1065  }
1066  }
1067 
1068  /// <summary>
1069  /// Deletes the old extension and replaces it with the specified extension. A leading '.' is not neccesary
1070  /// </summary>
1071  /// <param name="newextension"></param>
1072  public void ChangeExtension(string newextension)
1073  {
1074  BaseFile = BaseFile.Remove(BaseFile.Length - this.Extension.Length, this.Extension.Length);
1075  BaseFile += "." + newextension.TrimStart(new char[] { '.' });
1076  }
1077  #endregion
1078 
1079  /// <summary>
1080  /// Returns the next unused filename similar to the current one,by incrementing (i)
1081  /// </summary>
1082  /// <returns></returns>
1084  {
1085  yrl newfilename = this.Copy();
1086  if (newfilename.FileExists)
1087  {
1088 
1089  //This section here just looks for the next unused filname in the form
1090  // oldfilename (1).oldextension
1091  // This way, if the same file is uploaded 5 times it will appear as
1092  // file.txt
1093  // file (1).txt
1094  // file (2).txt
1095  // file (3).txt
1096  // file (4).txt
1097  string minusext = newfilename.Parent.Local +
1098  System.IO.Path.DirectorySeparatorChar +
1099  newfilename.NameWithoutExtension;
1100 
1101  for (int i = 1; i < 100; i++)
1102  {
1103  if (!System.IO.File.Exists(minusext + " (" + i.ToString() + ")" + newfilename.Extension))
1104  {
1105  newfilename = new yrl(minusext + " (" + i.ToString() + ")" + newfilename.Extension);
1106  break;
1107  }
1108  }
1109  }
1110  return newfilename;
1111  }
1112  public static explicit operator string(yrl url)
1113  {
1114  return url.ToString();
1115  }
1116  public static explicit operator yrl(string s)
1117  {
1118  return yrl.FromString(s);
1119  }
1120  #endregion
1121  /// <summary>
1122  /// Joins two yrls. Querystrings on either are discarded.
1123  /// </summary>
1124  /// <param name="folder"></param>
1125  /// <param name="filenameOrSubdir"></param>
1126  /// <returns></returns>
1127  public static yrl Combine(yrl folder, yrl filenameOrSubdir)
1128  {
1129  return new yrl(folder.Local + System.IO.Path.DirectorySeparatorChar + filenameOrSubdir.BaseFile.Replace('/','\\'));
1130  }
1131  /// <summary>
1132  /// Returns an absolute path to the current yrl using the specified base path
1133  /// </summary>
1134  /// <param name="protocolHostnameFolder">A base path like http://youngfoundations.org/ or http://localhost:2755/yf/</param>
1135  /// <returns></returns>
1136  public string CreateAbsoluteUrl(string protocolHostnameFolder)
1137  {
1138  return protocolHostnameFolder + this.URL;
1139  }
1140  /// <summary>
1141  /// Returns true if the yrl is null or empty (site root)
1142  /// </summary>
1143  /// <param name="path"></param>
1144  /// <returns></returns>
1145  public static bool IsNullOrEmpty(yrl path)
1146  {
1147  if (path == null) return true;
1148  if (path.IsEmpty) return true;
1149  return false;
1150  }
1151  }
1152 }
override int GetHashCode()
Returns a unique hash code derived from the URL property of this instance
Definition: yrl.cs:874
static string TrimStartAppFolder(string path)
Removes the application folder from the specified path. Leaves the leading forwardslash. Assuming the Application path is /yf, this function will transform /yf/home.aspx to /home.aspx and /yf/css/john.css to /css/john.css If the application is located in &#39;/&#39;, nothing will be done to the path. Transforms yf/home.aspx to /home.aspx, yf\home.aspx to .aspx, .aspx to .aspx Won&#39;t work if the app folder has a child directory named the same!
Definition: yrl.cs:263
static yrl FromRelativePhysicalString(string path)
Definition: yrl.cs:215
static yrl Combine(yrl folder, yrl filenameOrSubdir)
Joins two yrls. Querystrings on either are discarded.
Definition: yrl.cs:1127
static bool IsNullOrEmpty(yrl path)
Returns true if the yrl is null or empty (site root)
Definition: yrl.cs:1145
static string NavigateUrlEncode(string text)
Definition: yrl.cs:594
yrl()
Creates a default instance of yrl which points to the application root directory. ...
Definition: yrl.cs:100
string BaseFile
The base file, usually a .aspx page. Ex. &#39;home.aspx&#39; or &#39;admin/new.aspx&#39;. Can also be a base director...
Definition: yrl.cs:55
Enapsulates a mutable (changable) site-relative URL. Note that &quot;&quot; is equivalent to the application ...
Definition: yrl.cs:50
static PathType GetPathType(string path)
Returns the type of path
Definition: yrl.cs:362
static string GetAppFolderName()
either &#39;/&#39; or &#39;/folder&#39; like &#39;/yf&#39; or sometimes &#39;/folder/folder&#39;
Definition: yrl.cs:250
static yrl FromPhysicalString(string path)
Creates a yrl Instance from the specified physical path. Throws an exception if the path is outside t...
Definition: yrl.cs:228
int ID
Returns the ID if a leading 8-digit ID is found in the base file. Returns -1 otherwise ...
Definition: yrl.cs:661
static yrl FromString(string path)
Returns a yrl object from a path. Returns null if the path cannot be parsed. (Out of application boun...
Definition: yrl.cs:123
void ChangeExtension(string newextension)
Deletes the old extension and replaces it with the specified extension. A leading &#39;...
Definition: yrl.cs:1072
yrl GetNextAvailableDerivitive()
Returns the next unused filename similar to the current one,by incrementing (i)
Definition: yrl.cs:1083
string Local
Returns the physical filesystem path for this yrl
Definition: yrl.cs:532
static yrl FromYrlString(string path)
Creates an instance from a yrl-syntax string (virtual, but without ~/). Use FromString if you&#39;re not ...
Definition: yrl.cs:173
bool IsEmpty
An empty yrl signifies the application root. This verifies both the path and the querystring are empt...
Definition: yrl.cs:895
string CreateAbsoluteUrl(string protocolHostnameFolder)
Returns an absolute path to the current yrl using the specified base path
Definition: yrl.cs:1136
override bool Equals(object obj)
Compares the hash codes of the two instances.
Definition: yrl.cs:865
bool FileExists
Returns true if BaseFile exists as a local file.
Definition: yrl.cs:711
override string ToString()
Returns a site-root-relative path along with query paramaters
Definition: yrl.cs:506
yrl Copy()
Creates a deep copy of the yrl
Definition: yrl.cs:490
PathType
Path syntaxes, determined by patterns.
Definition: yrl.cs:310