1749 lines
63 KiB
C#
1749 lines
63 KiB
C#
///////////////////////////////////////////////////////////
|
|
// WikiPage.cs
|
|
// Implementation of Class WikiPage
|
|
// CSLA type: Editable Root
|
|
// Created on: 29-Rocktober-2008
|
|
// Object design: John
|
|
// Coded: 29-Rocktober-2008
|
|
///////////////////////////////////////////////////////////
|
|
|
|
using System;
|
|
using System.Data;
|
|
using CSLA.Data;
|
|
using GZTW.Data;
|
|
using CSLA;
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
//case 73
|
|
namespace GZTW.AyaNova.BLL
|
|
{
|
|
/// <summary>
|
|
/// WikiPage object
|
|
///
|
|
/// </summary>
|
|
[Serializable]
|
|
public class WikiPage : BusinessBase
|
|
{
|
|
|
|
#region Attributes
|
|
|
|
|
|
private Guid mID;
|
|
|
|
private SmartDate mCreated;
|
|
private SmartDate mModified;
|
|
private bool mInternalOnly;
|
|
private Guid mCreator;
|
|
private Guid mModifier;
|
|
private Guid mRootObjectID = Guid.Empty;
|
|
private RootObjectTypes mRootObjectType;
|
|
private string mTitle = null;
|
|
private byte[] mContent = null;
|
|
private bool bReadOnly = false;
|
|
|
|
//case 1584
|
|
private RootObjectTypes mExactObjectType;
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// Private constructor to prevent direct instantiation
|
|
/// </summary>
|
|
private WikiPage()
|
|
{
|
|
//New ID
|
|
mID = Guid.NewGuid();
|
|
|
|
InternalOnly = true;
|
|
|
|
//Set record history to defaults
|
|
mCreated = new SmartDate(DBUtil.CurrentWorkingDateTime);
|
|
mModified = new SmartDate();
|
|
mCreator = Guid.Empty;
|
|
mModifier = Guid.Empty;
|
|
|
|
|
|
//pre-break various rules
|
|
Title = "";
|
|
RootObjectID = Guid.Empty;
|
|
RootObjectType = RootObjectTypes.Nothing;
|
|
mExactObjectType = RootObjectTypes.Nothing;
|
|
mContent = new byte[0];
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Business properties
|
|
|
|
/// <summary>
|
|
/// Internal Unique GUID value of WikiPage record in database
|
|
/// </summary>
|
|
public Guid ID
|
|
{
|
|
get
|
|
{
|
|
return mID;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get created date
|
|
///
|
|
///
|
|
/// </summary>
|
|
public string Created
|
|
{
|
|
get
|
|
{
|
|
return mCreated.ToString();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get modified date
|
|
///
|
|
///
|
|
/// </summary>
|
|
public string Modified
|
|
{
|
|
get
|
|
{
|
|
return mModified.ToString();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get user record ID of person who created this record
|
|
///
|
|
///
|
|
/// </summary>
|
|
public Guid Creator
|
|
{
|
|
get
|
|
{
|
|
return mCreator;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get user ID of person who modified this record
|
|
///
|
|
///
|
|
/// </summary>
|
|
public Guid Modifier
|
|
{
|
|
get
|
|
{
|
|
return mModifier;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// AyaNova object ID
|
|
/// </summary>
|
|
public Guid RootObjectID
|
|
{
|
|
get
|
|
{
|
|
return mRootObjectID;
|
|
}
|
|
set
|
|
{
|
|
if (bReadOnly)
|
|
ThrowSetError();
|
|
else
|
|
{
|
|
if (mRootObjectID != value)
|
|
{
|
|
mRootObjectID = value;
|
|
BrokenRules.Assert("RootObjectID", "Error.Object.RequiredFieldEmpty,WikiPage.Label.RootObjectID", "RootObjectID", value == Guid.Empty);
|
|
|
|
MarkDirty();
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// AyaNova object type
|
|
/// </summary>
|
|
public RootObjectTypes RootObjectType
|
|
{
|
|
get
|
|
{
|
|
return mRootObjectType;
|
|
}
|
|
set
|
|
{
|
|
if (bReadOnly)
|
|
ThrowSetError();
|
|
else
|
|
{
|
|
if (mRootObjectType != value)
|
|
{
|
|
mRootObjectType = value;
|
|
BrokenRules.Assert("RootObjectType", "Error.Object.RequiredFieldEmpty,WikiPage.Label.RootObjectType", "RootObjectType", value == RootObjectTypes.Nothing);
|
|
|
|
MarkDirty();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Wikipages associated with any workorder type are saved attributed to rootobject type Workorder.
|
|
/// Since version 7 that has been deprecated but to maintain backward compatibility wikipages are still
|
|
/// associated with Workorder.
|
|
///
|
|
/// This properly shows the actual exact workorder type that retrieved this wikipage object.
|
|
/// </summary>
|
|
public RootObjectTypes ExactRootObjectType
|
|
{
|
|
get
|
|
{
|
|
return mExactObjectType;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///If true (default) then only visible to AyaNova usertypes who are not
|
|
///a client.
|
|
/// </summary>
|
|
public bool InternalOnly
|
|
{
|
|
get
|
|
{
|
|
return mInternalOnly;
|
|
}
|
|
set
|
|
{
|
|
if (bReadOnly)
|
|
ThrowSetError();
|
|
else
|
|
{
|
|
if (mInternalOnly != value)
|
|
{
|
|
mInternalOnly = value;
|
|
MarkDirty();
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Title displayed on wiki page if set 1-255 Unicode characters
|
|
/// </summary>
|
|
public string Title
|
|
{
|
|
get
|
|
{
|
|
return mTitle;
|
|
}
|
|
set
|
|
{
|
|
if (bReadOnly)
|
|
ThrowSetError();
|
|
else
|
|
{
|
|
if (mTitle != value)
|
|
{
|
|
mTitle = value;
|
|
|
|
//BrokenRules.Assert("NameRequired",
|
|
// "Error.Object.RequiredFieldEmpty,WikiPage.Label.Title",
|
|
// "Title", value.Length == 0);
|
|
|
|
BrokenRules.Assert("NameLength",
|
|
"Error.Object.FieldLengthExceeded255,WikiPage.Label.Title",
|
|
"Title", value.Length > 255);
|
|
MarkDirty();
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Internal flag set from AyaNova / WBI when content is dirty
|
|
/// but WikiPage hasn't had it's content updated yet
|
|
/// </summary>
|
|
public bool HasDirtyContent
|
|
{
|
|
set
|
|
{
|
|
if (value == true)
|
|
MarkDirty();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates whether page has content or not
|
|
/// </summary>
|
|
public bool HasContent
|
|
{
|
|
get
|
|
{
|
|
return mContent.GetLength(0) > 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the content of the wiki page which is stored in HTML
|
|
/// format natively as plain text
|
|
/// </summary>
|
|
public string GetContentAsPlainText
|
|
{
|
|
get
|
|
{
|
|
string s = GetContentAsString;
|
|
s = s.Replace("<br/>", "\r\n");
|
|
s = s.Replace("<br>", "\r\n");
|
|
s = System.Text.RegularExpressions.Regex.Replace(s, "<[^>]+?>", "");
|
|
s = AyaBizUtils.rxAyaImageTags.Replace(s, "");
|
|
s = AyaBizUtils.rxAyaLinks.Replace(s, "");
|
|
s = s.Replace(" ", " ");
|
|
s = s.Replace(">", ">");
|
|
s = s.Replace("<", "<");
|
|
return s;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Get's content with WBI ready AyaImage: tags and AyaNova: urls
|
|
/// </summary>
|
|
/// <param name="bForEditing">true=don't build file list</param>
|
|
/// <returns></returns>
|
|
public string GetContentAsWBIReadyHTML(bool bForEditing)
|
|
{
|
|
|
|
string s = GetContentAsString;
|
|
MatchCollection mc = null;
|
|
List<string> list = new List<string>();
|
|
|
|
|
|
#region urlify any urls that aren't already anchored
|
|
if (!bForEditing)
|
|
{
|
|
|
|
//remove all anchored links first
|
|
MatchCollection mcHrefs = AyaBizUtils.rxAllHrefTags.Matches(s);
|
|
foreach (Match m in mcHrefs)
|
|
s = s.Replace(m.Value, "[PH:" + m.Index + "]");
|
|
|
|
//now find all the anchorable links
|
|
|
|
List<string> listProtocoled = new List<string>();
|
|
List<string> listEmail = new List<string>();
|
|
List<string> listWeb = new List<string>();
|
|
//This is for when a user in wbi is viewing a read only
|
|
//wiki page to automatically display all discernable urls
|
|
//with anchor tags so the user can click on them to activate
|
|
list.Clear();
|
|
|
|
//Hack to avoid double processing
|
|
//without this urls with protocols of mail or http
|
|
//pre-existing (and more than once) will get double replaced
|
|
s = s.Replace("http://", "");
|
|
s = s.Replace("mailto://", "");
|
|
|
|
|
|
mc = AyaBizUtils.rxAllUrlsWithProtocol.Matches(s);
|
|
foreach (Match m in mc)
|
|
if (!listProtocoled.Contains(m.Value))
|
|
listProtocoled.Add(m.Value);
|
|
|
|
mc = AyaBizUtils.rxEmailWithoutProtocol.Matches(s);
|
|
foreach (Match m in mc)
|
|
if (!listEmail.Contains(m.Groups["url"].Value))
|
|
listEmail.Add(m.Groups["url"].Value);
|
|
|
|
mc = AyaBizUtils.rxWebsiteUrlsWithoutProtocol.Matches(s);
|
|
foreach (Match m in mc)
|
|
if (!listWeb.Contains(m.Groups["url"].Value))
|
|
listWeb.Add(m.Groups["url"].Value);
|
|
|
|
//iterate list and replace matches with anchor tag
|
|
foreach (string surl in listProtocoled)
|
|
s = s.Replace(surl, "<a href=\"" + surl + "\" target=\"_blank\">" + surl + "</a>");
|
|
|
|
foreach (string surl in listEmail)
|
|
s = s.Replace(surl, "<a href=\"" + "mailto://" + surl + "\" target=\"_blank\">" + surl + "</a>");
|
|
|
|
foreach (string surl in listWeb)
|
|
s = s.Replace(surl, "<a href=\"" + "http://" + surl + "\" target=\"_blank\">" + surl + "</a>");
|
|
|
|
//ok, at this point we should have proper urls in s and original ones still X'd out
|
|
//so replace the x's with the original url
|
|
foreach (Match m in mcHrefs)
|
|
{
|
|
//need to insert a target tag for the url if none found
|
|
string sTarget = m.Value;
|
|
if (!AyaBizUtils.rsHrefContainsTargetTag.IsMatch(sTarget))
|
|
{
|
|
sTarget = sTarget.Replace("\">", "\" target=\"_blank\">");
|
|
}
|
|
s = s.Replace("[PH:" + m.Index + "]", sTarget);
|
|
}
|
|
|
|
|
|
}
|
|
#endregion
|
|
|
|
#region Images
|
|
mc = AyaBizUtils.rxAyaImageTags.Matches(s);
|
|
foreach (Match m in mc)
|
|
{
|
|
|
|
s = s.Replace(m.Value, "<img src=\"AyaImage.ashx?id=" + m.Groups["guid"].Value + "\">");
|
|
}
|
|
#endregion images
|
|
|
|
#region AyaNova: links
|
|
if (!bForEditing)
|
|
{
|
|
mc = AyaBizUtils.rxAyaLinks.Matches(s);
|
|
foreach (Match m in mc)
|
|
{
|
|
TypeAndID tid = TypeAndID.ParseAyaURL(m.Value);
|
|
if (tid != null)
|
|
{
|
|
|
|
string toUrl = "";
|
|
switch (tid.RootObjectType)
|
|
{
|
|
case RootObjectTypes.Part:
|
|
toUrl = "PartInventoryView.aspx?id=" + tid.ID.ToString();
|
|
break;
|
|
case RootObjectTypes.WikiPage:
|
|
toUrl = "Wiki?id=" + tid.ID.ToString();
|
|
break;
|
|
|
|
default:
|
|
{
|
|
if (tid.ID == Guid.Empty)
|
|
toUrl = tid.RootObjectType.ToString() + "Edit.aspx";
|
|
else
|
|
toUrl = tid.RootObjectType.ToString() + "Edit.aspx?id=" + tid.ID.ToString();
|
|
}
|
|
break;
|
|
}
|
|
|
|
string name = NameFetcher.GetItem(tid).RecordName;
|
|
s = s.Replace(m.Value, "<a href=\"" + toUrl + "\"target=\"_blank\">" + name + "</a>");
|
|
|
|
}
|
|
}
|
|
}
|
|
#endregion ayanova: links
|
|
|
|
#region wo: style links
|
|
if (!bForEditing)
|
|
{
|
|
mc = AyaBizUtils.rxAyaWOLinks.Matches(s);
|
|
foreach (Match m in mc)
|
|
{
|
|
Guid g = Guid.Empty;
|
|
|
|
WorkorderTypes wotype = WorkorderTypes.Service;
|
|
string sType = m.Groups[1].Value.ToLowerInvariant();
|
|
switch (sType)
|
|
{
|
|
case "quote:":
|
|
wotype = WorkorderTypes.Quote;
|
|
break;
|
|
case "pm:":
|
|
wotype = WorkorderTypes.PreventiveMaintenance;
|
|
break;
|
|
|
|
}
|
|
g = WorkorderInternalIDFetcher.GetItem(m.Groups[2].Value, wotype);
|
|
string toUrl = "WorkorderEdit.aspx?id=" + g.ToString();
|
|
if (g != Guid.Empty && (AyaBizUtils.InYourRegion(ObjectRegionIDFetcher.ObjectRegion(new TypeAndID(RootObjectTypes.Workorder, g)))))//case 58
|
|
s = s.Replace(m.Value, "<a href=\"" + toUrl + "\"target=\"_blank\">" + m.Value + "</a>");
|
|
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get's content with AyaNova RI ready AyaImage: tags and AyaNova: urls
|
|
/// </summary>
|
|
/// <param name="bForEditing">true=don't build file list</param>
|
|
/// <param name="baseSiteURL"></param>
|
|
/// <returns></returns>
|
|
public string GetContentAsRIReadyHTML(bool bForEditing, string baseSiteURL)
|
|
{
|
|
string s = GetContentAsString;
|
|
MatchCollection mc = null;
|
|
List<string> list = new List<string>();
|
|
|
|
#region urlify any urls that aren't already anchored
|
|
if (!bForEditing)
|
|
{
|
|
|
|
//remove all anchored links first
|
|
MatchCollection mcHrefs = AyaBizUtils.rxAllHrefTags.Matches(s);
|
|
foreach (Match m in mcHrefs)
|
|
s = s.Replace(m.Value, "[PH:" + m.Index + "]");
|
|
|
|
//now find all the anchorable links
|
|
|
|
List<string> listProtocoled = new List<string>();
|
|
List<string> listEmail = new List<string>();
|
|
List<string> listWeb = new List<string>();
|
|
//This is for when a user in wbi is viewing a read only
|
|
//wiki page to automatically display all discernable urls
|
|
//with anchor tags so the user can click on them to activate
|
|
list.Clear();
|
|
|
|
//Hack to avoid double processing
|
|
//without this urls with protocols of mail or http
|
|
//pre-existing (and more than once) will get double replaced
|
|
s = s.Replace("http://", "");
|
|
s = s.Replace("mailto://", "");
|
|
|
|
|
|
mc = AyaBizUtils.rxAllUrlsWithProtocol.Matches(s);
|
|
foreach (Match m in mc)
|
|
if (!listProtocoled.Contains(m.Value))
|
|
listProtocoled.Add(m.Value);
|
|
|
|
mc = AyaBizUtils.rxEmailWithoutProtocol.Matches(s);
|
|
foreach (Match m in mc)
|
|
if (!listEmail.Contains(m.Groups["url"].Value))
|
|
listEmail.Add(m.Groups["url"].Value);
|
|
|
|
mc = AyaBizUtils.rxWebsiteUrlsWithoutProtocol.Matches(s);
|
|
foreach (Match m in mc)
|
|
if (!listWeb.Contains(m.Groups["url"].Value))
|
|
listWeb.Add(m.Groups["url"].Value);
|
|
|
|
//iterate list and replace matches with anchor tag
|
|
foreach (string surl in listProtocoled)
|
|
s = s.Replace(surl, "<a href=\"" + surl + "\" target=\"_blank\">" + surl + "</a>");
|
|
|
|
foreach (string surl in listEmail)
|
|
s = s.Replace(surl, "<a href=\"" + "mailto://" + surl + "\" target=\"_blank\">" + surl + "</a>");
|
|
|
|
foreach (string surl in listWeb)
|
|
s = s.Replace(surl, "<a href=\"" + "http://" + surl + "\" target=\"_blank\">" + surl + "</a>");
|
|
|
|
//ok, at this point we should have proper urls in s and original ones still X'd out
|
|
//so replace the x's with the original url
|
|
foreach (Match m in mcHrefs)
|
|
{
|
|
//need to insert a target tag for the url if none found
|
|
string sTarget = m.Value;
|
|
if (!AyaBizUtils.rsHrefContainsTargetTag.IsMatch(sTarget))
|
|
{
|
|
sTarget = sTarget.Replace("\">", "\" target=\"_blank\">");
|
|
}
|
|
s = s.Replace("[PH:" + m.Index + "]", sTarget);
|
|
}
|
|
|
|
|
|
}
|
|
#endregion
|
|
|
|
#region Images
|
|
mc = AyaBizUtils.rxAyaImageTags.Matches(s);
|
|
foreach (Match m in mc)
|
|
{
|
|
s = s.Replace(m.Value, "<img src=\"" + baseSiteURL + "ayImage/view/" + m.Groups["guid"].Value + "\">");
|
|
}
|
|
#endregion images
|
|
|
|
#region AyaNova: links
|
|
if (!bForEditing)
|
|
{
|
|
mc = AyaBizUtils.rxAyaLinks.Matches(s);
|
|
foreach (Match m in mc)
|
|
{
|
|
TypeAndID tid = TypeAndID.ParseAyaURL(m.Value);
|
|
if (tid != null)
|
|
{
|
|
|
|
string toUrl = "";
|
|
switch (tid.RootObjectType)
|
|
{
|
|
case RootObjectTypes.Nothing:
|
|
toUrl = baseSiteURL;
|
|
break;
|
|
|
|
//In RI all editable objects have a path that is the root object type name plus /edit/{guidvalue}
|
|
//so this should work (in theory)
|
|
default:
|
|
{
|
|
if (tid.ID == Guid.Empty)
|
|
toUrl = tid.RootObjectType.ToString() + "/edit/";
|
|
else
|
|
toUrl = tid.RootObjectType.ToString() + "/edit/" + tid.ID.ToString();
|
|
}
|
|
break;
|
|
}
|
|
|
|
string name = NameFetcher.GetItem(tid).RecordName;
|
|
s = s.Replace(m.Value, "<a href=\"" + toUrl + "\"target=\"_blank\">" + name + "</a>");
|
|
|
|
}
|
|
}
|
|
}
|
|
#endregion ayanova: links
|
|
|
|
#region wo: style links
|
|
if (!bForEditing)
|
|
{
|
|
mc = AyaBizUtils.rxAyaWOLinks.Matches(s);
|
|
foreach (Match m in mc)
|
|
{
|
|
Guid g = Guid.Empty;
|
|
|
|
WorkorderTypes wotype = WorkorderTypes.Service;
|
|
string sType = m.Groups[1].Value.ToLowerInvariant();
|
|
switch (sType)
|
|
{
|
|
case "quote:":
|
|
wotype = WorkorderTypes.Quote;
|
|
break;
|
|
case "pm:":
|
|
wotype = WorkorderTypes.PreventiveMaintenance;
|
|
break;
|
|
|
|
}
|
|
|
|
g = WorkorderInternalIDFetcher.GetItem(m.Groups[2].Value, wotype);
|
|
|
|
//*** WRONG: this fuckery is because RI has separate edit pages for different workorder types whereas older UI's didn't ***: wrong
|
|
//case 2026 - above is wrong, actually it's just workorder/edit and the id now
|
|
//changed block below to reflect this
|
|
string toUrl = string.Empty;
|
|
switch (wotype)
|
|
{
|
|
case WorkorderTypes.Service:
|
|
toUrl = baseSiteURL + "Workorder/edit/" + g.ToString();
|
|
break;
|
|
case WorkorderTypes.Quote:
|
|
toUrl = baseSiteURL + "Workorder/edit/" + g.ToString();
|
|
break;
|
|
case WorkorderTypes.PreventiveMaintenance:
|
|
toUrl = baseSiteURL + "Workorder/edit/" + g.ToString();
|
|
break;
|
|
}
|
|
|
|
//Note wbi code checks if in region here, I think we'll let RI handle that
|
|
s = s.Replace(m.Value, "<a href=\"" + toUrl + "\"target=\"_blank\">" + m.Value + "</a>");
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
|
|
|
|
RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;
|
|
|
|
Regex rxBody = new Regex("<body>(?<theBody>.*)</body>", options);
|
|
Match matchBody = rxBody.Match(s);
|
|
string theBody = string.Empty;
|
|
|
|
Regex rxStyle = new Regex("<style(?<theStyle>.*)</style>", options);
|
|
Match matchStyle = rxStyle.Match(s);
|
|
string theStyle = string.Empty;
|
|
|
|
if (matchStyle.Success)
|
|
{
|
|
theStyle = matchStyle.Groups["theStyle"].Value;
|
|
}
|
|
|
|
if (matchBody.Success)
|
|
{
|
|
theBody = matchBody.Groups["theBody"].Value;
|
|
}
|
|
|
|
//case 1975
|
|
//Handle empty body
|
|
if (string.IsNullOrWhiteSpace(theBody) || !string.IsNullOrWhiteSpace(s))
|
|
theBody = s;
|
|
|
|
|
|
if (bForEditing)
|
|
return theBody;//don't return the style, needs to be fetched separately then fed to the editor at the client end separately
|
|
else
|
|
{
|
|
if (string.IsNullOrWhiteSpace(theStyle))
|
|
return theBody;
|
|
else
|
|
return "<style " + theStyle + "</style>\r\n" + theBody;
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the style
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public string GetStyleContentOnly()
|
|
{
|
|
string s = GetContentAsString;
|
|
RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;
|
|
|
|
Regex rxStyle = new Regex("<style(?<theStyle>.*)</style>", options);
|
|
Match matchStyle = rxStyle.Match(s);
|
|
string theStyle = string.Empty;
|
|
|
|
if (matchStyle.Success)
|
|
{
|
|
theStyle = matchStyle.Groups["theStyle"].Value;
|
|
return "<style " + theStyle + "</style>";
|
|
}
|
|
|
|
return "<style></style>";
|
|
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetches HTML fragment for file list area of web page in WBI based on files in current wiki page
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public string GetFileListContentForWBI()
|
|
{
|
|
#region Files
|
|
|
|
//If a file is read/write then it's delete automatically
|
|
bool bFilesReadOnly = AyaBizUtils.Right("Object.AyaFile") < (int)SecurityLevelTypes.ReadWrite;
|
|
bool bFilesVisible = AyaBizUtils.Right("Object.AyaFile") > (int)SecurityLevelTypes.NoAccess;
|
|
System.Text.StringBuilder sbFiles = new StringBuilder();
|
|
|
|
if (bFilesVisible)
|
|
{
|
|
|
|
//Build up file list / links
|
|
AyaFileList fl = AyaFileList.GetList(this.ID);
|
|
if (!(fl.Count == 0))
|
|
{
|
|
sbFiles.Append("<ul>");
|
|
|
|
foreach (AyaFileList.AyaFileListInfo i in fl)
|
|
{
|
|
if (i.FileType != AyaFileType.EmbeddedWikiImage)
|
|
{
|
|
sbFiles.Append("<li><a href=\"AyaFileHandler.ashx?id=");
|
|
sbFiles.Append(i.LT_O_AyaFile.Value.ToString());
|
|
sbFiles.Append("\">");
|
|
sbFiles.Append(i.LT_O_AyaFile.Display);
|
|
sbFiles.Append("</a>");
|
|
sbFiles.Append(" - ");
|
|
sbFiles.Append(i.LT_AyaFile_Label_FileSize);
|
|
sbFiles.Append(" [");
|
|
sbFiles.Append(i.LT_Common_Label_Creator.Display);
|
|
sbFiles.Append(" ");
|
|
sbFiles.Append(i.LT_Common_Label_Created);
|
|
sbFiles.Append("] ");
|
|
|
|
//can delete?
|
|
if (!bFilesReadOnly)
|
|
{//<a href="http://www.ayanova.com" onClick="return confirm_delete();"><img src="graphics/Delete16.gif"/></a>
|
|
//put a delete link on the end of the file list item
|
|
sbFiles.Append("<a href=\"AyaFileHandler.ashx?id=");
|
|
sbFiles.Append(i.LT_O_AyaFile.Value.ToString());
|
|
sbFiles.Append("&d=1\" onClick=\"return confirm_delete();\">");
|
|
sbFiles.Append("<img src=\"graphics/Delete16.png\">");
|
|
sbFiles.Append("</a>");
|
|
}
|
|
//close list item tag
|
|
sbFiles.Append("</li>");
|
|
|
|
}
|
|
}
|
|
sbFiles.Append("</ul>");
|
|
|
|
}
|
|
}
|
|
|
|
return sbFiles.ToString();
|
|
#endregion files
|
|
}
|
|
|
|
|
|
#region File list data structure
|
|
#pragma warning disable 1591
|
|
/// <summary>
|
|
/// Properties
|
|
/// </summary>
|
|
[Serializable]
|
|
public struct WikiFileInfo
|
|
{
|
|
public string Name { get; set; }
|
|
public string Id { get; set; }
|
|
public string Size { get; set; }
|
|
public string Creator { get; set; }
|
|
public string Created { get; set; }
|
|
|
|
}//end WikiFileInfo
|
|
#pragma warning restore 1591
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Flag indicating if current user can edit / delete wiki files
|
|
/// </summary>
|
|
public bool WikiFileReadOnly
|
|
{
|
|
get
|
|
{
|
|
return AyaBizUtils.Right("Object.AyaFile") < (int)SecurityLevelTypes.ReadWrite; ;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetches file list based on files in current wiki page
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public List<WikiFileInfo> GetFileListContentForRI()
|
|
{
|
|
#region Files
|
|
bool bFilesVisible = AyaBizUtils.Right("Object.AyaFile") > (int)SecurityLevelTypes.NoAccess;
|
|
|
|
List<WikiFileInfo> ret = new List<WikiFileInfo>();
|
|
|
|
if (bFilesVisible)
|
|
{
|
|
//Build up file list / links
|
|
AyaFileList fl = AyaFileList.GetList(this.ID);
|
|
if (!(fl.Count == 0))
|
|
{
|
|
foreach (AyaFileList.AyaFileListInfo i in fl)
|
|
{
|
|
if (i.FileType != AyaFileType.EmbeddedWikiImage)
|
|
{
|
|
WikiFileInfo fi = new WikiFileInfo();
|
|
fi.Id = i.LT_O_AyaFile.Value.ToString();
|
|
fi.Name = i.LT_O_AyaFile.Display;
|
|
fi.Size = i.LT_AyaFile_Label_FileSize;
|
|
fi.Creator = i.LT_Common_Label_Creator.Display;
|
|
fi.Created = i.LT_Common_Label_Created.ToString();
|
|
ret.Add(fi);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
#endregion files
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Set the content based on string
|
|
/// from WBI's html editor for wiki pages
|
|
/// (fixes up image tags)
|
|
/// </summary>
|
|
/// <param name="sContent"></param>
|
|
public void SetContentFromWBI(string sContent)
|
|
{
|
|
|
|
//convert the image links
|
|
//parse out the image links and convert them to AyaNova style image links
|
|
MatchCollection mc = AyaBizUtils.rxImages.Matches(sContent);
|
|
foreach (Match m in mc)
|
|
{
|
|
//parse out id
|
|
string sID = AyaBizUtils.rxGuid.Match(m.Value).Value;
|
|
if (!string.IsNullOrEmpty(sID))
|
|
sContent = sContent.Replace(m.Value, "[AyaImage:" + sID + "]");
|
|
}
|
|
|
|
#region Convert font sizes
|
|
//THIS WAS NOT REQUIRED AS THERE IS A CONTENT FILTER IN THE RADEDIT CONTROL
|
|
//TO NOT CONVERT FONTS TO SPANS WHICH FIXED THE PROBLEM
|
|
//BUT KEEPING FOR FUTURE REFERENCE JUST IN CASE
|
|
//convert the font sizes from pixels to points
|
|
//this is a requirement only because the easybyte rtf2html control
|
|
//goes snaky if the font sizes are specified in pixels
|
|
//This is what it likes: <font SIZE="2"
|
|
//this is what drives it nuts: font-size: 13px;
|
|
|
|
// Points Pixels Ems Percent
|
|
//6pt 8px 0.5em 50%
|
|
//7pt 9px 0.55em 55%
|
|
//7.5pt 10px 0.625em 62.5%
|
|
//8pt 11px 0.7em 70%
|
|
//9pt 12px 0.75em 75%
|
|
//10pt 13px 0.8em 80%
|
|
//10.5pt 14px 0.875em 87.5%
|
|
//11pt 15px 0.95em 95%
|
|
//12pt 16px 1em 100%
|
|
//13pt 17px 1.05em 105%
|
|
//13.5pt 18px 1.125em 112.5%
|
|
//14pt 19px 1.2em 120%
|
|
//14.5pt 20px 1.25em 125%
|
|
//15pt 21px 1.3em 130%
|
|
//16pt 22px 1.4em 140%
|
|
//17pt 23px 1.45em 145%
|
|
//18pt 24px 1.5em 150%
|
|
//20pt 26px 1.6em 160%
|
|
//22pt 29px 1.8em 180%
|
|
//24pt 32px 2em 200%
|
|
//26pt 35px 2.2em 220%
|
|
//27pt 36px 2.25em 225%
|
|
//28pt 37px 2.3em 230%
|
|
//29pt 38px 2.35em 235%
|
|
//30pt 40px 2.45em 245%
|
|
//32pt 42px 2.55em 255%
|
|
//34pt 45px 2.75em 275%
|
|
//36pt 48px 3em 300%
|
|
|
|
#endregion
|
|
|
|
|
|
SetContent(sContent);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the content in it's native Unicode encoded HTML format
|
|
///
|
|
/// </summary>
|
|
public string GetContentAsString
|
|
{
|
|
get
|
|
{
|
|
return Encoding.Unicode.GetString(AyaBizUtils.Decompress(mContent));
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Get the content as a memory stream
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public System.IO.MemoryStream GetContent()
|
|
{
|
|
|
|
return new System.IO.MemoryStream(AyaBizUtils.Decompress(mContent));
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the content based on string
|
|
/// </summary>
|
|
/// <param name="sContent"></param>
|
|
public void SetContent(string sContent)
|
|
{
|
|
using (MemoryStream ms = new MemoryStream(System.Text.Encoding.Unicode.GetBytes(sContent)))
|
|
{
|
|
SetContent(ms);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the content from memory stream
|
|
/// </summary>
|
|
/// <param name="mStream"></param>
|
|
public void SetContent(System.IO.MemoryStream mStream)
|
|
{
|
|
mStream.Position = 0;
|
|
//byte[] bData = new byte[mStream.Length + 1];//bugbug? Could this be wrong and resulting in the extra characters in the page?
|
|
byte[] bData = new byte[mStream.Length];
|
|
mStream.Read(bData, 0, (int)mStream.Length);
|
|
|
|
mContent = AyaBizUtils.Compress(bData);
|
|
MarkDirty();
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Flag - indicates if current user can open the wiki page for this object
|
|
/// See <see cref="WikiPage.ShowWikiLink(RootObjectTypes, Guid)"/> method for details
|
|
///
|
|
/// This is cached for the lifetime of this object
|
|
/// </summary>
|
|
|
|
public bool CanWiki//case 73
|
|
{
|
|
get
|
|
{
|
|
if (!bCanWiki.HasValue)
|
|
bCanWiki = WikiPage.ShowWikiLink(RootObjectTypes.WikiPage, mID);
|
|
return bCanWiki.Value;
|
|
}
|
|
|
|
}
|
|
//cache the result in case the UI calls this repeatedly
|
|
private bool? bCanWiki = null;
|
|
|
|
/// <summary>
|
|
/// Throw an error when a read only user
|
|
/// tries to set a property
|
|
/// (this should normally never be called unless someone is using the developer api since the UI
|
|
/// should prevent it from happening initially)
|
|
/// </summary>
|
|
private void ThrowSetError()
|
|
{
|
|
throw new System.Security.SecurityException
|
|
(
|
|
string.Format
|
|
(
|
|
LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToChange"),
|
|
LocalizedTextTable.GetLocalizedTextDirect("O.WikiPage")
|
|
)
|
|
);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region System.object overrides
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override string ToString()
|
|
{
|
|
return "WikiPage" + mID.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
/// <returns></returns>
|
|
public override bool Equals(Object obj)
|
|
{
|
|
if (obj == null || GetType() != obj.GetType()) return false;
|
|
WikiPage c = (WikiPage)obj;
|
|
return mID == c.mID;
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override int GetHashCode()
|
|
{
|
|
return ("WikiPage" + mID).GetHashCode();
|
|
}
|
|
#endregion
|
|
|
|
#region Searching
|
|
|
|
/// <summary>
|
|
/// Returns a search result object based on search terms
|
|
/// for the ID specified
|
|
/// </summary>
|
|
/// <param name="ID"></param>
|
|
/// <param name="searchTerms"></param>
|
|
/// <returns></returns>
|
|
public static SearchResult GetSearchResult(Guid ID, string[] searchTerms)
|
|
{
|
|
if (AyaBizUtils.Right("Object.WikiPage") < (int)SecurityLevelTypes.ReadOnly)
|
|
return new SearchResult();
|
|
|
|
SearchResult sr = new SearchResult();
|
|
|
|
WikiPage w = null;
|
|
try
|
|
{
|
|
w = WikiPage.GetItem(ID);
|
|
}
|
|
catch
|
|
{
|
|
//some kind of failure then return empty sr
|
|
return sr;
|
|
}
|
|
|
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
|
|
|
//Get description for later adding to description string
|
|
sr.Description = NameFetcher.GetItem(new TypeAndID(w.RootObjectType, w.RootObjectID)).RecordName;
|
|
sr.AncestorRootObjectID = w.RootObjectID; ;
|
|
sr.AncestorRootObjectType = w.RootObjectType;
|
|
|
|
|
|
sr.Created = w.mCreated;
|
|
sr.Modified = w.mModified;
|
|
sr.Creator = w.Creator;
|
|
sr.Modifier = w.Modifier;
|
|
|
|
//Case 58
|
|
//Is the document's ancestor within their region?
|
|
if (
|
|
!AyaBizUtils.InYourRegion(
|
|
ObjectRegionIDFetcher.ObjectRegion(
|
|
new TypeAndID(
|
|
sr.AncestorRootObjectType, sr.AncestorRootObjectID
|
|
)
|
|
)
|
|
)
|
|
) return new SearchResult();//case 58
|
|
|
|
//Security check..do they have rights to the ancestor object?
|
|
//BUGBUG case 1387, as a workaround just allow it if it's of type workorder
|
|
if (sr.AncestorRootObjectType != RootObjectTypes.Workorder)
|
|
if (AyaBizUtils.Right("Object." + sr.AncestorRootObjectType.ToString()) < (int)SecurityLevelTypes.ReadOnly)
|
|
return new SearchResult();
|
|
|
|
//Formulate results
|
|
sb.Append(w.Title);
|
|
sb.Append(" ");
|
|
sb.Append(w.GetContentAsPlainText.Replace("\r\n", " "));
|
|
ExtractAndRank er = new ExtractAndRank();
|
|
er.Process(sb.ToString().Trim(), searchTerms);
|
|
sr.Extract = er.Extract;
|
|
sr.Rank = er.Ranking;
|
|
|
|
//flip back to what should be opened
|
|
sr.AncestorRootObjectID = ID;
|
|
sr.AncestorRootObjectType = RootObjectTypes.WikiPage;
|
|
return sr;
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region Static methods
|
|
|
|
|
|
#region rights
|
|
/// <summary>
|
|
/// Indicates whether user has rights to view a wiki page given the TypeAndID object
|
|
///
|
|
/// </summary>
|
|
/// <param name="tid">Object the wiki page want's to be viewed for</param>
|
|
/// <returns>true if can view and should show link to view</returns>
|
|
public static bool ShowWikiLink(TypeAndID tid)
|
|
{
|
|
return ShowWikiLink(tid.RootObjectType, tid.ID);
|
|
}
|
|
/// <summary>
|
|
/// Indicates whether user has rights to view a wiki page given the object type and id
|
|
/// Checks wikipage rights, global wiki page rights, checks if their own user wiki page and finally checks object rights
|
|
/// </summary>
|
|
/// <param name="oType">Object type who's wiki page is desired to be viewed</param>
|
|
/// <param name="RootObjectID">ID of object</param>
|
|
/// <returns>true if can view and should show link to view</returns>
|
|
public static bool ShowWikiLink(RootObjectTypes oType, Guid RootObjectID)
|
|
{
|
|
bool AllowedToViewPage = (AyaBizUtils.Right("Object.WikiPage") > (int)SecurityLevelTypes.NoAccess);
|
|
bool AllowedToViewGlobalWikiPage = (AyaBizUtils.Right("Object.GlobalWikiPage") > (int)SecurityLevelTypes.NoAccess);
|
|
|
|
//Special right - GlobalWikiPage supersedes all other rights
|
|
if (oType == RootObjectTypes.Global)
|
|
{
|
|
return AllowedToViewGlobalWikiPage;
|
|
|
|
}
|
|
|
|
//Can they view non global wiki pages at all
|
|
if (!AllowedToViewPage)
|
|
return false;
|
|
|
|
//Special right - their own wiki page
|
|
if (oType == RootObjectTypes.User && RootObjectID == User.CurrentThreadUserID)
|
|
{
|
|
//Users can *always* view their own personal wiki page if they have rights to view wiki pages
|
|
return true;
|
|
}
|
|
|
|
//From here on rights will follow biz object rights
|
|
|
|
//If they are allowed to view the object they are allowed to view the wiki page
|
|
//then Bob's your lobster
|
|
if (AyaBizUtils.Right(oType) > (int)SecurityLevelTypes.NoAccess)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Indicates whether user has rights to edit the wiki page for the object in question
|
|
/// </summary>
|
|
/// <param name="tid">TypeAndID combined of object who's wiki page is desired to be edited</param>
|
|
/// <returns></returns>
|
|
public static bool CanEditWikiPage(TypeAndID tid)
|
|
{
|
|
return CanEditWikiPage(tid.RootObjectType, tid.ID);
|
|
}
|
|
/// <summary>
|
|
/// Indicates whether user has rights to edit the wiki page for the object in question
|
|
/// </summary>
|
|
/// <param name="oType">Object type who's wiki page is desired to be edited</param>
|
|
/// <param name="RootObjectID">ID of object</param>
|
|
/// <returns></returns>
|
|
public static bool CanEditWikiPage(RootObjectTypes oType, Guid RootObjectID)
|
|
{
|
|
//If you can't even view it you certainly can't edit it
|
|
if (!ShowWikiLink(oType, RootObjectID)) return false;
|
|
|
|
bool AllowedToEditPage = (AyaBizUtils.Right("Object.WikiPage") > (int)SecurityLevelTypes.ReadOnly);
|
|
bool AllowedToEditGlobalWikiPage = (AyaBizUtils.Right("Object.GlobalWikiPage") > (int)SecurityLevelTypes.ReadOnly);
|
|
|
|
//Special right - GlobalWikiPage supersedes all other rights
|
|
if (oType == RootObjectTypes.Global)
|
|
{
|
|
return AllowedToEditGlobalWikiPage;
|
|
|
|
}
|
|
|
|
//Can they edit non global wiki pages at all
|
|
if (!AllowedToEditPage)
|
|
return false;
|
|
|
|
//Special right - their own wiki page
|
|
if (oType == RootObjectTypes.User && RootObjectID == User.CurrentThreadUserID)
|
|
{
|
|
//Users can *always* edit their own personal wiki page if they have rights to edit wiki pages
|
|
return true;
|
|
}
|
|
|
|
//From here on rights will follow biz object rights
|
|
|
|
//If they are allowed to edit the object they are allowed to edit the wiki page
|
|
//then Bob's your lobster
|
|
if (AyaBizUtils.Right(oType) > (int)SecurityLevelTypes.ReadOnly)
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
#endregion rights
|
|
|
|
|
|
/// <summary>
|
|
/// Fetch existing WikiPage
|
|
///
|
|
/// </summary>
|
|
/// <returns>WikiPage</returns>
|
|
/// <param name="ID">WikiPage Guid</param>
|
|
public static WikiPage GetItem(Guid ID)
|
|
{
|
|
return (WikiPage)DataPortal.Fetch(new Criteria(ID, null, null, RootObjectTypes.Nothing));
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetch existing WikiPage for object type and id specified
|
|
/// if none exists it will be created
|
|
/// </summary>
|
|
/// <returns>WikiPage</returns>
|
|
/// <param name="tid">Type and ID of object page is attached to</param>
|
|
/// <example><code>
|
|
/// //Get a wiki page for a specified client
|
|
/// WikiPage wp = WikiPage.GetItem(new TypeAndID(RootObjectTypes.Client, ClientID));
|
|
/// </code></example>
|
|
public static WikiPage GetItem(TypeAndID tid)
|
|
{
|
|
//case 1975
|
|
//added because this should have always been here
|
|
//to lighten client code
|
|
if (tid.RootObjectType == RootObjectTypes.WikiPage)
|
|
{
|
|
return GetItem(tid.ID);
|
|
}
|
|
//case 1584
|
|
//all wikipages for the different workorder types are simply stored with object type "workorder"
|
|
//but that object type isn't used anymore post case 1387 so this will be called with the exact workorder type
|
|
//so if it's an exact type translate back to simply workorder
|
|
RootObjectTypes actualWorkorderType = tid.RootObjectType;
|
|
switch (tid.RootObjectType)
|
|
{
|
|
case RootObjectTypes.WorkorderService:
|
|
case RootObjectTypes.WorkorderServiceTemplate:
|
|
case RootObjectTypes.WorkorderQuote:
|
|
case RootObjectTypes.WorkorderQuoteTemplate:
|
|
case RootObjectTypes.WorkorderPreventiveMaintenance:
|
|
case RootObjectTypes.WorkorderPreventiveMaintenanceTemplate:
|
|
actualWorkorderType = tid.RootObjectType;
|
|
tid = new TypeAndID(RootObjectTypes.Workorder, tid.ID);
|
|
break;
|
|
}
|
|
|
|
WikiPage w = (WikiPage)DataPortal.Fetch(new Criteria(Guid.Empty, null, tid, actualWorkorderType));
|
|
w.mExactObjectType = actualWorkorderType;
|
|
if (w.IsNew)
|
|
w = (WikiPage)w.Save();
|
|
return w;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete WikiPage
|
|
/// </summary>
|
|
/// <param name="ID">WikiPage applicationID</param>
|
|
public static void DeleteItem(Guid ID)
|
|
{
|
|
DataPortal.Delete(new Criteria(ID, null, null, RootObjectTypes.Nothing));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used internally and called by DBUtil.RemoveDocs
|
|
/// which in turn is called by root objects when they are deleted
|
|
/// </summary>
|
|
/// <param name="ID"></param>
|
|
/// <param name="transaction"></param>
|
|
internal static void DeleteItem(Guid ID, IDbTransaction transaction)
|
|
{
|
|
DataPortal.Delete(new Criteria(ID, transaction, null, RootObjectTypes.Nothing));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if given object Id has an associated wikipage or not
|
|
/// </summary>
|
|
public static bool HasWiki(Guid objectId)
|
|
{
|
|
return WikiPageExistanceChecker.WikiPageExists(objectId);
|
|
}
|
|
|
|
|
|
//case 3160
|
|
/// <summary>
|
|
/// Copy existing wiki page to destination type and id specified
|
|
/// If destination already has a wiki page or source id is not found nothing will happen and it will return false
|
|
/// NOTE: this is not a public method because it could break so many things if misused
|
|
/// </summary>
|
|
/// <param name="SourceTypeAndId">Source object with wiki page to be copied</param>
|
|
/// <param name="DestTypeAndId">Type and Id of destination object to receive wikipage</param>
|
|
/// <returns>false on any fail, true on success</returns>
|
|
internal static bool DuplicateItem(TypeAndID SourceTypeAndId, TypeAndID DestTypeAndId)
|
|
{
|
|
//Not valid to copy to
|
|
if (DestTypeAndId.RootObjectType == RootObjectTypes.WikiPage)
|
|
{
|
|
throw new System.NotSupportedException("Wikipage is not a valid destination object type for duplicate item");
|
|
}
|
|
|
|
if (HasWiki(DestTypeAndId.ID))
|
|
{
|
|
throw new System.NotSupportedException("Destination already has a Wikipage");
|
|
}
|
|
|
|
WikiPage src = WikiPage.GetItem(SourceTypeAndId);
|
|
|
|
if (src.IsNew)
|
|
{
|
|
throw new System.NotSupportedException("Source Wikipage specified does not exist");
|
|
}
|
|
|
|
//case 1584
|
|
//all wikipages for the different workorder types are simply stored with object type "workorder"
|
|
//but that object type isn't used anymore post case 1387 so this will be called with the exact workorder type
|
|
//so if it's an exact type translate back to simply workorder
|
|
RootObjectTypes actualWorkorderType = DestTypeAndId.RootObjectType;
|
|
switch (DestTypeAndId.RootObjectType)
|
|
{
|
|
case RootObjectTypes.WorkorderService:
|
|
case RootObjectTypes.WorkorderServiceTemplate:
|
|
case RootObjectTypes.WorkorderQuote:
|
|
case RootObjectTypes.WorkorderQuoteTemplate:
|
|
case RootObjectTypes.WorkorderPreventiveMaintenance:
|
|
case RootObjectTypes.WorkorderPreventiveMaintenanceTemplate:
|
|
actualWorkorderType = DestTypeAndId.RootObjectType;
|
|
DestTypeAndId = new TypeAndID(RootObjectTypes.Workorder, DestTypeAndId.ID);
|
|
break;
|
|
}
|
|
|
|
WikiPage w = (WikiPage)DataPortal.Fetch(new Criteria(Guid.Empty, null, DestTypeAndId, actualWorkorderType));
|
|
w.mExactObjectType = actualWorkorderType;
|
|
|
|
|
|
//copy fields
|
|
//through property to trigger dirty-ness
|
|
w.Title = src.Title;
|
|
w.mContent = src.mContent;
|
|
|
|
//copy files
|
|
AyaFileList fl = AyaFileList.GetList(src.ID);
|
|
if (!(fl.Count == 0))
|
|
{
|
|
foreach (AyaFileList.AyaFileListInfo i in fl)
|
|
{
|
|
if (i.FileType != AyaFileType.EmbeddedWikiImage)
|
|
{
|
|
//copy file over to new wikipage
|
|
AyaFile afsrc=AyaFile.GetItem(i.LT_O_AyaFile.Value);
|
|
AyaFile af = AyaFile.NewItem();
|
|
af.RootObjectID = w.ID;
|
|
af.RootObjectType = RootObjectTypes.WikiPage;
|
|
af.Name = afsrc.Name;
|
|
af.FileType = AyaFileType.WikiFile;
|
|
af.SetContent(afsrc.GetContent());
|
|
af.Save();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (w.IsNew)
|
|
w = (WikiPage)w.Save();
|
|
return true;
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Utils
|
|
/// <summary>
|
|
/// Add a file to wikipage
|
|
/// </summary>
|
|
/// <param name="sPath">Full path to file (any format acceptible to File.ReadAllBytes)</param>
|
|
public void AddFile(string sPath)
|
|
{
|
|
if (File.Exists(sPath))
|
|
{
|
|
AyaFile af = AyaFile.NewItem();
|
|
af.RootObjectID = this.ID;
|
|
af.RootObjectType = RootObjectTypes.WikiPage;
|
|
af.Name = Path.GetFileName(sPath);
|
|
af.FileType = AyaFileType.WikiFile;
|
|
af.SetContent(File.ReadAllBytes(sPath));
|
|
af.Save();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region DAL DATA ACCESS
|
|
|
|
#region Fetch
|
|
///
|
|
/// <param name="Criteria"></param>
|
|
protected override void DataPortal_Fetch(object Criteria)
|
|
{
|
|
|
|
|
|
Criteria crit = (Criteria)Criteria;
|
|
SafeDataReader dr = null;
|
|
|
|
try
|
|
{
|
|
|
|
if (crit.TID != null)
|
|
{
|
|
dr = DBUtil.GetReaderFromSQLString("SELECT * FROM aWikiPage WHERE aRootObjectID=@ID;", crit.TID.ID);
|
|
if (!dr.Read())
|
|
{
|
|
if (dr != null) dr.Close();
|
|
|
|
//Ok, there is no existing wikipage for this object
|
|
//So create a new empty one and return it
|
|
mRootObjectID = crit.TID.ID;
|
|
mRootObjectType = crit.TID.RootObjectType;
|
|
|
|
//case 1632
|
|
string defaultText = "";
|
|
if (crit.ActualObjectType != RootObjectTypes.Nothing)
|
|
defaultText = LocalizedTextTable.GetLocalizedTextDirect("O.WikiPage") + " - " + NameFetcher.GetItem(new TypeAndID(crit.ActualObjectType, crit.TID.ID)).RecordName;
|
|
else
|
|
defaultText = LocalizedTextTable.GetLocalizedTextDirect("O.WikiPage") + " - " + NameFetcher.GetItem(crit.TID).RecordName;
|
|
|
|
this.SetContent(defaultText);
|
|
|
|
bReadOnly = AyaBizUtils.Right("Object.WikiPage") < (int)SecurityLevelTypes.ReadWrite;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dr = DBUtil.GetReaderFromSQLString("SELECT * FROM aWikiPage WHERE aID=@ID;", crit.ID);
|
|
if (!dr.Read())
|
|
DBUtil.ThrowFetchError("WikiPage ID: " + crit.ID.ToString());
|
|
}
|
|
//Standard fields
|
|
mID = dr.GetGuid("aID");
|
|
mCreated = DBUtil.ToLocal(dr.GetSmartDate("aCreated"));
|
|
mModified = DBUtil.ToLocal(dr.GetSmartDate("aModified"));
|
|
mCreator = dr.GetGuid("aCreator");
|
|
mModifier = dr.GetGuid("aModifier");
|
|
//WikiPage fields
|
|
mInternalOnly = dr.GetBoolean("AINTERNALONLY");
|
|
Title = dr.GetString("aTitle");
|
|
RootObjectID = dr.GetGuid("aRootObjectID");
|
|
RootObjectType = (RootObjectTypes)dr.GetInt16("aRootObjectType");
|
|
|
|
//Get the WObject
|
|
//Get the data's size
|
|
int nSize = dr.GetInt32("aWObjectSize");
|
|
|
|
//allocate a place to store it
|
|
mContent = new Byte[nSize];
|
|
|
|
//retrieve the (compressed) bytes
|
|
dr.GetBytes("aWObject", 0, mContent, 0, mContent.Length);
|
|
|
|
if (dr != null) dr.Close();
|
|
|
|
|
|
|
|
|
|
}
|
|
finally
|
|
{
|
|
if (dr != null) dr.Close();
|
|
}
|
|
|
|
|
|
MarkOld();
|
|
|
|
//Get access rights level
|
|
bReadOnly = AyaBizUtils.Right("Object.WikiPage") < (int)SecurityLevelTypes.ReadWrite;
|
|
|
|
}
|
|
|
|
#endregion fetch
|
|
|
|
#region Update
|
|
|
|
/// <summary>
|
|
/// Called by DataPortal to delete/add/update data into the database
|
|
/// </summary>
|
|
protected override void DataPortal_Update()
|
|
{
|
|
// If not a new record, check if record was modified
|
|
//by another user since original retrieval:
|
|
if (!IsNew)
|
|
DBUtil.CheckSafeToUpdate(this.mModified.Date, this.mID, "aWikiPage");
|
|
|
|
#region Delete
|
|
if (IsDeleted)
|
|
{
|
|
throw new System.NotSupportedException("WikiPage->Update->Delete: not supported for this object, call the static/shared WikiPage.DeleteItem() method instead");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Add / Update
|
|
|
|
//get modification time temporarily, if update succeeds then
|
|
//set to this time
|
|
System.DateTime dtModified = DBUtil.CurrentWorkingDateTime;
|
|
DBCommandWrapper cm = null;
|
|
if (IsNew)//Add or update?
|
|
cm = DBUtil.GetCommandFromSQL(
|
|
"INSERT INTO aWikiPage (aID, aInternalOnly, aCreated,aModified,aCreator,aModifier, " +
|
|
"aRootObjectID, aRootObjectType, aTitle,aWObject,aWObjectSize) " +
|
|
"VALUES (@ID,@InternalOnly,@Created,@Modified,@CurrentUserID,@CurrentUserID, " +
|
|
"@RootObjectID,@RootObjectType, @Title,@WObject,@WObjectSize)"
|
|
);
|
|
else
|
|
cm = DBUtil.GetCommandFromSQL(
|
|
"UPDATE aWikiPage SET aID=@ID, " +
|
|
"aInternalOnly=@InternalOnly,aModified=@Modified,aModifier=@CurrentUserID, " +
|
|
"aRootObjectID=@RootObjectID,aRootObjectType=@RootObjectType,aTitle=@Title, " +
|
|
"aWObject=@WObject,aWObjectSize=@WObjectSize " +
|
|
"WHERE aID=@ID"
|
|
);
|
|
|
|
|
|
//WikiPage fields
|
|
cm.AddInParameter("@ID", DbType.Guid, mID);
|
|
cm.AddInParameter("@InternalOnly", DbType.Boolean, mInternalOnly);
|
|
cm.AddInParameter("@Title", DbType.String, mTitle);
|
|
cm.AddInParameter("@RootObjectID", DbType.Guid, mRootObjectID);
|
|
cm.AddInParameter("@RootObjectType", DbType.Int16, (int)mRootObjectType);
|
|
//Standard fields
|
|
cm.AddInParameter("@CurrentUserID", DbType.Guid, CurrentUserID);
|
|
cm.AddInParameter("@Created", DbType.DateTime, DBUtil.ToUTC(mCreated).DBValue);
|
|
cm.AddInParameter("@Modified", DbType.DateTime, DBUtil.ToUTC(dtModified));
|
|
|
|
cm.AddInParameter("@WObject", DbType.Object, mContent);
|
|
cm.AddInParameter("@WObjectSize", DbType.Int32, mContent.GetLength(0));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using (IDbConnection connection = DBUtil.DB.GetConnection())
|
|
{
|
|
connection.Open();
|
|
IDbTransaction transaction = connection.BeginTransaction();
|
|
|
|
try
|
|
{
|
|
|
|
DBUtil.DB.ExecuteNonQuery(cm, transaction);
|
|
|
|
//Process keywords
|
|
DBUtil.ProcessKeywords(transaction, this.mID, RootObjectTypes.WikiPage, IsNew, AyaBizUtils.Break(false, GetContentAsPlainText));
|
|
|
|
|
|
MarkOld();//db is now synched with object
|
|
|
|
// Commit the transaction
|
|
transaction.Commit();
|
|
|
|
}
|
|
catch
|
|
{
|
|
|
|
// Rollback transaction
|
|
transaction.Rollback();
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
connection.Close();
|
|
}
|
|
//Successful update so
|
|
//change modification time to match
|
|
this.mModified.Date = dtModified;
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
#endregion update
|
|
|
|
#region Delete
|
|
|
|
/// <summary>
|
|
/// Remove a WikiPage record, this is recursive in order to get the whole tree of potential pages .
|
|
/// </summary>
|
|
/// <param name="Criteria"></param>
|
|
protected override void DataPortal_Delete(object Criteria)
|
|
{
|
|
Criteria crit = (Criteria)Criteria;
|
|
|
|
//Get a list of all child pages that link to this page
|
|
List<Guid> l = new List<Guid>();
|
|
SafeDataReader dr = DBUtil.GetReaderFromSQLString(
|
|
"SELECT aID " +
|
|
"FROM aWikiPage WHERE aRootObjectID=@ID", crit.ID);
|
|
while (dr.Read())
|
|
{
|
|
l.Add(dr.GetGuid("aID"));
|
|
}
|
|
dr.Close();
|
|
|
|
|
|
|
|
|
|
//Delete object and child objects
|
|
DBCommandWrapper cmDelete = DBUtil.GetCommandFromSQL("DELETE FROM aWikiPage WHERE aID = @ID;");
|
|
cmDelete.AddInParameter("@ID", DbType.Guid, crit.ID);
|
|
|
|
DBCommandWrapper cmDeleteFiles = DBUtil.GetCommandFromSQL("DELETE FROM aFile WHERE aRootObjectID = @ID;");
|
|
cmDeleteFiles.AddInParameter("@ID", DbType.Guid, crit.ID);
|
|
|
|
|
|
using (IDbConnection connection = DBUtil.DB.GetConnection())
|
|
{
|
|
connection.Open();
|
|
IDbTransaction transaction = null;
|
|
if (crit.transaction != null)
|
|
transaction = crit.transaction;
|
|
else
|
|
transaction = connection.BeginTransaction();
|
|
|
|
try
|
|
{
|
|
//Call delete on children first so that the deletion process goes from bottom of tree upwards
|
|
//and if there is a failure at any point the topmost page is still present for the user to attempt a
|
|
//delete on again or whatever. No orphaned records this way
|
|
foreach (Guid g in l)
|
|
{
|
|
WikiPage.DeleteItem(g, transaction);
|
|
}
|
|
|
|
DBUtil.DB.ExecuteNonQuery(cmDeleteFiles, transaction);
|
|
DBUtil.DB.ExecuteNonQuery(cmDelete, transaction);
|
|
DBUtil.RemoveKeywords(transaction, RootObjectTypes.WikiPage, crit.ID);
|
|
|
|
|
|
// Commit the transaction if it was started here
|
|
if (crit.transaction == null)
|
|
transaction.Commit();
|
|
|
|
connection.Close();
|
|
//Successful deletion, now find all pages that have rootobjectid set to this pages ID
|
|
//and call delete on them hence recursively deleting the entire tree
|
|
|
|
}
|
|
catch
|
|
{
|
|
// Rollback transaction
|
|
transaction.Rollback();
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
connection.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion delete
|
|
|
|
#endregion
|
|
|
|
#region Override IsValid / IsDirty
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public override bool IsValid
|
|
{
|
|
get
|
|
{
|
|
return base.IsValid;
|
|
}
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public override bool IsDirty
|
|
{
|
|
get
|
|
{
|
|
return base.IsDirty;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region criteria
|
|
/// <summary>
|
|
/// Criteria for identifying existing object
|
|
/// </summary>
|
|
[Serializable]
|
|
private class Criteria
|
|
{
|
|
public Guid ID;
|
|
public IDbTransaction transaction;//used for delete when called from root object being deleted in DBUtil.RemoveDocs
|
|
public TypeAndID TID;
|
|
public RootObjectTypes ActualObjectType;
|
|
public Criteria(Guid _ID, IDbTransaction _transaction, TypeAndID _TID, RootObjectTypes _actualObjectType)
|
|
{
|
|
ID = _ID;
|
|
transaction = _transaction;
|
|
TID = _TID;
|
|
ActualObjectType = _actualObjectType;
|
|
}
|
|
|
|
}
|
|
#endregion
|
|
|
|
}//end WikiPage
|
|
|
|
}//end namespace GZTW.AyaNova.BLL
|