Example #1
0
		/// <summary>
		/// Displays the change list composition. This is called when the main table shows the details of one
		/// change list.
		/// </summary>
		/// <param name="cid"> Change Id. This is relative to the database, not source control. </param>
		/// <param name="userName"> User name. </param>
		private void DisplayChange(int cid, string userName)
		{
			var changeQuery = from ch in DataContext.ChangeLists where ch.Id == cid select ch;
			if (changeQuery.Count() != 1)
			{
				ErrorOut("Could not find this change in the system!");
				return;
			}

			HintsData.InChangeView = true;

			ChangeList changeList = changeQuery.Single();

			DisplayPageHeader("Change list " + changeList.CL);

			Table table = new Table();
			table.AppendCSSClass("CssChangeListDetail");

			ActivePage.Controls.Add(table);

			table.Rows.Add(GetChangeDescriptionRow("Date:", WrapTimeStamp(changeList.TimeStamp)));
			if (changeList.UserClient != null && changeList.UserClient != String.Empty)
				table.Rows.Add(GetChangeDescriptionRow("Client:", changeList.UserClient));
			var userRow = GetChangeDescriptionRow("User:"******"Status:", changeList.Stage == 0 ? "Pending" : "Submitted"));
			if (changeList.Description != null && changeList.Description != String.Empty)
				table.Rows.Add(GetChangeDescriptionRow("Description:", Server.HtmlEncode(changeList.Description)));
			table.Rows.Add(GetChangeDescriptionRow("Files:", ""));

			var latestReview = GetLatestUserReviewForChangeList(userName, cid);
			foreach (ChangeFile file in
				(from fl in DataContext.ChangeFiles where fl.ChangeListId == cid select fl))
			{
				var versions = GetVersionsAbstract(file.Id);
				bool hasTextBody = (from ver in versions where ver.HasTextBody select ver).Count() != 0;
				table.Rows.Add(GetChangeFileRow(file, versions.LastOrDefault(), hasTextBody, latestReview));
			}

			var attachments = (from ll in DataContext.Attachments 
							   where ll.ChangeListId == cid
							   orderby ll.TimeStamp
							   select ll);

			if (attachments.Count() > 0)
			{
				table.Rows.Add(GetChangeDescriptionRow("Links:", ""));

				foreach (Attachment a in attachments)
					AddAttachmentRow(table, a);
			}

			AddLabel("<h3>Review history</h3>");
			Table reviewResults = new Table();

			ActivePage.Controls.Add(reviewResults);

			reviewResults.AppendCSSClass("CssChangeListReviewHistory");

			var allReviewsQuery = from rr in DataContext.Reviews
								  where rr.ChangeListId == cid && rr.IsSubmitted
								  orderby rr.TimeStamp
								  select rr;

			foreach (Review review in allReviewsQuery)
			{
				TableRow row = new TableRow();
				reviewResults.Rows.Add(row);

				row.AppendCSSClass("CssTopAligned");

				TableCell dateCell = new TableCell();
				row.Cells.Add(dateCell);

				dateCell.AppendCSSClass("CssDate");
				dateCell.Text = WrapTimeStamp(review.TimeStamp);

				TableCell nameCell = new TableCell();
				row.Cells.Add(nameCell);

				nameCell.AppendCSSClass("CssName");
				nameCell.Text = review.UserName;

				TableCell verdictCell = new TableCell();
				row.Cells.Add(verdictCell);

				verdictCell.AppendCSSClass("CssScore");

				HyperLink reviewTarget = new HyperLink();
				verdictCell.Controls.Add(reviewTarget);

				if (review.OverallStatus == 0)
					HintsData.HaveNeedsWorkVotes = true;

				reviewTarget.Text = Malevich.Util.CommonUtils.ReviewStatusToString(review.OverallStatus);
				reviewTarget.NavigateUrl = Request.FilePath + "?rid=" + review.Id;

				if (review.CommentText != null)
				{
					TableCell abbreviatedCommentCell = new TableCell();
					row.Cells.Add(abbreviatedCommentCell);
					abbreviatedCommentCell.AppendCSSClass("CssComment");
					abbreviatedCommentCell.Text = AbbreviateToOneLine(review.CommentText, MaxReviewCommentLength);
				}
			}

			//Todo: modify stage logic, stage 1 is pending...
			if (changeList.Stage != 0)
			{
				HintsData.IsChangeInactive = true;

				AddLabel(String.Format("<br>This review has been {0}.", (changeList.Stage == 2 ? "closed" : "deleted")));
				return;
			}

			AddLabel("<h3>Vote so far</h3>");

			TableGen.Table reviewerVote = new TableGen.Table(2) { CssClass = "CssChangeListVoteHistory" };
			ActivePage.Controls.Add(reviewerVote);

			var reviewerQuery = from rv in DataContext.Reviewers where rv.ChangeListId == cid select rv;
			Reviewer[] reviewers = reviewerQuery.ToArray();
			bool iAmAReviewer = false;
			foreach (Reviewer reviewer in reviewers)
			{
				var row = reviewerVote.CreateRow();
				reviewerVote.AddItem(row);

				row[0].Text = reviewer.ReviewerAlias;

				if (userName.EqualsIgnoreCase(reviewer.ReviewerAlias))
				{
					DropDownList list = new DropDownList() { ID = "verdictlist" };
					row[1].Add(list);

					list.Items.Add(new ListItem() { Text = "Needs work" });				
					list.Items.Add(new ListItem() { Text = "LGTM with minor tweaks" });
					list.Items.Add(new ListItem() { Text = "LGTM" });
					list.Items.Add(new ListItem() { Text = "Non-scoring comment" });

					iAmAReviewer = true;
				}
				else if (!Page.IsPostBack)
				{
					Review review = GetLastReview(cid, reviewer.ReviewerAlias);
					if (review == null)
					{
						row[1].Text = "Have not looked";
					}
					else
					{
						row[1].Add(new HyperLink()
						{
							Text = Malevich.Util.CommonUtils.ReviewStatusToString(review.OverallStatus),
							NavigateUrl = Request.FilePath + "?rid=" + review.Id,
						});
					}
				}
			}

			bool iOwnTheChange = changeList.UserName.EqualsIgnoreCase(userName);
			if (!iAmAReviewer && !iOwnTheChange)
			{
				AddLabel("<br>");
				AddLink("I would like to review this change.", "?cid=" + changeList.Id + "&action=makemereviewer")
					.AppendCSSClass("button");
			}

			if (iOwnTheChange && changeList.Stage != 2 && /* 2 = closed */
				GetChangeListStatus(changeList) == ChangeListStatus.Closable)
			{
				AddLabel("<br>");
				AddLink("Close Review", "?cid=" + changeList.Id + "&action=close")
					.AppendCSSClass("button");
			}

			if (iOwnTheChange)
			{
				HintsData.IsChangeAuthor = true;
				AddLabel("<h3>My response</h3>");
			}
			else
			{
				HintsData.IsChangeReviewer = true;
				AddLabel("<h3>My comments</h3>");
			}

			TextBox commentTextBox = new TextBox();
			ActivePage.Controls.Add(commentTextBox);
			commentTextBox.TextMode = TextBoxMode.MultiLine;
			commentTextBox.AppendCSSClass("CssGeneralCommentInputBox");
			commentTextBox.ID = "reviewcommenttextbox";

			AddLabel("<br>");

			Button submitReviewButton =
				new Button() { ID = "submitreviewbutton", Text = "Submit", CssClass = "button" };
			ActivePage.Controls.Add(submitReviewButton.As(HtmlTextWriterTag.P));
			submitReviewButton.Click += new EventHandler(
				delegate(object sender, EventArgs e) { submitReview_Clicked(cid, sender, e); });

			AddLabel("<br>");

			int myReviewId = GetBaseReviewId(userName, cid);
			if (myReviewId == 0)
				return;

			DisplayReviewLineComments(myReviewId);
		}
Example #2
0
		/// <summary>
		/// Generates the diff view for two file revisions.
		/// </summary>
		/// <param name="baseFile"> The base file. </param>
		/// <param name="baseId"> The database id of the file. </param>
		/// <param name="baseComments"> The set of comments associated with the base file. </param>
		/// <param name="baseHeader"> The caption for the base file column. </param>
		/// <param name="diffFile"> The diff file. </param>
		/// <param name="diffId"> The database id of the diff. </param>
		/// <param name="diffComments"> The set of comments associated with the diff file. </param>
		/// <param name="diffHeader"> The caption for the changed file column. </param>
		/// <param name="baseIsLeft"> True if the base file is left column. </param>
		/// <param name="rawDiff"> Stream containing raw diff.exe output. </param>
		/// <param name="fileName"> The name of the file being compared. </param>
		private Control GenerateFileDiffView(
			StreamCombiner baseFile, int baseId, AbstractedComment[] baseComments, string baseHeader,
			StreamCombiner diffFile, int diffId, AbstractedComment[] diffComments, string diffHeader,
			bool baseIsLeft, StreamCombiner rawDiff, string fileName)
		{
			bool isSingleFileView = baseId == diffId;

			{   // Get user-configurable settings
				UserContext uc = CurrentUserContext;

				DiffViewOptions = new FileDiffViewOptions()
				{
					IsBaseLeft = baseIsLeft,
					IsUnified = isSingleFileView ? false : uc.UnifiedDiffView ?? false,
					OmitUnchangedLines = (Request.QueryString["showAllLines"] ?? "false") != "true",
					CommentClickMode = uc.CommentClickMode,
					AutoCollapseComments = uc.AutoCollapseComments ?? true, // default to auto collapse
				};
			}

			ILineEncoder baseEncoder = GetEncoderForFile(fileName);
			ILineEncoder diffEncoder = GetEncoderForFile(fileName);

			Master.FindControl<Panel>("RootDivElement").Style[HtmlTextWriterStyle.Width] = "95%";

			#region View table initialization
			TableGen.Table fileView;
			if (isSingleFileView)
			{   // Single file view
				fileView = new TableGen.Table(new string[2] { "Num Base", "Txt Base" })
				{
					ID = "fileview",
					EnableViewState = false,
					CssClass = "CssFileView CssFileViewSingle"
				};
				fileView.ColumnGroup.ColumnNameIndexMap = new KeyValuePair<string, int>[4]
				{
					new KeyValuePair<string, int>("Num Base", 0),
					new KeyValuePair<string, int>("Txt Base", 1),
					new KeyValuePair<string, int>("Num Diff", 0),
					new KeyValuePair<string, int>("Txt Diff", 1),
				};
			}
			else if (DiffViewOptions.IsSplit)
			{   // Split file diff
				fileView = new TableGen.Table(new string[4]
				{
					"Num " + (DiffViewOptions.IsBaseLeft ? "Base" : "Diff"),
					"Txt " + (DiffViewOptions.IsBaseLeft ? "Base" : "Diff"),
					"Num " + (DiffViewOptions.IsBaseLeft ? "Diff" : "Base"),
					"Txt " + (DiffViewOptions.IsBaseLeft ? "Diff" : "Base"),
				});
			}
			else
			{   // Inline file diff
				fileView = new TableGen.Table(new string[3]
				{
					"Num " + (DiffViewOptions.IsBaseLeft ? "Base" : "Diff"),
					"Num " + (DiffViewOptions.IsBaseLeft ? "Diff" : "Base"),
					"Txt",
				});
				fileView.ColumnGroup.ColumnNameIndexMap = new KeyValuePair<string, int>[4]
				{
					new KeyValuePair<string, int>("Num " + (DiffViewOptions.IsBaseLeft ? "Base" : "Diff"), 0),
					new KeyValuePair<string, int>("Num " + (DiffViewOptions.IsBaseLeft ? "Diff" : "Base"), 1),
					new KeyValuePair<string, int>("Txt Base", 2),
					new KeyValuePair<string, int>("Txt Diff", 2),
				};
			}

			fileView.AppendCSSClass("CssFileView");
			if (!isSingleFileView)
				fileView.AppendCSSClass(DiffViewOptions.IsSplit ? "CssFileViewSplit" : "CssFileViewUnified");
			fileView.EnableViewState = false;
			fileView.ID = "fileview";
			fileView.Attributes["maxLineLen"] = MaxLineLength.ToString();

			AddJScriptCreateCommentOnClickAttribute(fileView);

			// Add the table header
			var fileViewHeaderGroup = fileView.CreateRowGroup();
			fileViewHeaderGroup.IsHeader = true;
			fileView.Add(fileViewHeaderGroup);

			var fileViewHeader = new TableGen.Row(isSingleFileView ? 1 : 2);
			fileViewHeader[0].ColumnSpan = 2;

			fileViewHeader[DiffViewOptions.IsSplit ? 0 : 1].Add(new HyperLink()
			{
				Text = baseHeader,
				NavigateUrl = Request.FilePath + "?vid=" + baseId,
			});

			if (!isSingleFileView)
			{
				fileViewHeader[1].ColumnSpan = 2;
				fileViewHeader[1].Add(new HyperLink()
				{
					Text = diffHeader,
					NavigateUrl = Request.FilePath + "?vid=" + diffId,
				});
			}

			fileViewHeader.AppendCSSClass("Header");
			fileViewHeaderGroup.AddRow(fileViewHeader);

			#endregion

			var baseFileInfo = new DiffFileInfo(baseFile, baseEncoder, baseId, baseComments, BaseOrDiff.Base);
			var diffFileInfo = new DiffFileInfo(diffFile, diffEncoder, diffId, diffComments, BaseOrDiff.Diff);

			int curRowNum = 1;
			int curRowGroupNum = 1;

			string baseScriptIdPrefix = baseFileInfo.ScriptId;
			string diffScriptIdPrefix = diffFileInfo.ScriptId;

			foreach (var diffItem in DiffItem.EnumerateDifferences(rawDiff))
			{
				bool atStart = diffItem.BaseStartLineNumber == 1;
				bool atEnd = diffItem.BaseLineCount == int.MaxValue;

				var baseLines = new List<LineAndComments>();
				for (int i = 0; i < diffItem.BaseLineCount && baseFileInfo.MoveNextLine(); ++i)
					baseLines.Add(GetLineAndComments(baseFileInfo));

				var diffLines = new List<LineAndComments>();
				if (isSingleFileView)
				{
					diffLines = baseLines;
				}
				else
				{
					for (int i = 0; i < diffItem.DiffLineCount && diffFileInfo.MoveNextLine(); ++i)
						diffLines.Add(GetLineAndComments(diffFileInfo));
				}

				var baseLinesLength = baseLines.Count();
				var diffLinesLength = diffLines.Count();

				// The end is the only case where the DiffInfo line counts may be incorrect. If there are in fact
				// zero lines then just continue, which should cause the foreach block to end and we'll continue
				// like the DiffItem never existed.
				if (atEnd && diffItem.DiffType == DiffType.Unchanged && baseLinesLength == 0 && diffLinesLength == 0)
					continue;

				var curGroup = fileView.CreateRowGroup();
				curGroup.AppendCSSClass(diffItem.DiffType.ToString());
				curGroup.ID = "rowgroup" + (curRowGroupNum++).ToString();
				fileView.AddItem(curGroup);

				var numPasses = 1;
				if (DiffViewOptions.IsUnified && diffItem.DiffType != DiffType.Unchanged)
					numPasses = 2;

				for (int pass = 1; pass <= numPasses; ++pass)
				{
					int lastLineWithComment = 0;
					int nextLineWithComment = 0;
					for (int i = 0; i < Math.Max(baseLinesLength, diffLinesLength); ++i)
					{
						var row = curGroup.CreateRow();
						if (pass == 1)
						{
							if (DiffViewOptions.IsUnified && diffItem.DiffType != DiffType.Unchanged)
								row.AppendCSSClass("Base");
						}
						else if (pass == 2)
						{
							Debug.Assert(DiffViewOptions.IsUnified);
							if (DiffViewOptions.IsUnified && diffItem.DiffType != DiffType.Unchanged)
								row.AppendCSSClass("Diff");
						}

						if (pass == 1)
						{
							// Check if we should omit any lines.
							if (diffItem.DiffType == DiffType.Unchanged &&
								DiffViewOptions.OmitUnchangedLines)
							{
								int contextLineCount = 50;
								if (baseLinesLength >= ((atStart || atEnd) ? contextLineCount : contextLineCount*2))
								{
									if (i >= nextLineWithComment)
									{
										lastLineWithComment = nextLineWithComment;
										if (isSingleFileView)
										{
											nextLineWithComment = baseLines.IndexOfFirst(x => !x.Comments.IsNullOrEmpty(), i);
										}
										else
										{
											nextLineWithComment = Math.Min(
												baseLines.IndexOfFirst(x => !x.Comments.IsNullOrEmpty(), i),
												diffLines.IndexOfFirst(x => !x.Comments.IsNullOrEmpty(), i));
										}
									}
									if (((atStart && i == 0) || (i - lastLineWithComment == contextLineCount)) &&
										((atEnd && nextLineWithComment == baseLinesLength) || (nextLineWithComment - i > contextLineCount)))
									{
										// Skip a bunch of lines!
										row = GetUnchangedLinesBreak(fileView.ColumnCount);
										row.ID = "row" + (curRowNum++).ToString();
										curGroup.AddItem(row);
										i = nextLineWithComment - ((atEnd && nextLineWithComment == baseLinesLength) ? 0 : 50);
										continue;
									}
								}
							}
						}

						if (i < baseLinesLength && pass == 1)
						{
							string scriptId = baseScriptIdPrefix + baseLines[i].LineNum.ToString();
							row["Num Base"].ID = scriptId + "_linenumber";
							row["Num Base"].Text = baseLines[i].LineNum.ToString();
							row["Txt Base"].ID = scriptId;
							row["Txt Base"].Add(EncodeLineTextAndComments(baseFileInfo.Encoder, "base", baseLines[i]));
						}
						if (i < diffLinesLength && !isSingleFileView)
						{
							string scriptId = diffScriptIdPrefix + diffLines[i].LineNum.ToString();
							if (DiffViewOptions.IsSplit || pass == 2 || (pass == 1 && diffItem.DiffType == DiffType.Unchanged))
							{
								row["Num Diff"].ID = scriptId + "_linenumber";
								row["Num Diff"].Text = diffLines[i].LineNum.ToString();
							}
							if (DiffViewOptions.IsSplit || pass == 2)
							{
								row["Txt Diff"].ID = scriptId;
								row["Txt Diff"].Add(EncodeLineTextAndComments(diffFileInfo.Encoder, "diff", diffLines[i]));
							}
						}
						row.ID = "row" + (curRowNum++).ToString();
						curGroup.AddItem(row);
					}
				}
			}

			encoderStyles = baseEncoder.GetEncoderCssStream();

			baseEncoder.Dispose();
			diffEncoder.Dispose();

			return fileView;
		}