NewThread implemented; some bugfixes

main
Inga 🏳‍🌈 14 years ago
parent b138249550
commit 16e750b946
  1. 2
      Builder/IISMainHandler/build.txt
  2. 2
      Builder/IISUploadHandler/build.txt
  3. 4
      Common/Common.csproj
  4. 8
      Common/actions/ChangeSet.cs
  5. 33
      Common/dataobjects/Board.cs
  6. 89
      Common/dataobjects/Post.cs
  7. 105
      Common/dataobjects/Thread.cs
  8. 20
      IISMainHandler/HandlersFactory.cs
  9. 1
      IISMainHandler/IISMainHandler.csproj
  10. 6
      IISMainHandler/handlers/request/AbstractNewMessageHandler.cs
  11. 23
      IISMainHandler/handlers/request/CreateThreadHandler.cs
  12. 6
      IISMainHandler/handlers/request/ReplyHandler.cs
  13. 30
      IISMainHandler/handlers/response/CreateThreadHandler.cs
  14. 1
      templates/Full/Board.xslt
  15. 3
      templates/Full/BoardAsThread.xslt
  16. 63
      templates/Full/NewThread.xslt
  17. 6
      templates/Full/PostReply.xslt

@ -24,9 +24,9 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Optimize>false</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>

@ -19,14 +19,14 @@ namespace FLocal.Common.actions {
return Cache<IEnumerable<string>>.instance.get(
tablesLockOrder_locker,
() => new List<string>() {
dataobjects.Account.TableSpec.TABLE,
dataobjects.User.TableSpec.TABLE,
dataobjects.Thread.TableSpec.TABLE, //thread should come first because of Board.newThread locking order with two changesets
dataobjects.Board.TableSpec.TABLE,
dataobjects.Thread.TableSpec.TABLE,
dataobjects.Post.TableSpec.TABLE,
dataobjects.Post.RevisionTableSpec.TABLE,
dataobjects.Board.ReadMarkerTableSpec.TABLE,
dataobjects.Account.TableSpec.TABLE,
dataobjects.User.TableSpec.TABLE,
dataobjects.Thread.ReadMarkerTableSpec.TABLE,
dataobjects.Board.ReadMarkerTableSpec.TABLE,
dataobjects.Session.TableSpec.TABLE,
}
);

@ -308,5 +308,38 @@ namespace FLocal.Common.dataobjects {
}
}
public Thread CreateThread(User poster, string title, string body, int desiredLayerId) {
int actualLayerId = Math.Max(poster.getMinAllowedLayer(this), desiredLayerId);
AbstractChange threadInsert = new InsertChange(
Thread.TableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ Thread.TableSpec.FIELD_BOARDID, new ScalarFieldValue(this.id.ToString()) },
{ Thread.TableSpec.FIELD_ISANNOUNCEMENT, new ScalarFieldValue("0") },
{ Thread.TableSpec.FIELD_ISLOCKED, new ScalarFieldValue("0") },
{ Thread.TableSpec.FIELD_TITLE, new ScalarFieldValue(title) },
{ Thread.TableSpec.FIELD_TOPICSTARTERID, new ScalarFieldValue(poster.id.ToString()) },
{ Thread.TableSpec.FIELD_TOTALPOSTS, new ScalarFieldValue("0") },
{ Thread.TableSpec.FIELD_TOTALVIEWS, new ScalarFieldValue("0") },
{ Thread.TableSpec.FIELD_FIRSTPOSTID, new ScalarFieldValue(null) },
{ Thread.TableSpec.FIELD_LASTPOSTID, new ScalarFieldValue(null) },
{ Thread.TableSpec.FIELD_LASTPOSTDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
}
);
using(ChangeSet threadInsertSet = new ChangeSet(), dataInsertSet = new ChangeSet()) {
Config.Transactional(transaction => {
threadInsertSet.Add(threadInsert);
threadInsertSet.Apply(transaction);
foreach(AbstractChange change in Thread.getNewPostChanges(this, threadInsert.getId().Value, null, poster, actualLayerId, title, body).Value) {
dataInsertSet.Add(change);
}
dataInsertSet.Apply(transaction);
});
}
return Thread.LoadById(threadInsert.getId().Value);
}
}
}

@ -108,7 +108,7 @@ namespace FLocal.Common.dataobjects {
}
public string bodyShort {
get {
return this.body.Replace("<br />", Util.EOL).Substring(0, Math.Min(1000, this.body.IndexOf("<")));
return this.body.Replace("<br />", Util.EOL).PHPSubstring(0, 1000);
}
}
@ -189,92 +189,11 @@ namespace FLocal.Common.dataobjects {
}
int actualLayerId = Math.Max(poster.getMinAllowedLayer(this.thread.board), desiredLayerId);
AbstractChange postInsert = new InsertChange(
TableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ TableSpec.FIELD_THREADID, new ScalarFieldValue(this.thread.id.ToString()) },
{ TableSpec.FIELD_PARENTPOSTID, new ScalarFieldValue(this.id.ToString()) },
{ TableSpec.FIELD_POSTERID, new ScalarFieldValue(poster.id.ToString()) },
{ TableSpec.FIELD_POSTDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ TableSpec.FIELD_REVISION, new ScalarFieldValue("0") },
{ TableSpec.FIELD_LASTCHANGEDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ TableSpec.FIELD_LAYERID, new ScalarFieldValue(layerId.ToString()) },
{ TableSpec.FIELD_TITLE, new ScalarFieldValue(title) },
{ TableSpec.FIELD_BODY, new ScalarFieldValue(UBBParser.UBBToIntermediate(body)) },
}
);
AbstractChange revisionInsert = new InsertChange(
RevisionTableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ RevisionTableSpec.FIELD_POSTID, new ReferenceFieldValue(postInsert) },
{ RevisionTableSpec.FIELD_NUMBER, new ScalarFieldValue("0") },
{ RevisionTableSpec.FIELD_CHANGEDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ RevisionTableSpec.FIELD_TITLE, new ScalarFieldValue(title) },
{ RevisionTableSpec.FIELD_BODY, new ScalarFieldValue(body) },
}
);
AbstractChange threadUpdate = new UpdateChange(
Thread.TableSpec.instance,
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) => {
int oldId = int.Parse(oldStringId);
int newId = int.Parse(newStringId);
return Math.Max(oldId, newId).ToString();
}
)
}
},
this.thread.id
);
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,
};
int? boardId = thread.boardId;
do {
Board board = Board.LoadById(boardId.Value);
changes.Add(
new UpdateChange(
Board.TableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ Board.TableSpec.FIELD_TOTALPOSTS, new IncrementFieldValue() },
{
Board.TableSpec.FIELD_LASTPOSTID,
new TwoWayReferenceFieldValue(
postInsert,
(oldStringId, newStringId) => {
int oldId = int.Parse(oldStringId);
int newId = int.Parse(newStringId);
return Math.Max(oldId, newId).ToString();
}
)
}
},
board.id
)
);
boardId = board.parentBoardId;
} while(boardId.HasValue);
ChangeSetUtil.ApplyChanges(changes.ToArray());
var changes = Thread.getNewPostChanges(this.thread.board, this.threadId, this, poster, actualLayerId, title, body);
ChangeSetUtil.ApplyChanges(changes.Value.ToArray());
return Post.LoadById(postInsert.getId().Value);
return Post.LoadById(changes.Key.getId().Value);
}
}

@ -357,6 +357,111 @@ namespace FLocal.Common.dataobjects {
});
}
internal static KeyValuePair<AbstractChange, IEnumerable<AbstractChange>> getNewPostChanges(Board board, int threadId, Post parentPost, User poster, int layerId, 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(layerId.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);
}
}
}

@ -25,7 +25,18 @@ namespace FLocal.IISHandler {
case "boards":
return new handlers.BoardsHandler();
case "board":
return new handlers.BoardHandler();
if(context.requestParts.Length < 2) {
return new handlers.WrongUrlHandler();
}
if(context.requestParts.Length == 2) {
return new handlers.BoardHandler();
}
switch(context.requestParts[2].ToLower()) {
case "newthread":
return new handlers.response.CreateThreadHandler();
default:
return new handlers.WrongUrlHandler();
}
case "boardasthread":
return new handlers.response.BoardAsThreadHandler();
case "thread":
@ -34,11 +45,14 @@ namespace FLocal.IISHandler {
if(context.requestParts.Length < 2) {
return new handlers.WrongUrlHandler();
}
if(context.requestParts.Length == 2) {
return new handlers.PostHandler();
}
switch(context.requestParts[2].ToLower()) {
case "reply":
return new handlers.response.ReplyHandler();
default:
return new handlers.PostHandler();
return new handlers.WrongUrlHandler();
}
case "login":
return new handlers.response.LoginHandler();
@ -77,6 +91,8 @@ namespace FLocal.IISHandler {
return new handlers.request.MigrateAccountHandler();
case "reply":
return new handlers.request.ReplyHandler();
case "newthread":
return new handlers.request.CreateThreadHandler();
default:
return new handlers.WrongUrlHandler();
}

@ -67,6 +67,7 @@
<Compile Include="handlers\request\ReplyHandler.cs" />
<Compile Include="handlers\request\UploadHandler.cs" />
<Compile Include="handlers\response\BoardAsThread.cs" />
<Compile Include="handlers\response\CreateThreadHandler.cs" />
<Compile Include="handlers\response\LegacyUploadHandler.cs" />
<Compile Include="handlers\response\LoginHandler.cs" />
<Compile Include="handlers\response\MigrateAccountHandler.cs" />

@ -7,6 +7,12 @@ using FLocal.Core;
namespace FLocal.IISHandler.handlers.request {
abstract class AbstractNewMessageHandler : AbstractPostHandler {
protected override string templateName {
get {
return "result/MessageCreated.xslt";
}
}
protected string getTitle(WebContext context) {
string title = context.httprequest.Form["title"].Trim();
if(title == "") {

@ -3,18 +3,27 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using FLocal.Common.dataobjects;
namespace FLocal.IISHandler.handlers.request {
class CreateThreadHandler : AbstractNewMessageHandler {
protected override string templateName {
get {
throw new NotImplementedException();
}
}
protected override XElement[] Do(WebContext context) {
throw new NotImplementedException();
Thread newThread = Board.LoadById(
int.Parse(context.httprequest.Form["board"])
).CreateThread(
context.session.account.user,
this.getTitle(context),
this.getBody(context),
int.Parse(context.httprequest.Form["layerId"])
);
Post newPost = newThread.firstPost;
newThread.markAsRead(context.session.account, newPost, newPost);
return new XElement[] { newPost.exportToXmlWithoutThread(context, false) };
}
}
}

@ -8,12 +8,6 @@ using FLocal.Common.dataobjects;
namespace FLocal.IISHandler.handlers.request {
class ReplyHandler : AbstractNewMessageHandler {
protected override string templateName {
get {
return "result/MessageCreated.xslt";
}
}
protected override XElement[] Do(WebContext context) {
// Post post = Post.LoadById(int.Parse(context.httprequest.Form["parent"]));
//int desiredLayerId = Math.Min(context.session.account.user.getMinAllowedLayer(post.thread.board), int.Parse(context.httprequest.Form["layer"]));

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Linq;
using FLocal.Core;
using FLocal.Common;
using FLocal.Common.dataobjects;
namespace FLocal.IISHandler.handlers.response {
class CreateThreadHandler : AbstractGetHandler {
override protected string templateName {
get {
return "NewThread.xslt";
}
}
override protected XElement[] getSpecificData(WebContext context) {
Board board = Board.LoadById(int.Parse(context.requestParts[1]));
return new XElement[] {
board.exportToXml(context, false),
};
}
}
}

@ -21,6 +21,7 @@
<td class="navigation" nowrap="nowrap">
<!-- postoption is either newpost.gif or greynewpost.gif -->
<a>
<xsl:attribute name="href">/Board/<xsl:value-of select="currentLocation/board/id"/>/NewThread/</xsl:attribute>
<img src="/static/images/newpost.gif" alt="Íîâîå ñîîáùåíèå" border="0" width="13" height="15" style="vertical-align: text-bottom" />
<xsl:text>Ñîîáùåíèå</xsl:text>
</a>

@ -19,8 +19,9 @@
<tr>
<td class="navigation" nowrap="nowrap">
<!-- postoption is either newpost.gif or greynewpost.gif -->
<img src="/static/images/newpost.gif" alt="Íîâîå ñîîáùåíèå" border="0" width="13" height="15" style="vertical-align: text-bottom" />
<a>
<xsl:attribute name="href">/Board/<xsl:value-of select="currentLocation/board/id"/>/NewThread/</xsl:attribute>
<img src="/static/images/newpost.gif" alt="Íîâîå ñîîáùåíèå" border="0" width="13" height="15" style="vertical-align: text-bottom" />
<xsl:text>Ñîîáùåíèå</xsl:text>
</a>
</td>

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="Windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:import href="elems\Main.xslt"/>
<xsl:import href="elems\TextEditor.xslt"/>
<xsl:template name="specific">
<table width="95%" align="center" cellpadding="1" cellspacing="1" class="tablesurround">
<tr>
<td>
<table cellpadding="3" cellspacing="1" width="100%" class="tableborders">
<tr>
<td class="tdheader">
<xsl:text>Ñîçäàíèå íîâîãî ñîîáùåíèÿ - Ôîðóì (</xsl:text>
<xsl:value-of select="board/name"/>
<xsl:text>)</xsl:text>
</td>
</tr>
<tr class="darktable">
<td>
<xsl:text>Çàïîëíèòå ïðèâåäåííóþ íèæå ôîðìó äëÿ îòïðàâêè ñîîáùåíèÿ â ôîðóì. HTML îòêëþ÷åí. UBBCode âêëþ÷åí, è âû ìîæåòå èñïîëüçîâàòü UBBCode â âàøèõ ñîîáùåíèÿõ. Àíîíèìíûå ñîîáùåíèÿ ðàçðåøåíû, è âû ìîæåòå âûáðàòü ëþáîå íåçàðåãèñòðèðîâàííîå èìÿ.</xsl:text>
</td>
</tr>
<tr>
<td class="lighttable">
<form method="post" action="/do/NewThread/" name="replier">
<input type="hidden" name="board">
<xsl:attribute name="value"><xsl:value-of select="board/id"/></xsl:attribute>
</input>
<xsl:text>Ïîëüçîâàòåëü: </xsl:text>
<xsl:value-of select="session/user/name"/>
<br/>
<br/>
<xsl:text>Òåìà: </xsl:text>
<br/>
<input type="text" tabindex="1" name="title" maxlength="70" class="formboxes" size="60"/>
<span class="small">Ñëîé ñîîáùåíèÿ:</span>
<select class="formboxes" name="layerId">
<option value="1" >Íîðìàëüíîå ñîîáùåíèå</option>
<option value="2" >Ôëóä/îôôòîïèê</option>
<option value="3" >Ìóñîð</option>
</select>
<br/>
<br/>
<xsl:call-template name="textEditor"/>
<br/>
<input type="checkbox" name="preview" value="1" class="formboxes" disabled="disabled" />
<xsl:text>ß õî÷ó ïðåäâàðèòåëüíî ïðîñìîòðåòü ñîîáùåíèå ïåðåä îòïðàâêîé</xsl:text>
<br/>
<br/>
<input type="checkbox" name="spellcheck" value="1" class="formboxes" onChange="document.replier.preview.checked=this.checked;" disabled="disabled" />
<xsl:text>Ïðîâåðèòü ïðàâîïèñàíèå</xsl:text>
<br/>
<br/>
<input type="submit" tabindex="3" name="textcont" taborder="2" value="Ïðîäîëæèòü" class="buttons"/>
</form>
</td>
</tr>
</table>
</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>

@ -33,7 +33,7 @@
<br/>
<input type="text" tabindex="1" name="title" maxlength="70" class="formboxes" size="60">
<xsl:choose>
<xsl:when test="substring(post/title, 4)='Re: '">
<xsl:when test="substring(post/title, 1, 4)='Re: '">
<xsl:attribute name="value"><xsl:value-of select="post/title"/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
@ -54,11 +54,11 @@
<br/>
<xsl:call-template name="textEditor"/>
<br/>
<input type="checkbox" name="preview" value="1" checked="checked" class="formboxes"/>
<input type="checkbox" name="preview" value="1" class="formboxes" disabled="disabled" />
<xsl:text>Я хочу предварительно просмотреть сообщение перед отправкой</xsl:text>
<br/>
<br/>
<input type="checkbox" name="spellcheck" value="1" class="formboxes" onclick="document.replier.preview.checked=true;"/>
<input type="checkbox" name="spellcheck" value="1" class="formboxes" onChange="document.replier.preview.checked=this.checked;" disabled="disabled" />
<xsl:text>Проверить правописание</xsl:text>
<br/>
<br/>

Loading…
Cancel
Save