/// <summary> /// Final data processing /// </summary> protected void PostProcessData() { // Final data processing if (!DataHelper.DataSourceIsEmpty(DataSource)) { DataSource.Tables[0].DefaultView.Sort = "type ASC"; DataTable dtResult = DataSource.Tables[0].DefaultView.ToTable(); DataSource.Tables.Clear(); DataSource.Tables.Add(dtResult); DocumentValidationHelper.ProcessValidationResult(DataSource, DocumentValidationEnum.Link, new Dictionary <string, object> { { "culture", currentCulture } }); } SetupControls(); // Fill the grid data source if (!DataHelper.DataSourceIsEmpty(DataSource)) { gridValidationResult.ReloadData(); } ProcessResult(DataSource); }
/// <summary> /// Initializes the validation scripts /// </summary> /// <param name="url">URL of the page</param> private void InitializeScripts(string url) { ScriptHelper.RegisterScriptFile(Page, "Validation.js"); ScriptHelper.RegisterJQuery(Page); // Disable minification on URL if (txtCodeText.Language == LanguageEnum.CSS) { url = DocumentValidationHelper.DisableMinificationOnUrl(url); } RegisterModalPageScripts(); string script = @" function ResizeCodeArea() { var height = $cmsj(""#divContent"").height(); $cmsj(""#" + txtCodeText.ClientID + @""").parent().css(""height"", height - 20 + ""px""); $cmsj("".js-code-mirror-scroll"").css(""height"", height - 52 + ""px""); } $cmsj(window).resize(function(){ResizeCodeArea()}); $cmsj(document).ready(function(){setTimeout(""ResizeCodeArea()"",300);" + ((!RequestHelper.IsPostBack() && !String.IsNullOrEmpty(url)) ? "LoadHTMLToElement('" + hdnHTML.ClientID + "','" + url + "');" + ControlsHelper.GetPostBackEventReference(this, null) + ";" : "") + @"});$cmsj(""#divContent"").css(""overflow"", ""hidden""); "; ScriptManager managaer = ScriptManager.GetCurrent(Page); managaer.RegisterAsyncPostBackControl(this); // Register script for resizing and scroll bar remove ScriptHelper.RegisterStartupScript(this, typeof(string), "AreaResizeAndScrollBarRemover", ScriptHelper.GetScript(script)); }
/// <summary> /// General method to process validation and return validation results /// </summary> private DataSet ValidateHtml() { if (!String.IsNullOrEmpty(Url)) { string docHtml = GetHtml(Url); if (!String.IsNullOrEmpty(docHtml)) { DataSet dsValidationResult = GetValidationResult(docHtml); // Check if result contains error table if (!DataHelper.DataSourceIsEmpty(dsValidationResult) && !DataHelper.DataSourceIsEmpty(dsValidationResult.Tables["errors"])) { DataTable tbError = DocumentValidationHelper.ProcessValidationResult(dsValidationResult, DocumentValidationEnum.Accessibility, null); tbError.DefaultView.Sort = "line ASC"; DataSet result = new DataSet(); result.Tables.Add(tbError); return(result); } else { return(dsValidationResult); } } else { mErrorText = GetString("validation.diffdomainorprotocol"); } } return(null); }
/// <summary> /// Page load /// </summary> protected void Page_PreRender(object sender, EventArgs e) { if (!RequestHelper.IsCallback() && RequestHelper.IsPostBack() && DataPostProcessing) { DocumentValidationHelper.PostProcessValidationData(DataSource, DocumentValidationEnum.CSS, null); DataPostProcessing = false; // Fill the grid data source if (!DataHelper.DataSourceIsEmpty(DataSource)) { gridValidationResult.DataSource = DataSource; gridValidationResult.ReloadData(); } ProcessResult(DataSource); } // Additional settings if (DataHelper.DataSourceIsEmpty(DataSource)) { lnkExportToExcel.Enabled = lnkNewWindow.Enabled = false; lnkExportToExcel.CssClass = lnkNewWindow.CssClass = "MenuItemEditDisabled"; lnkNewWindow.OnClientClick = lnkExportToExcel.OnClientClick = null; } else { lnkNewWindow.OnClientClick = String.Format("modalDialog('" + ResolveUrl("~/CMSModules/Content/CMSDesk/Validation/ValidationResults.aspx") + "?datakey={0}&docid={1}&hash={2}', 'ViewValidationResult', 800, 600);return false;", ResultKey, Node.DocumentID, QueryHelper.GetHash(String.Format("?datakey={0}&docid={1}", ResultKey, Node.DocumentID))); } }
protected DataSet gridValidationResult_OnDataReload(string completeWhere, string currentOrder, int currentTopN, string columns, int currentOffset, int currentPageSize, ref int totalRecords) { DataSet result = null; if (!DataHelper.DataSourceIsEmpty(DataSource)) { result = DocumentValidationHelper.PostProcessValidationData(DataSource.Copy(), DocumentValidationEnum.Link, null); } return(result); }
protected DataSet gridValidationResult_OnDataReload(string completeWhere, string currentOrder, int currentTopN, string columns, int currentOffset, int currentPageSize, ref int totalRecords) { DataSet ds = null; if (!DataHelper.DataSourceIsEmpty(DataSource)) { if (Data.PostProcessingRequired) { ds = DocumentValidationHelper.PostProcessValidationData(DataSource, DocumentValidationEnum.CSS, null); Data.PostProcessingRequired = false; } else { ds = DataSource; } } return(ds); }
/// <summary> /// Page load /// </summary> protected void Page_Load(object sender, EventArgs e) { if (!RequestHelper.IsPostBack()) { DataSource = null; } SetupControls(); if (RequestHelper.IsPostBack()) { // Fill the grid data source if (!DataHelper.DataSourceIsEmpty(DataSource)) { gridValidationResult.DataSource = DocumentValidationHelper.PostProcessValidationData(DataSource.Copy(), DocumentValidationEnum.Link, null); gridValidationResult.ReloadData(); } ProcessResult(DataSource); } }
/// <summary> /// General method to process validation and return validation results /// </summary> private DataSet ValidateHtml() { if (!String.IsNullOrEmpty(Url)) { string docHtml = GetHtml(Url); if (!String.IsNullOrEmpty(docHtml)) { DataSet dsValidationResult = GetValidationResult(GetRequestParameters(docHtml)); if (!DataHelper.DataSourceIsEmpty(dsValidationResult)) { // Check if result contains error table if (!DataHelper.DataSourceIsEmpty(dsValidationResult.Tables["error"])) { Dictionary <string, object> parameters = new Dictionary <string, object>(); parameters["validatorurl"] = ValidatorURL; parameters["validatorapppath"] = AppValidatorPath; DataTable tbError = DocumentValidationHelper.ProcessValidationResult(dsValidationResult, DocumentValidationEnum.HTML, parameters); DataSet result = new DataSet(); result.Tables.Add(tbError); return(result); } else { return(new DataSet()); } } } else { mErrorText = GetString("validation.diffdomainorprotocol"); } } return(null); }
/// <summary> /// Send validation request to validator and obtain result /// </summary> /// <param name="validationData">Validator parameters</param> /// <param name="parameter">Parameter</param> /// <returns>DataSet containing validator response</returns> private void GetValidationResults(Dictionary <string, string> validationData, string parameter) { DataSet dsResponse = null; List <string> validatedUrls = validationData.Keys.ToList(); Random randGen = new Random(); DataSet dsResult = DataSource = ((validationData.Count == 1) && string.IsNullOrEmpty(validationData[validatedUrls[0]])) ? new DataSet() : null; string source = null; int counter = 0; while (validatedUrls.Count > 0) { // Check if source is processed repeatedly if (source == validatedUrls[0]) { counter++; } else { counter = 0; } // Set current source to validate source = validatedUrls[0]; string cssData = validationData[source]; validatedUrls.RemoveAt(0); if (!String.IsNullOrEmpty(cssData)) { // Create web request HttpWebRequest req = (HttpWebRequest)WebRequest.Create(ValidatorURL); req.Method = "POST"; // 1 second for timeout req.ReadWriteTimeout = req.Timeout = 10000; string boundary = "---------------------------" + randGen.Next(1000000, 9999999) + randGen.Next(1000000, 9999999); req.ContentType = "multipart/form-data; boundary=" + boundary; // Set data to web request for validation byte[] data = Encoding.GetEncoding("UTF-8").GetBytes(GetRequestData(GetRequestDictionary(cssData), boundary)); req.ContentLength = data.Length; try { AddLog(String.Format(GetString("validation.css.validatingcss"), source)); using (var stream = req.GetRequestStream()) { stream.Write(data, 0, data.Length); } // Process server answer using (var webResponse = (HttpWebResponse)req.GetResponse()) { using (var response = webResponse.GetResponseStream()) { if (response != null) { if (dsResult == null) { dsResult = DataSource = new DataSet(); } dsResponse = new DataSet(); dsResponse.ReadXml(response); response.Close(); } } webResponse.Close(); } string[] currentUrlValues = parameter.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); Dictionary <string, object> parameters = new Dictionary <string, object>(); parameters["source"] = source; DataTable dtResponse = DocumentValidationHelper.ProcessValidationResult(dsResponse, DocumentValidationEnum.CSS, parameters); // Check if response contain any relevant data if (!DataHelper.DataSourceIsEmpty(dtResponse)) { // Add response data to validation DataSet if (DataHelper.DataSourceIsEmpty(dsResult)) { dsResult.Tables.Add(dtResponse); } else { dsResult.Tables[0].Merge(dtResponse); } } } catch (WebException) { AddError(string.Format(GetString("validation.css.cssnotvalidated"), source)); } catch { if (counter < 5) { validatedUrls.Insert(0, source); } else { AddError(string.Format(GetString("validation.css.cssnotvalidated"), source)); } } finally { req.Abort(); Thread.Sleep(ValidationDelay); } } } }
/// <summary> /// Prepare Dictionary with requests for CSS validation /// </summary> /// <param name="parameter">Asynchronous parameter containing current url data to resolve absolute URL </param> private Dictionary <string, string> GetValidationRequests(string parameter) { string html = GetHtml(Url); Dictionary <string, string> cssRequests = null; string[] urlParams = parameter.Split(';'); if (!String.IsNullOrEmpty(html)) { cssRequests = new Dictionary <string, string>(); // Get inline CSS AddLog(GetString("validation.css.preparinginline")); StringBuilder sbInline = new StringBuilder(); foreach (Match m in InlineStylesRegex.Matches(html)) { string captured = m.Groups["css"].Value; sbInline.AppendLine(captured); } cssRequests.Add(DocumentValidationHelper.InlineCSSSource, sbInline.ToString()); // Get linked styles URLs foreach (Match m in LinkedStylesRegex.Matches(html)) { string url = m.Groups["url"].Value; url = Server.HtmlDecode(url); if (!String.IsNullOrEmpty(url)) { bool processCss = true; string[] excludedCsss = EXCLUDED_CSS.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); // Check if CSS is not excluded (CMS stylesheets) foreach (string excludedCss in excludedCsss) { if (url.EndsWithCSafe(excludedCss, true)) { processCss = false; break; } } if (processCss && !cssRequests.ContainsKey(url)) { AddLog(String.Format(GetString("validation.css.preparinglinkedstyles"), url)); try { // Get CSS data from URL string readUrl = DocumentValidationHelper.DisableMinificationOnUrl(URLHelper.GetAbsoluteUrl(url, urlParams[0], urlParams[1], urlParams[2])); var request = WebRequest.CreateHttp(readUrl); EnsureCertificateValidation(request); using (var stream = request.GetResponse().GetResponseStream()) using (var reader = StreamReader.New(stream)) { string css = reader.ReadToEnd(); if (!String.IsNullOrEmpty(css)) { cssRequests.Add(url, css.Trim(new[] { '\r', '\n' })); } } } catch (Exception ex) { Service.Resolve <IEventLogService>().LogException("CSSValidator", "GetValidationRequests", ex); } } } } } return(cssRequests); }
/// <summary> /// Check URLs contained in document. Returns DataSet with validation results. /// </summary> /// <param name="urls">List of URLs to be processed</param> /// <param name="parameter">Parameter containing data to resolve relative URLs to absolute</param> private void CheckUrls(List <string> urls, string parameter) { int index = 0; int indexOffset = 0; // Initialize DataTable DataTable tbErrors = new DataTable(); tbErrors.Columns.Add("statuscode"); tbErrors.Columns.Add("type"); tbErrors.Columns.Add("message"); tbErrors.Columns.Add("url"); tbErrors.Columns.Add("time"); // Store table to DataSet DataSource = new DataSet(); DataSource.Tables.Add(tbErrors); // Prepare variables string[] urlParams = parameter.Split(';'); string message = null; int firstResponseCode = 0; Uri reqUri = null; HttpStatusCode statusCode = HttpStatusCode.OK; string statusDescription = null; string[] exceptions = UrlRequestExceptions.ToLowerCSafe().Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); // Process URLs while (index < urls.Count) { string url = urls[index + indexOffset]; string type = "E"; bool cont = false; bool loadDataFromResponse = true; string time; HttpWebResponse response; bool sslWarning = false; try { AddLog(urls[index + indexOffset], false); // Create HEAD web request for each URL HttpWebRequest req = (HttpWebRequest)WebRequest.Create(URLHelper.GetAbsoluteUrl(url, urlParams[0], urlParams[1], urlParams[2])); req.Method = "HEAD"; req.UserAgent = "CMS-LinkChecker"; req.AllowAutoRedirect = false; // If exception use GET request instead foreach (string exception in exceptions) { if (url.ToLowerCSafe().Contains(exception)) { req.Method = "GET"; break; } } // Sleep thread for specified time Thread.Sleep(ValidationDelay); // Initialize watcher to get time required to access URL Stopwatch sw = new Stopwatch(); sw.Start(); try { response = (HttpWebResponse)req.GetResponse(); } catch (WebException e) { response = (HttpWebResponse)e.Response; if (e.InnerException is AuthenticationException) { statusDescription = e.InnerException.Message; statusCode = HttpStatusCode.SwitchingProtocols; loadDataFromResponse = false; sslWarning = true; } } sw.Stop(); time = "(" + sw.ElapsedMilliseconds + " ms)"; reqUri = req.RequestUri; } catch { time = "(0 ms)"; response = null; } // Store response values if (loadDataFromResponse) { if (response != null) { statusCode = response.StatusCode; statusDescription = response.StatusDescription; response.Close(); } else { statusCode = HttpStatusCode.NotFound; statusDescription = HttpWorkerRequest.GetStatusDescription((int)statusCode); } } // Process response status code switch (statusCode) { // Response OK status case HttpStatusCode.Accepted: case HttpStatusCode.Continue: case HttpStatusCode.Created: case HttpStatusCode.NoContent: case HttpStatusCode.NonAuthoritativeInformation: case HttpStatusCode.NotModified: case HttpStatusCode.OK: case HttpStatusCode.PartialContent: case HttpStatusCode.ResetContent: case HttpStatusCode.SwitchingProtocols: case HttpStatusCode.Unused: case HttpStatusCode.UseProxy: message = statusDescription; break; // Moved, follow redirection case HttpStatusCode.MultipleChoices: case HttpStatusCode.MovedPermanently: case HttpStatusCode.Found: case HttpStatusCode.RedirectMethod: case HttpStatusCode.RedirectKeepVerb: indexOffset++; cont = true; string host = reqUri.Host; if (firstResponseCode == 0) { firstResponseCode = (int)statusCode; } string newLocation = response.Headers["Location"]; string redirectUrl = URLHelper.ContainsProtocol(newLocation) ? newLocation : reqUri.AbsoluteUri.Substring(0, reqUri.AbsoluteUri.IndexOfCSafe(host) + host.Length) + newLocation; urls.Insert(index + indexOffset, redirectUrl); break; // Client errors case HttpStatusCode.BadRequest: case HttpStatusCode.Unauthorized: case HttpStatusCode.PaymentRequired: case HttpStatusCode.Forbidden: case HttpStatusCode.NotFound: case HttpStatusCode.MethodNotAllowed: case HttpStatusCode.NotAcceptable: case HttpStatusCode.ProxyAuthenticationRequired: case HttpStatusCode.RequestTimeout: case HttpStatusCode.Conflict: case HttpStatusCode.Gone: case HttpStatusCode.LengthRequired: case HttpStatusCode.PreconditionFailed: case HttpStatusCode.RequestEntityTooLarge: case HttpStatusCode.RequestUriTooLong: case HttpStatusCode.UnsupportedMediaType: case HttpStatusCode.RequestedRangeNotSatisfiable: case HttpStatusCode.ExpectationFailed: message = ResHelper.GetString("validation.link.clienterror", currentCulture) + " " + statusDescription; break; // Internal server errors case HttpStatusCode.InternalServerError: case HttpStatusCode.NotImplemented: case HttpStatusCode.BadGateway: case HttpStatusCode.ServiceUnavailable: case HttpStatusCode.GatewayTimeout: case HttpStatusCode.HttpVersionNotSupported: message = ResHelper.GetString("validation.link.servererror", currentCulture) + " " + statusDescription; break; } string statusCodeText = ((int)statusCode).ToString(); // Add log describing link validation result AddLog(" " + time + " <b>" + DocumentValidationHelper.GetStatusCodeDescription((int)statusCode, currentCulture) + "</b> "); if (!cont) { // Store link validation result if link broken or final target of redirection found if (LinkBroken(response) || (indexOffset > 0)) { if (!LinkBroken(response) || sslWarning) { type = "W"; } // Check if redirection was present if (indexOffset > 0) { statusCodeText = firstResponseCode + "->" + (int)statusCode; firstResponseCode = 0; message = EnsureMaximumLineLength(urls[index]) + "<br />" + ResHelper.GetString("validation.link.permanentredir") + "<br />" + EnsureMaximumLineLength(urls[index + indexOffset]) + " <b>" + message + "</b>"; } // Add validation result to result table tbErrors.Rows.Add(statusCodeText, type, message, EnsureMaximumLineLength(urls[index + indexOffset]), time); } // Move to next url index += indexOffset + 1; indexOffset = 0; } } }
/// <summary> /// Prepare Dictionary with requests for CSS validation /// </summary> /// <param name="parameter">Asynchronous parameter containing current url data to resolve absolute URL </param> private Dictionary <string, string> GetValidationRequests(string parameter) { string html = GetHtml(Url); Dictionary <string, string> cssRequests = null; string[] urlParams = parameter.Split(';'); if (!String.IsNullOrEmpty(html)) { cssRequests = new Dictionary <string, string>(); // Get inline CSS AddLog(GetString("validation.css.preparinginline")); StringBuilder sbInline = new StringBuilder(); foreach (Match m in InlineStylesRegex.Matches(html)) { string captured = m.Groups["css"].Value; sbInline.Append(captured); sbInline.Append("\n"); } cssRequests.Add(DocumentValidationHelper.InlineCSSSource, sbInline.ToString()); // Get linked styles URLs WebClient client = new WebClient(); foreach (Match m in LinkedStylesRegex.Matches(html)) { string url = m.Groups["url"].Value; url = Server.HtmlDecode(url); string css = null; if (!String.IsNullOrEmpty(url)) { bool processCss = true; string[] excludedCsss = mExcludedCSS.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); // Check if CSS is not excluded (CMS stylesheets) foreach (string excludedCss in excludedCsss) { if (url.EndsWithCSafe(excludedCss, true)) { processCss = false; break; } } if (processCss && !cssRequests.ContainsKey(url)) { AddLog(String.Format(GetString("validation.css.preparinglinkedstyles"), url)); try { // Get CSS data from URL string readUrl = DocumentValidationHelper.DisableMinificationOnUrl(URLHelper.GetAbsoluteUrl(url, urlParams[0], urlParams[1], urlParams[2])); StreamReader reader = StreamReader.New(client.OpenRead(readUrl)); css = reader.ReadToEnd(); if (!String.IsNullOrEmpty(css)) { cssRequests.Add(url, css.Trim(new char[] { '\r', '\n' })); } } catch { } } } } } return(cssRequests); }
/// <summary> /// Send validation request to validator and obtain result /// </summary> /// <param name="validatorParameters">Validator parameters</param> /// <returns>DataSet containing validator response</returns> private DataSet GetValidationResults(Dictionary <string, string> validationData, string parameter) { DataSet dsResponse = null; DataSet dsResult = new DataSet(); DataTable dtResponse = null; List <string> validatedUrls = validationData.Keys.ToList <string>(); Random randGen = new Random(); DataSource = dsResult; string source = null; int counter = 0; while (validatedUrls.Count > 0) { // Check if source is processed repeatedly if (source == validatedUrls[0]) { counter++; } else { counter = 0; } // Set current source to validate source = validatedUrls[0]; string cssData = validationData[source]; validatedUrls.RemoveAt(0); if (!String.IsNullOrEmpty(cssData)) { // Create web request HttpWebRequest req = (HttpWebRequest)WebRequest.Create(ValidatorURL); req.Method = "POST"; string boundary = "---------------------------" + randGen.Next(1000000, 9999999) + randGen.Next(1000000, 9999999); req.ContentType = "multipart/form-data; boundary=" + boundary; // Set data to web request for validation byte[] data = Encoding.GetEncoding("UTF-8").GetBytes(GetRequestData(GetRequestDictionary(cssData), boundary)); req.ContentLength = data.Length; StreamWrapper writer = StreamWrapper.New(req.GetRequestStream()); writer.Write(data, 0, data.Length); writer.Close(); try { // Process server answer AddLog(String.Format(GetString("validation.css.validatingcss"), source)); StreamWrapper response = StreamWrapper.New(req.GetResponse().GetResponseStream()); if (response != null) { dsResponse = new DataSet(); dsResponse.ReadXml(response.SystemStream); } string[] currentUrlValues = parameter.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); Dictionary <string, object> parameters = new Dictionary <string, object>(); parameters["sitename"] = CMSContext.CurrentSiteName; parameters["user"] = currentUser; parameters["source"] = source; parameters["domainurl"] = currentUrlValues[0]; parameters["applicationurl"] = currentUrlValues[1]; dtResponse = DocumentValidationHelper.ProcessValidationResult(dsResponse, DocumentValidationEnum.CSS, parameters); // Check if response contain any relevant data if (!DataHelper.DataSourceIsEmpty(dtResponse)) { // Add response data to validation DataSet if (DataHelper.DataSourceIsEmpty(dsResult)) { dsResult.Tables.Add(dtResponse); } else { dsResult.Tables[0].Merge(dtResponse); } } } catch { if (counter < 5) { validatedUrls.Insert(0, source); } } finally { Thread.Sleep(ValidationDelay); } } } return(dsResult); }