/// <summary> /// Gets the Tab Dictionary from the DataCache memory location, if it's empty or missing, builds a new one /// </summary> /// <param name="portalId"></param> /// <param name="minTabPathDepth">ByRef parameter to return the minimum tab path depth (the number of '/' in the tab path)</param> /// <param name="maxTabPathDepth">ByRef parameter to return the maximum tab path depth (the number of '/' in the tab path)</param> /// <param name="minAliasPathDepth">ByRef parameter to return the minimum alias path depth (the number of '/' in the alias path</param> /// <param name="maxAliasPathDepth">ByRef parameter to return the maximum alias path depth (the number of '/' in the alias path)</param> /// <param name="settings"></param> /// <param name="forceRebuild"></param> /// <param name="bypassCache"></param> /// <param name="parentTraceId"></param> /// <returns>Dictionary (string, string) of Tab paths in tab key, with the rewrite path as the value</returns> /// <remarks> /// Changes /// Task 608 : Incrementally build tab dictionary instead of building entire dicitionary all at once /// Task 609 : If usePortalAlias is specified, only build dictionary with specific portal alias : ignore others /// Task 791 : allow for specification of true/false for using thread locking to prevent multiple rebuilds on threads /// </remarks> internal static SharedDictionary<string, string> FetchTabDictionary(int portalId, out int minTabPathDepth, out int maxTabPathDepth, out int minAliasPathDepth, out int maxAliasPathDepth, FriendlyUrlSettings settings, bool forceRebuild, bool bypassCache, Guid parentTraceId) { SharedDictionary<int, PathSizes> portalDepths; SharedDictionary<string, string> dict; //place threadlock to prevent two threads getting a null object //check for the tab dictionary in the DataCache var cc = new CacheController(); cc.GetPageIndexFromCache(out dict, out portalDepths, settings); string reason = ""; if (dict == null) { reason += "No Page index in cache;"; } if (forceRebuild) { reason += "Force Rebuild;"; } if (bypassCache) { reason += "Bypass Cache;"; } if (dict != null) { using (dict.GetReadLock()) { reason += "Existing Page Index=" + dict.Count.ToString() + " items;"; } } Hashtable homePageSkins; //keeps a list of skins set per home page and culture SharedDictionary<string, string> portalTabPathDictionary; if (dict != null && portalDepths != null && forceRebuild == false && bypassCache == false) { PathSizes depthInfo; bool changed = false; using (portalDepths.GetWriteLock()) { if (!portalDepths.ContainsKey(portalId)) { reason += "Portal " + portalId.ToString() + " added to index;"; //tab dictionary built, but portal not in it dict = BuildTabDictionary(out depthInfo, settings, portalId, dict, out homePageSkins, out portalTabPathDictionary, parentTraceId); //recheck portal add, when running with locks can get duplicate key exceptions if (portalDepths.ContainsKey(portalId) == false) { portalDepths.Add(portalId, depthInfo); changed = true; } cc.StoreTabPathsInCache(portalId, portalTabPathDictionary, settings); CacheController.StoreHomePageSkinsInCache(portalId, homePageSkins); } else { depthInfo = portalDepths[portalId]; } } if (changed) { //restash dictionary cc.StorePageIndexInCache(dict, portalDepths, settings, reason); } if (depthInfo != null) { minTabPathDepth = depthInfo.MinTabPathDepth; maxTabPathDepth = depthInfo.MaxTabPathDepth; minAliasPathDepth = depthInfo.MinAliasDepth; maxAliasPathDepth = depthInfo.MaxAliasDepth; } else { //fallback values, should never get here: mainly for compiler wranings minTabPathDepth = 1; maxTabPathDepth = 10; minAliasPathDepth = 1; maxAliasPathDepth = 4; } } else { //the cached dictionary was null or forceRebuild = true or bypassCache = true, so go get a new dictionary PathSizes depthInfo; dict = BuildTabDictionary(out depthInfo, settings, portalId, null, out homePageSkins, out portalTabPathDictionary, parentTraceId); //store the fact that this portal has been built portalDepths = new SharedDictionary<int, PathSizes>(); using (portalDepths.GetWriteLock()) { portalDepths.Add(portalId, depthInfo); } if (bypassCache == false) //only cache if bypass not switched on { cc.StorePageIndexInCache(dict, portalDepths, settings, reason); } cc.StoreTabPathsInCache(portalId, portalTabPathDictionary, settings); CacheController.StoreHomePageSkinsInCache(portalId, homePageSkins); minTabPathDepth = depthInfo.MinTabPathDepth; maxTabPathDepth = depthInfo.MaxTabPathDepth; minAliasPathDepth = depthInfo.MinAliasDepth; maxAliasPathDepth = depthInfo.MaxAliasDepth; } return dict; }
/// <summary> /// returns a tabId indexed dictionary of Friendly Urls /// </summary> /// <param name="portalId"></param> /// <param name="forceRebuild"></param> /// <param name="bypassCache"></param> /// <param name="settings"></param> /// <param name="customAliasForTabs"></param> /// <param name="parentTraceId"></param> /// <returns></returns> internal static SharedDictionary<int, SharedDictionary<string, string>> FetchCustomUrlDictionary(int portalId, bool forceRebuild, bool bypassCache, FriendlyUrlSettings settings, out SharedDictionary<string, string> customAliasForTabs, Guid parentTraceId) { SharedDictionary<int, SharedDictionary<string, string>> urlDict; //this contains a list of all tabs for all the portals that have been retrieved List<int> urlPortals; //this contains a list of the portals that have been retrieved //get the objects from the cache var cc = new CacheController(); cc.GetFriendlyUrlIndexFromCache(out urlDict, out urlPortals, out customAliasForTabs); if (urlDict != null && forceRebuild == false && bypassCache == false) { if (urlPortals == null) //no portals retrieved from cache, but was a dictionary. Bit weird, but we'll run with it { urlPortals = new List<int>(); } //check to see if this portal has been included in the dict if (urlPortals.Contains(portalId) == false) { //ok, there is a url dictionary, but this portal isn't in it, so //put it in and get the urls for this portal //this call appends extra portals to the list urlDict = BuildUrlDictionary(urlDict, portalId, settings, ref customAliasForTabs); urlPortals.Add(portalId); cc.StoreFriendlyUrlIndexInCache(urlDict, urlPortals, customAliasForTabs, settings, "Portal Id " + portalId.ToString() + " added to index."); } } else //either values are null (Not in cache) or we want to force the rebuild, or we want to bypass the cache { //rebuild the dictionary for this portal urlDict = BuildUrlDictionary(urlDict, portalId, settings, ref customAliasForTabs); urlPortals = new List<int> { portalId }; //always rebuild the portal list if (bypassCache == false) //if we are to cache this item (byPassCache = false) { //cache these items string reason = forceRebuild ? "Force Rebuild of Index" : "Index not in cache"; cc.StoreFriendlyUrlIndexInCache(urlDict, urlPortals, customAliasForTabs, settings, reason); } } return urlDict; }
internal static bool CheckForParameterRedirect( Uri requestUri, ref UrlAction result, NameValueCollection queryStringCol, FriendlyUrlSettings settings) { // check for parameter replaced works by inspecting the parameters on a rewritten request, comparing // them agains the list of regex expressions on the friendlyurls.config file, and redirecting to the same page // but with new parameters, if there was a match bool redirect = false; // get the redirect actions for this portal var messages = new List <string>(); Dictionary <int, List <ParameterRedirectAction> > redirectActions = CacheController.GetParameterRedirects(settings, result.PortalId, ref messages); if (redirectActions != null && redirectActions.Count > 0) { try { string rewrittenUrl = result.RewritePath ?? result.RawUrl; List <ParameterRedirectAction> parmRedirects = null; // find the matching redirects for the tabid int tabId = result.TabId; if (tabId > -1) { if (redirectActions.ContainsKey(tabId)) { // find the right set of replaced actions for this tab parmRedirects = redirectActions[tabId]; } } // check for 'all tabs' redirections if (redirectActions.ContainsKey(-1)) // -1 means 'all tabs' - rewriting across all tabs { // initialise to empty collection if there are no specific tab redirects if (parmRedirects == null) { parmRedirects = new List <ParameterRedirectAction>(); } // add in the all redirects List <ParameterRedirectAction> allRedirects = redirectActions[-1]; parmRedirects.AddRange(allRedirects); // add the 'all' range to the tab range tabId = result.TabId; } if (redirectActions.ContainsKey(-2) && result.OriginalPath.ToLowerInvariant().Contains("default.aspx")) { // for the default.aspx page if (parmRedirects == null) { parmRedirects = new List <ParameterRedirectAction>(); } List <ParameterRedirectAction> defaultRedirects = redirectActions[-2]; parmRedirects.AddRange(defaultRedirects); // add the default.aspx redirects to the list tabId = result.TabId; } // 726 : allow for site-root redirects, ie redirects where no page match if (redirectActions.ContainsKey(-3)) { // request is for site root if (parmRedirects == null) { parmRedirects = new List <ParameterRedirectAction>(); } List <ParameterRedirectAction> siteRootRedirects = redirectActions[-3]; parmRedirects.AddRange(siteRootRedirects); // add the site root redirects to the collection } // OK what we have now is a list of redirects for the currently requested tab (either because it was specified by tab id, // or because there is a replaced for 'all tabs' if (parmRedirects != null && parmRedirects.Count > 0 && rewrittenUrl != null) { foreach (ParameterRedirectAction parmRedirect in parmRedirects) { // regex test each replaced to see if there is a match between the parameter string // and the parmRedirect string compareWith = rewrittenUrl; var redirectRegex = RegexUtils.GetCachedRegex( parmRedirect.LookFor, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); Match regexMatch = redirectRegex.Match(compareWith); bool success = regexMatch.Success; bool siteRootTried = false; // if no match, but there is a site root redirect to try if (!success && parmRedirect.TabId == -3) { siteRootTried = true; compareWith = result.OriginalPathNoAlias; regexMatch = redirectRegex.Match(compareWith); success = regexMatch.Success; } if (!success) { result.DebugMessages.Add(parmRedirect.Name + " redirect not matched (" + rewrittenUrl + ")"); if (siteRootTried) { result.DebugMessages.Add(parmRedirect.Name + " redirect not matched [site root] (" + result.OriginalPathNoAlias + ")"); } } else { // success! there was a match in the parameters string parms = redirectRegex.Replace(compareWith, parmRedirect.RedirectTo); if (siteRootTried) { result.DebugMessages.Add(parmRedirect.Name + " redirect matched [site root] with (" + result.OriginalPathNoAlias + "), replaced with " + parms); } else { result.DebugMessages.Add(parmRedirect.Name + " redirect matched with (" + compareWith + "), replaced with " + parms); } string finalUrl = string.Empty; // now we need to generate the friendly Url // first check to see if the parameter replacement string has a destination tabid specified if (parms.ToLowerInvariant().Contains("tabid/")) { // if so, using a feature whereby the dest tabid can be changed within the parameters, which will // redirect the page as well as redirecting the parameter values string[] parmParts = parms.Split('/'); bool tabIdNext = false; foreach (string parmPart in parmParts) { if (tabIdNext) { // changes the tabid of page, effects a page redirect along with a parameter redirect int.TryParse(parmPart, out tabId); parms = parms.Replace("tabid/" + tabId.ToString(), string.Empty); // remove the tabid/xx from the path break; // that's it, we're finished } if (parmPart.Equals("tabid", StringComparison.InvariantCultureIgnoreCase)) { tabIdNext = true; } } } else if (tabId == -1) { // find the home tabid for this portal // 735 : switch to custom method for getting portal PortalInfo portal = CacheController.GetPortal(result.PortalId, true); tabId = portal.HomeTabId; } if (parmRedirect.ChangeToSiteRoot) { // when change to siteroot requested, new path goes directly off the portal alias // so set the finalUrl as the poratl alias finalUrl = result.Scheme + result.HttpAlias + "/"; } else { // if the tabid has been supplied, do a friendly url provider lookup to get the correct format for the tab url if (tabId > -1) { TabInfo tab = TabController.Instance.GetTab(tabId, result.PortalId, false); if (tab != null) { string path = Globals.glbDefaultPage + TabIndexController.CreateRewritePath(tab.TabID, string.Empty); string friendlyUrlNoParms = AdvancedFriendlyUrlProvider.ImprovedFriendlyUrl( tab, path, Globals.glbDefaultPage, result.HttpAlias, false, settings, Guid.Empty); if (friendlyUrlNoParms.EndsWith("/") == false) { friendlyUrlNoParms += "/"; } finalUrl = friendlyUrlNoParms; } if (tab == null) { result.DebugMessages.Add(parmRedirect.Name + " tabId in redirect rule (tabId:" + tabId.ToString() + ", portalId:" + result.PortalId.ToString() + " ), tab was not found"); } else { result.DebugMessages.Add(parmRedirect.Name + " tabId in redirect rule (tabId:" + tabId.ToString() + ", portalId:" + result.PortalId.ToString() + " ), tab found : " + tab.TabName); } } } if (parms.StartsWith("//")) { parms = parms.Substring(2); } if (parms.StartsWith("/")) { parms = parms.Substring(1); } if (settings.PageExtensionUsageType != PageExtensionUsageType.Never) { if (parms.EndsWith("/")) { parms = parms.TrimEnd('/'); } if (parms.Length > 0) { // we are adding more parms onto the end, so remove the page extension // from the parameter list // 946 : exception when settings.PageExtension value is empty parms += settings.PageExtension; // 816: if page extension is /, then don't do this if (settings.PageExtension != "/" && string.IsNullOrEmpty(settings.PageExtension) == false) { finalUrl = finalUrl.Replace(settings.PageExtension, string.Empty); } } else { // we are removing all the parms altogether, so // the url needs to end in the page extension only // 816: if page extension is /, then don't do this if (settings.PageExtension != "/" && string.IsNullOrEmpty(settings.PageExtension) == false) { finalUrl = finalUrl.Replace( settings.PageExtension + "/", settings.PageExtension); } } } // put the replaced parms back on the end finalUrl += parms; // set the final url result.FinalUrl = finalUrl; result.Reason = RedirectReason.Custom_Redirect; switch (parmRedirect.Action) { case "301": result.Action = ActionType.Redirect301; break; case "302": result.Action = ActionType.Redirect302; break; case "404": result.Action = ActionType.Output404; break; } redirect = true; break; } } } } catch (Exception ex) { Services.Exceptions.Exceptions.LogException(ex); messages.Add("Exception: " + ex.Message + "\n" + ex.StackTrace); } finally { if (messages.Count > 0) { result.DebugMessages.AddRange(messages); } } } return(redirect); }
/// <summary> /// This method checks the list of rules for parameter replacement and modifies the parameter path accordingly /// </summary> /// <param name="parameterPath"></param> /// <param name="tab"></param> /// <param name="settings"></param> /// <param name="portalId"></param> /// <param name="replacedPath"></param> /// <param name="messages"></param> /// <param name="changeToSiteRoot"></param> /// <param name="parentTraceId"></param> /// <returns></returns> internal static bool CheckParameterRegexReplacement(string parameterPath, TabInfo tab, FriendlyUrlSettings settings, int portalId, out string replacedPath, ref List <string> messages, out bool changeToSiteRoot, Guid parentTraceId) { bool replaced = false; replacedPath = ""; changeToSiteRoot = false; if (messages == null) { messages = new List <string>(); } var replaceActions = CacheController.GetParameterReplacements(settings, portalId, ref messages); if (replaceActions != null && replaceActions.Count > 0) { List <ParameterReplaceAction> parmReplaces = null; int tabId = tab.TabID; if (replaceActions.ContainsKey(tabId)) { //find the right set of replaced actions for this tab parmReplaces = replaceActions[tabId]; } //check for 'all tabs' replaceions if (replaceActions.ContainsKey(-1)) //-1 means 'all tabs' - replacing across all tabs { //initialise to empty collection if there are no specific tab replaces if (parmReplaces == null) { parmReplaces = new List <ParameterReplaceAction>(); } //add in the all replaces List <ParameterReplaceAction> allReplaces = replaceActions[-1]; parmReplaces.AddRange(allReplaces); //add the 'all' range to the tab range } if (parmReplaces != null) { //OK what we have now is a list of replaces for the currently requested tab (either because it was specified by tab id, // or because there is a replaced for 'all tabs' try { foreach (ParameterReplaceAction parmReplace in parmReplaces) { //do a regex on the 'lookFor' in the parameter path var parmRegex = RegexUtils.GetCachedRegex(parmReplace.LookFor, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); if (parmRegex.IsMatch(parameterPath)) { replacedPath = parmRegex.Replace(parameterPath, parmReplace.ReplaceWith); messages.Add(parmReplace.Name + " replace rule match, replaced : " + parameterPath + " with: " + replacedPath); replaced = true; //593: if this replacement is marked as a site root replacement, we will be //removing the page path from the final url changeToSiteRoot = parmReplace.ChangeToSiteRoot; break; } messages.Add(parmReplace.Name + " replace rule not matched {" + parameterPath + "}"); } } catch (Exception ex) { //catch exceptions here because most likely to be related to regular expressions //don't want to kill entire site because of this Services.Exceptions.Exceptions.LogException(ex); messages.Add("Exception : " + ex.Message + "\n" + ex.StackTrace); } } } return(replaced); }
internal static void InvalidateDictionary() { CacheController.FlushPageIndexFromCache(); }