Board ReadMarkers implemented; fixes in Thread ReadMarkers

main
Inga 🏳‍🌈 15 years ago
parent a314b252cf
commit 406cdf2076
  1. 2
      Builder/IISMainHandler/build.txt
  2. 2
      Builder/IISUploadHandler/build.txt
  3. 12
      Common/SqlObject.cs
  4. 7
      Common/UserContext.cs
  5. 1
      Common/actions/ChangeSet.cs
  6. 91
      Common/dataobjects/Board.cs
  7. 27
      Common/dataobjects/Thread.cs
  8. 9
      IISMainHandler/WebContext.cs
  9. 13
      IISMainHandler/handlers/BoardHandler.cs
  10. 17
      IISMainHandler/handlers/PostHandler.cs
  11. 21
      IISMainHandler/handlers/ThreadHandler.cs
  12. 2
      templates/Full/elems/Main.xslt

@ -99,7 +99,7 @@ namespace FLocal.Common {
}
abstract public class SqlObject<T> : SqlObject<int, T> where T : SqlObject<T>, new() {
abstract public class SqlObject<T> : SqlObject<int, T>, IComparable<T> where T : SqlObject<T>, new() {
public static List<T> LoadByIds(IEnumerable<int> ids) {
@ -135,6 +135,16 @@ namespace FLocal.Common {
return res;
}
int IComparable<T>.CompareTo(T other) {
if(other.id > this.id) {
return -1;
} else if(other.id == this.id) {
return 0;
} else {
return 1;
}
}
}
}

@ -25,6 +25,13 @@ namespace FLocal.Common {
abstract public XElement formatTotalPosts(long posts);
/// <summary>
/// May be null
/// </summary>
abstract public dataobjects.Account account {
get;
}
}
public static class UserContext_Extensions {

@ -24,6 +24,7 @@ namespace FLocal.Common.actions {
dataobjects.Board.TableSpec.TABLE,
dataobjects.Thread.TableSpec.TABLE,
dataobjects.Post.TableSpec.TABLE,
dataobjects.Board.ReadMarkerTableSpec.TABLE,
dataobjects.Thread.ReadMarkerTableSpec.TABLE,
dataobjects.Session.TableSpec.TABLE,
}

@ -6,6 +6,7 @@ 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 Board : SqlObject<Board> {
@ -27,6 +28,18 @@ namespace FLocal.Common.dataobjects {
public void refreshSqlObject(int id) { Refresh(id); }
}
public class ReadMarkerTableSpec : ISqlObjectTableSpec {
public const string TABLE = "Boards_ReadMarkers";
public const string FIELD_ID = "Id";
public const string FIELD_BOARDID = "BoardId";
public const string FIELD_ACCOUNTID = "AccountId";
public const string FIELD_LASTREADDATE = "LastReadDate";
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 _sortOrder;
@ -149,8 +162,12 @@ namespace FLocal.Common.dataobjects {
Cache<IEnumerable<int>>.instance.delete(this.subBoards_Locker);
}
private bool hasNewPosts() {
return Core.Util.RandomInt(0, 1000) < 500;
public bool hasNewPosts(Account account) {
if(!this.lastPostId.HasValue) {
return false;
} else {
return this.getLastReadDate(account) < this.lastPost.postDate;
}
}
private XElement exportLastPostInfo(UserContext context) {
@ -170,7 +187,7 @@ namespace FLocal.Common.dataobjects {
);
}
public XElement exportToXml(UserContext context, bool includeSubBoards) {
public XElement exportToXml(UserContext context, bool includeSubBoards, params XElement[] additional) {
XElement result = new XElement("board",
new XElement("id", this.id),
new XElement("sortOrder", this.sortOrder),
@ -179,16 +196,23 @@ namespace FLocal.Common.dataobjects {
new XElement("totalThreads", this.totalThreads),
new XElement("name", this.name),
new XElement("description", this.description),
new XElement("hasNewPosts", this.hasNewPosts().ToPlainString()),
new XElement("lastPostInfo", this.exportLastPostInfo(context))
);
if(context.account != null) {
result.Add(new XElement("hasNewPosts", this.hasNewPosts(context.account).ToPlainString()));
}
if(includeSubBoards) {
result.Add(new XElement("subBoards",
from board in this.subBoards select board.exportToXml(context, false)
));
}
if(additional.Length > 0) {
result.Add(additional);
}
return result;
}
@ -225,5 +249,64 @@ namespace FLocal.Common.dataobjects {
);
}
public DateTime getLastReadDate(Account account) {
List<string> stringIds = Config.instance.mainConnection.LoadIdsByConditions(
ReadMarkerTableSpec.instance,
new ComplexCondition(
ConditionsJoinType.AND,
new ComparisonCondition(
ReadMarkerTableSpec.instance.getColumnSpec(ReadMarkerTableSpec.FIELD_BOARDID),
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 new DateTime(0);
}
Dictionary<string, string> data = Config.instance.mainConnection.LoadById(ReadMarkerTableSpec.instance, stringIds[0]);
return Util.ParseDateTimeFromTimestamp(data[ReadMarkerTableSpec.FIELD_LASTREADDATE]).Value;
}
public void markAsRead(Account account) {
if(this.lastPostId.HasValue) {
ChangeSetUtil.ApplyChanges(
new InsertOrUpdateChange(
ReadMarkerTableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ ReadMarkerTableSpec.FIELD_BOARDID, new ScalarFieldValue(this.id.ToString()) },
{ ReadMarkerTableSpec.FIELD_ACCOUNTID, new ScalarFieldValue(account.id.ToString()) },
{ ReadMarkerTableSpec.FIELD_LASTREADDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
},
new Dictionary<string,AbstractFieldValue> {
{ ReadMarkerTableSpec.FIELD_LASTREADDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
},
new ComplexCondition(
ConditionsJoinType.AND,
new ComparisonCondition(
ReadMarkerTableSpec.instance.getColumnSpec(ReadMarkerTableSpec.FIELD_BOARDID),
ComparisonType.EQUAL,
this.id.ToString()
),
new ComparisonCondition(
ReadMarkerTableSpec.instance.getColumnSpec(ReadMarkerTableSpec.FIELD_ACCOUNTID),
ComparisonType.EQUAL,
account.id.ToString()
)
)
)
);
}
}
}
}

@ -152,10 +152,6 @@ namespace FLocal.Common.dataobjects {
this._totalViews = int.Parse(data[TableSpec.FIELD_TOTALVIEWS]);
}
private bool hasNewPosts() {
return Core.Util.RandomInt(0, 1000) < 500;
}
public XElement exportToXmlSimpleWithParent(UserContext context) {
return new XElement("thread",
new XElement("id", this.id),
@ -182,6 +178,9 @@ namespace FLocal.Common.dataobjects {
if(includeFirstPost) {
result.Add(new XElement("firstPost", this.firstPost.exportToXmlWithoutThread(context, false)));
}
if(context.account != null) {
result.Add(new XElement("afterLastRead", this.getLastReadId(context.account) + 1));
}
if(additional.Length > 0) {
result.Add(additional);
}
@ -224,7 +223,7 @@ namespace FLocal.Common.dataobjects {
});
}
private Post getLastRead(Account account) {
public int getLastReadId(Account account) {
List<string> stringIds = Config.instance.mainConnection.LoadIdsByConditions(
ReadMarkerTableSpec.instance,
new ComplexCondition(
@ -246,24 +245,13 @@ namespace FLocal.Common.dataobjects {
throw new CriticalException("more than one row");
}
if(stringIds.Count < 1) {
return null;
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 null;
}
return Post.LoadById(int.Parse(data[ReadMarkerTableSpec.FIELD_POSTID]));
}
public int getLastReadId(Session session) {
if(session == null) {
return 0;
}
Post post = this.getLastRead(session.account);
if(post == null) {
return 0;
}
return post.id;
return int.Parse(data[ReadMarkerTableSpec.FIELD_POSTID]);
}
public void markAsRead(Account account, Post minPost, Post maxPost) {
@ -282,7 +270,7 @@ namespace FLocal.Common.dataobjects {
{
ReadMarkerTableSpec.FIELD_POSTID,
new ScalarFieldValue(
(minPost.id < this.firstPostId)
(minPost.id <= this.firstPostId)
?
maxPost.id.ToString()
:
@ -330,7 +318,6 @@ namespace FLocal.Common.dataobjects {
}
)
}
},
new ComplexCondition(
ConditionsJoinType.AND,

@ -62,6 +62,15 @@ namespace FLocal.IISHandler {
public Session session;
public override Account account {
get {
if(this.session == null) {
return null;
}
return this.session.account;
}
}
public WebContext(HttpContext httpcontext) {
this.httpcontext = httpcontext;
this.requestTime = DateTime.Now;

@ -6,6 +6,7 @@ using System.Web;
using System.Xml.Linq;
using FLocal.Common;
using FLocal.Common.dataobjects;
using FLocal.Core;
using FLocal.Core.DB;
namespace FLocal.IISHandler.handlers {
@ -22,14 +23,22 @@ namespace FLocal.IISHandler.handlers {
Board board = Board.LoadById(int.Parse(context.requestParts[1]));
PageOuter pageOuter = PageOuter.createFromGet(context.requestParts, context.userSettings.threadsPerPage, 2);
IEnumerable<Thread> threads = board.getThreads(pageOuter, context);
return new XElement[] {
XElement[] result = new XElement[] {
new XElement("currentLocation", board.exportToXmlSimpleWithParent(context)),
new XElement("boards", from subBoard in board.subBoards select subBoard.exportToXml(context, true)),
new XElement("threads",
(from thread in threads select thread.exportToXml(context, false, new XElement("afterLastRead", thread.getLastReadId(context.session) + 1))).addNumbers(),
(from thread in threads select thread.exportToXml(context, false)).addNumbers(),
pageOuter.exportToXml(1, 5, 1)
)
};
if(context.session != null) {
if(pageOuter.start == 0) {
board.markAsRead(context.session.account);
}
}
return result;
}
}

@ -21,18 +21,23 @@ namespace FLocal.IISHandler.handlers {
override protected XElement[] getSpecificData(WebContext context) {
Post post = Post.LoadById(int.Parse(context.requestParts[1]));
int lastReadId = post.thread.getLastReadId(context.session);
post.thread.incrementViewsCounter();
int lastReadId = 0;
if(context.session != null) {
post.thread.markAsRead(context.session.account, post, post);
lastReadId = post.thread.getLastReadId(context.session.account);
}
return new XElement[] {
XElement[] result = new XElement[] {
new XElement("currentLocation", post.exportToXmlSimpleWithParent(context)),
new XElement("posts", post.exportToXmlWithoutThread(context, true, new XElement("isUnread", (post.id > lastReadId).ToPlainString())))
};
}
post.thread.incrementViewsCounter();
if(context.session != null) {
post.thread.markAsRead(context.session.account, post, post);
}
return result;
}
}

@ -51,24 +51,25 @@ namespace FLocal.IISHandler.handlers {
);
IEnumerable<Post> posts = thread.getPosts(pageOuter, context);
int lastReadId = thread.getLastReadId(context.session);
thread.incrementViewsCounter();
if((context.session != null) && (posts.Count() > 0)) {
thread.markAsRead(
context.session.account,
(from post in posts orderby post.id ascending select post).First(),
(from post in posts orderby post.id descending select post).First()
);
int lastReadId = 0;
if(context.session != null) {
lastReadId = thread.getLastReadId(context.session.account);
}
return new XElement[] {
XElement[] result = new XElement[] {
new XElement("currentLocation", thread.exportToXmlSimpleWithParent(context)),
new XElement("posts",
from post in posts select post.exportToXmlWithoutThread(context, true, new XElement("isUnread", (post.id > lastReadId).ToPlainString())),
pageOuter.exportToXml(2, 5, 2)
)
};
thread.incrementViewsCounter();
if((context.session != null) && (posts.Count() > 0)) {
thread.markAsRead(context.session.account, posts.Min(), posts.Max());
}
return result;
}
}

@ -13,9 +13,11 @@
<body>
<xsl:call-template name="header"/>
<xsl:call-template name="specific"/>
<xsl:text disable-output-escaping="yes"><![CDATA[<!--]]></xsl:text>
<br />
<xsl:text>Data used for authoring this XHTML document:</xsl:text>
<xmp><xsl:copy-of select="/"/></xmp>
<xsl:text disable-output-escaping="yes"><![CDATA[-->]]></xsl:text>
</body>
</html>
</xsl:template>

Loading…
Cancel
Save