An alternative to UBB.threads
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

469 lines
15 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using FLocal.Core;
using FLocal.Core.DB;
using FLocal.Core.DB.conditions;
using FLocal.Common.actions;
namespace FLocal.Common.dataobjects {
public class Thread : SqlObject<Thread> {
public class TableSpec : ISqlObjectTableSpec {
public const string TABLE = "Threads";
public const string FIELD_ID = "Id";
public const string FIELD_BOARDID = "BoardId";
public const string FIELD_FIRSTPOSTID = "FirstPostId";
public const string FIELD_TOPICSTARTERID = "TopicstarterId";
public const string FIELD_TITLE = "Title";
public const string FIELD_LASTPOSTID = "LastPostId";
public const string FIELD_LASTPOSTDATE = "LastPostDate";
public const string FIELD_ISANNOUNCEMENT = "IsAnnouncement";
public const string FIELD_ISLOCKED = "IsLocked";
public const string FIELD_TOTALPOSTS = "TotalPosts";
public const string FIELD_TOTALVIEWS = "TotalViews";
public static readonly TableSpec instance = new TableSpec();
public string name { get { return TABLE; } }
public string idName { get { return FIELD_ID; } }
public void refreshSqlObject(int id) { Refresh(id); }
}
public class ReadMarkerTableSpec : ISqlObjectTableSpec {
public const string TABLE = "Threads_ReadMarkers";
public const string FIELD_ID = "Id";
public const string FIELD_THREADID = "ThreadId";
public const string FIELD_ACCOUNTID = "AccountId";
public const string FIELD_POSTID = "PostId";
public static readonly ReadMarkerTableSpec instance = new ReadMarkerTableSpec();
public string name { get { return TABLE; } }
public string idName { get { return FIELD_ID; } }
public void refreshSqlObject(int id) { }
}
protected override ISqlObjectTableSpec table { get { return TableSpec.instance; } }
private int _boardId;
public int boardId {
get {
this.LoadIfNotLoaded();
return this._boardId;
}
}
public Board board {
get {
return Board.LoadById(this.boardId);
}
}
private int _firstPostId;
public int firstPostId {
get {
this.LoadIfNotLoaded();
return this._firstPostId;
}
}
public Post firstPost {
get {
return Post.LoadById(this.firstPostId);
}
}
private int _topicstarterId;
public int topicstarterId {
get {
this.LoadIfNotLoaded();
return this._topicstarterId;
}
}
public User topicstarter {
get {
return User.LoadById(this.topicstarterId);
}
}
private string _title;
public string title {
get {
this.LoadIfNotLoaded();
return this._title;
}
}
private int _lastPostId;
public int lastPostId {
get {
this.LoadIfNotLoaded();
return this._lastPostId;
}
}
private DateTime _lastPostDate;
public DateTime lastPostDate {
get {
this.LoadIfNotLoaded();
return this._lastPostDate;
}
}
private bool _isAnnouncement;
public bool isAnnouncement {
get {
this.LoadIfNotLoaded();
return this._isAnnouncement;
}
}
private bool _isLocked;
public bool isLocked {
get {
this.LoadIfNotLoaded();
return this._isAnnouncement;
}
}
private int _totalPosts;
public int totalPosts {
get {
this.LoadIfNotLoaded();
return this._totalPosts;
}
}
private int _totalViews;
public int totalViews {
get {
this.LoadIfNotLoaded();
return this._totalViews;
}
}
protected override void doFromHash(Dictionary<string, string> data) {
this._boardId = int.Parse(data[TableSpec.FIELD_BOARDID]);
this._firstPostId = int.Parse(data[TableSpec.FIELD_FIRSTPOSTID]);
this._topicstarterId = int.Parse(data[TableSpec.FIELD_TOPICSTARTERID]);
this._title = data[TableSpec.FIELD_TITLE];
this._lastPostId = int.Parse(data[TableSpec.FIELD_LASTPOSTID]);
this._lastPostDate = new DateTime(long.Parse(data[TableSpec.FIELD_LASTPOSTDATE]));
this._isAnnouncement = FLocal.Core.Util.string2bool(data[TableSpec.FIELD_ISANNOUNCEMENT]);
this._isLocked = FLocal.Core.Util.string2bool(data[TableSpec.FIELD_ISLOCKED]);
this._totalPosts = int.Parse(data[TableSpec.FIELD_TOTALPOSTS]);
this._totalViews = int.Parse(data[TableSpec.FIELD_TOTALVIEWS]);
}
public XElement exportToXmlSimpleWithParent(UserContext context) {
return new XElement("thread",
new XElement("id", this.id),
new XElement("name", this.title),
new XElement("parent", this.board.exportToXmlSimpleWithParent(context))
);
}
public XElement exportToXml(UserContext context, bool includeFirstPost, params XElement[] additional) {
XElement result = new XElement("thread",
new XElement("id", this.id),
new XElement("firstPostId", this.firstPostId),
new XElement("topicstarter", this.topicstarter.exportToXmlForViewing(context)),
new XElement("title", this.title),
new XElement("lastPostId", this.lastPostId),
new XElement("lastPostDate", this.lastPostDate.ToXml()),
new XElement("isAnnouncement", this.isAnnouncement),
new XElement("isLocked", this.isLocked),
new XElement("totalPosts", this.totalPosts),
new XElement("totalViews", this.totalViews),
new XElement("bodyShort", this.firstPost.bodyShort),
new XElement("layerId", this.firstPost.layerId),
new XElement("layerName", this.firstPost.layer.name),
context.formatTotalPosts(this.totalPosts)
);
if(includeFirstPost) {
result.Add(new XElement("firstPost", this.firstPost.exportToXmlWithoutThread(context, false)));
}
if(context.account != null) {
int lastReadId = this.getLastReadId(context.account);
result.Add(
new XElement("afterLastRead", lastReadId + 1),
new XElement(
"totalNewPosts",
Config.instance.mainConnection.GetCountByConditions(
Post.TableSpec.instance,
new ComplexCondition(
ConditionsJoinType.AND,
new ComparisonCondition(
Post.TableSpec.instance.getColumnSpec(Post.TableSpec.FIELD_THREADID),
ComparisonType.EQUAL,
this.id.ToString()
),
new ComparisonCondition(
Post.TableSpec.instance.getIdSpec(),
ComparisonType.GREATERTHAN,
lastReadId.ToString()
)
)
)
)
);
}
if(additional.Length > 0) {
result.Add(additional);
}
return result;
}
public IEnumerable<Post> getPosts(Diapasone diapasone, UserContext context) {
return Post.LoadByIds(
from stringId in Config.instance.mainConnection.LoadIdsByConditions(
Post.TableSpec.instance,
new ComparisonCondition(
Post.TableSpec.instance.getColumnSpec(Post.TableSpec.FIELD_THREADID),
ComparisonType.EQUAL,
this.id.ToString()
),
diapasone,
new JoinSpec[0],
new SortSpec[] {
new SortSpec(
Post.TableSpec.instance.getIdSpec(),
true
),
}
) select int.Parse(stringId)
);
}
public void incrementViewsCounter() {
ChangeSetUtil.ApplyChanges(new AbstractChange[] {
new UpdateChange(
TableSpec.instance,
new Dictionary<string,AbstractFieldValue>() {
{
TableSpec.FIELD_TOTALVIEWS,
new IncrementFieldValue()
}
},
this.id
)
});
}
public int getLastReadId(Account account) {
List<string> stringIds = Config.instance.mainConnection.LoadIdsByConditions(
ReadMarkerTableSpec.instance,
new ComplexCondition(
ConditionsJoinType.AND,
new ComparisonCondition(
ReadMarkerTableSpec.instance.getColumnSpec(ReadMarkerTableSpec.FIELD_THREADID),
ComparisonType.EQUAL,
this.id.ToString()
),
new ComparisonCondition(
ReadMarkerTableSpec.instance.getColumnSpec(ReadMarkerTableSpec.FIELD_ACCOUNTID),
ComparisonType.EQUAL,
account.id.ToString()
)
),
Diapasone.unlimited
);
if(stringIds.Count > 1) {
throw new CriticalException("more than one row");
}
if(stringIds.Count < 1) {
return 0;
}
Dictionary<string, string> data = Config.instance.mainConnection.LoadById(ReadMarkerTableSpec.instance, stringIds[0]);
if((data[ReadMarkerTableSpec.FIELD_POSTID] == "") || (data[ReadMarkerTableSpec.FIELD_POSTID] == null)) {
return 0;
}
return int.Parse(data[ReadMarkerTableSpec.FIELD_POSTID]);
}
public void markAsRead(Account account, Post minPost, Post maxPost) {
ChangeSetUtil.ApplyChanges(new AbstractChange[] {
new InsertOrUpdateChange(
ReadMarkerTableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{
ReadMarkerTableSpec.FIELD_THREADID,
new ScalarFieldValue(this.id.ToString())
},
{
ReadMarkerTableSpec.FIELD_ACCOUNTID,
new ScalarFieldValue(account.id.ToString())
},
{
ReadMarkerTableSpec.FIELD_POSTID,
new ScalarFieldValue(
(minPost.id <= this.firstPostId)
?
maxPost.id.ToString()
:
null
) },
},
new Dictionary<string,AbstractFieldValue> {
{
ReadMarkerTableSpec.FIELD_POSTID,
new IncrementFieldValue(
s => {
if((s == null) || (s == "")) {
s = "0"; //workaround
}
if(maxPost.id < int.Parse(s)) {
return (s == "0") ? null : s; //if some newer posts were already read
}
long count = Config.instance.mainConnection.GetCountByConditions(
Post.TableSpec.instance,
new ComplexCondition(
ConditionsJoinType.AND,
new ComparisonCondition(
Post.TableSpec.instance.getColumnSpec(Post.TableSpec.FIELD_THREADID),
ComparisonType.EQUAL,
this.id.ToString()
),
new ComparisonCondition(
Post.TableSpec.instance.getIdSpec(),
ComparisonType.GREATERTHAN,
s
),
new ComparisonCondition(
Post.TableSpec.instance.getIdSpec(),
ComparisonType.LESSTHAN,
minPost.id.ToString()
)
)
);
if(count > 0) {
return (s == "0") ? null : s; //if there are some unread posts earlier than minPost
} else {
return maxPost.id.ToString();
}
}
)
}
},
new ComplexCondition(
ConditionsJoinType.AND,
new ComparisonCondition(
ReadMarkerTableSpec.instance.getColumnSpec(ReadMarkerTableSpec.FIELD_THREADID),
ComparisonType.EQUAL,
this.id.ToString()
),
new ComparisonCondition(
ReadMarkerTableSpec.instance.getColumnSpec(ReadMarkerTableSpec.FIELD_ACCOUNTID),
ComparisonType.EQUAL,
account.id.ToString()
)
)
)
});
}
internal static KeyValuePair<AbstractChange, IEnumerable<AbstractChange>> getNewPostChanges(Board board, int threadId, Post parentPost, User poster, PostLayer layer, string title, string body) {
string parentPostId = null;
if(parentPost != null) parentPostId = parentPost.id.ToString();
bool isNewThread = (parentPost == null);
AbstractChange postInsert = new InsertChange(
Post.TableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ Post.TableSpec.FIELD_THREADID, new ScalarFieldValue(threadId.ToString()) },
{ Post.TableSpec.FIELD_PARENTPOSTID, new ScalarFieldValue(parentPostId) },
{ Post.TableSpec.FIELD_POSTERID, new ScalarFieldValue(poster.id.ToString()) },
{ Post.TableSpec.FIELD_POSTDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ Post.TableSpec.FIELD_REVISION, new ScalarFieldValue("0") },
{ Post.TableSpec.FIELD_LASTCHANGEDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ Post.TableSpec.FIELD_LAYERID, new ScalarFieldValue(layer.id.ToString()) },
{ Post.TableSpec.FIELD_TITLE, new ScalarFieldValue(title) },
{ Post.TableSpec.FIELD_BODY, new ScalarFieldValue(UBBParser.UBBToIntermediate(body)) },
}
);
AbstractChange revisionInsert = new InsertChange(
Post.RevisionTableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ Post.RevisionTableSpec.FIELD_POSTID, new ReferenceFieldValue(postInsert) },
{ Post.RevisionTableSpec.FIELD_NUMBER, new ScalarFieldValue("0") },
{ Post.RevisionTableSpec.FIELD_CHANGEDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ Post.RevisionTableSpec.FIELD_TITLE, new ScalarFieldValue(title) },
{ Post.RevisionTableSpec.FIELD_BODY, new ScalarFieldValue(body) },
}
);
Dictionary<string, AbstractFieldValue> threadData = new Dictionary<string,AbstractFieldValue> {
{ Thread.TableSpec.FIELD_LASTPOSTDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ Thread.TableSpec.FIELD_TOTALPOSTS, new IncrementFieldValue() },
{
Thread.TableSpec.FIELD_LASTPOSTID,
new TwoWayReferenceFieldValue(
postInsert,
(oldStringId, newStringId) => {
if((oldStringId == null) || (oldStringId == "")) {
return newStringId;
}
int oldId = int.Parse(oldStringId);
int newId = int.Parse(newStringId);
return Math.Max(oldId, newId).ToString();
}
)
}
};
if(isNewThread) {
threadData[Thread.TableSpec.FIELD_FIRSTPOSTID] = new ReferenceFieldValue(postInsert);
}
AbstractChange threadUpdate = new UpdateChange(
Thread.TableSpec.instance,
threadData,
threadId
);
AbstractChange userUpdate = new UpdateChange(
User.TableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ User.TableSpec.FIELD_TOTALPOSTS, new IncrementFieldValue() },
},
poster.id
);
List<AbstractChange> changes = new List<AbstractChange> {
postInsert,
revisionInsert,
threadUpdate,
userUpdate,
};
Dictionary<string, AbstractFieldValue> boardData = new Dictionary<string,AbstractFieldValue> {
{ Board.TableSpec.FIELD_TOTALPOSTS, new IncrementFieldValue() },
{
Board.TableSpec.FIELD_LASTPOSTID,
new TwoWayReferenceFieldValue(
postInsert,
(oldStringId, newStringId) => {
if((oldStringId == null) || (oldStringId == "")) {
return newStringId;
}
int oldId = int.Parse(oldStringId);
int newId = int.Parse(newStringId);
return Math.Max(oldId, newId).ToString();
}
)
}
};
if(isNewThread) {
boardData[Board.TableSpec.FIELD_TOTALTHREADS] = new IncrementFieldValue();
}
int? boardId = board.id;
do {
Board _board = Board.LoadById(boardId.Value);
changes.Add(
new UpdateChange(
Board.TableSpec.instance,
boardData,
_board.id
)
);
boardId = _board.parentBoardId;
} while(boardId.HasValue);
return new KeyValuePair<AbstractChange,IEnumerable<AbstractChange>>(postInsert, changes);
}
}
}