Posts editing implemented

main
Inga 🏳‍🌈 14 years ago
parent e699dee40d
commit 4f8980384a
  1. 2
      Builder/IISMainHandler/build.txt
  2. 2
      Builder/IISUploadHandler/build.txt
  3. 1
      Common/Common.csproj
  4. 2
      Common/actions/ChangeSet.cs
  5. 91
      Common/dataobjects/Post.cs
  6. 89
      Common/dataobjects/Revision.cs
  7. 12
      Common/dataobjects/Thread.cs
  8. 4
      IISMainHandler/HandlersFactory.cs
  9. 2
      IISMainHandler/IISMainHandler.csproj
  10. 35
      IISMainHandler/handlers/request/EditHandler.cs
  11. 36
      IISMainHandler/handlers/response/EditHandler.cs
  12. 99
      templates/Full/PostEdit.xslt
  13. 19
      templates/Full/elems/PostInfo.xslt
  14. 47
      templates/Full/result/MessageEdited.xslt

@ -88,6 +88,7 @@
<Compile Include="dataobjects\Post.cs" />
<Compile Include="dataobjects\PostLayer.cs" />
<Compile Include="dataobjects\QuickLink.cs" />
<Compile Include="dataobjects\Revision.cs" />
<Compile Include="dataobjects\Session.cs" />
<Compile Include="dataobjects\Skin.cs" />
<Compile Include="dataobjects\Thread.cs" />

@ -22,7 +22,7 @@ namespace FLocal.Common.actions {
dataobjects.Thread.TableSpec.TABLE, //thread should come first because of Board.newThread locking order with two changesets
dataobjects.Board.TableSpec.TABLE,
dataobjects.Post.TableSpec.TABLE,
dataobjects.Post.RevisionTableSpec.TABLE,
dataobjects.Revision.TableSpec.TABLE,
dataobjects.Account.TableSpec.TABLE,
dataobjects.User.TableSpec.TABLE,
dataobjects.AccountSettings.TableSpec.TABLE,

@ -5,6 +5,7 @@ using System.Text;
using System.Xml.Linq;
using FLocal.Core;
using FLocal.Core.DB;
using FLocal.Core.DB.conditions;
using FLocal.Common;
using FLocal.Common.actions;
@ -29,20 +30,6 @@ namespace FLocal.Common.dataobjects {
public void refreshSqlObject(int id) { Refresh(id); }
}
public class RevisionTableSpec : ISqlObjectTableSpec {
public const string TABLE = "Revisions";
public const string FIELD_ID = "Id";
public const string FIELD_POSTID = "PostId";
public const string FIELD_CHANGEDATE = "ChangeDate";
public const string FIELD_TITLE = "Title";
public const string FIELD_BODY = "Body";
public const string FIELD_NUMBER = "Number";
public static readonly RevisionTableSpec instance = new RevisionTableSpec();
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 _posterId;
@ -67,8 +54,8 @@ namespace FLocal.Common.dataobjects {
}
}
private DateTime? _lastChangeDate;
public DateTime? lastChangeDate {
private DateTime _lastChangeDate;
public DateTime lastChangeDate {
get {
this.LoadIfNotLoaded();
return this._lastChangeDate;
@ -82,6 +69,31 @@ namespace FLocal.Common.dataobjects {
return this._revision;
}
}
public Revision latestRevision {
get {
return Revision.LoadById(
int.Parse(
Config.instance.mainConnection.LoadIdsByConditions(
Revision.TableSpec.instance,
new ComplexCondition(
ConditionsJoinType.AND,
new ComparisonCondition(
Revision.TableSpec.instance.getColumnSpec(Revision.TableSpec.FIELD_POSTID),
ComparisonType.EQUAL,
this.id.ToString()
),
new ComparisonCondition(
Revision.TableSpec.instance.getColumnSpec(Revision.TableSpec.FIELD_NUMBER),
ComparisonType.EQUAL,
this.revision.ToString()
)
),
Diapasone.unlimited
).Single()
)
);
}
}
private int _layerId;
public int layerId {
@ -145,8 +157,8 @@ namespace FLocal.Common.dataobjects {
protected override void doFromHash(Dictionary<string, string> data) {
this._posterId = int.Parse(data[TableSpec.FIELD_POSTERID]);
this._postDate = new DateTime(long.Parse(data[TableSpec.FIELD_POSTDATE]));
this._lastChangeDate = Util.ParseDateTimeFromTimestamp(data[TableSpec.FIELD_LASTCHANGEDATE]);
this._postDate = Util.ParseDateTimeFromTimestamp(data[TableSpec.FIELD_POSTDATE]).Value;
this._lastChangeDate = Util.ParseDateTimeFromTimestamp(data[TableSpec.FIELD_LASTCHANGEDATE]).Value;
this._revision = Util.ParseInt(data[TableSpec.FIELD_REVISION]);
this._layerId = int.Parse(data[TableSpec.FIELD_LAYERID]);
this._title = data[TableSpec.FIELD_TITLE];
@ -175,15 +187,16 @@ namespace FLocal.Common.dataobjects {
new XElement("id", this.id),
new XElement("poster", this.poster.exportToXmlForViewing(context)),
new XElement("postDate", this.postDate.ToXml()),
new XElement("lastChangeDate", this.postDate.ToXml()),
new XElement("revision", this.revision),
new XElement("lastChangeDate", this.lastChangeDate.ToXml()),
new XElement("revision", this.revision.ToString()),
new XElement("layerId", this.layerId),
new XElement("layerName", this.layer.name),
new XElement("title", this.title),
new XElement("body", context.outputParams.preprocessBodyIntermediate(this.body)),
//this.XMLBody(context),
new XElement("bodyShort", this.bodyShort),
new XElement("threadId", this.threadId)
new XElement("threadId", this.threadId),
new XElement("isOwner", ((context.account != null) && (this.poster.id == context.account.user.id)).ToPlainString())
);
if(includeParentPost) {
if(this.parentPostId.HasValue) {
@ -213,5 +226,41 @@ namespace FLocal.Common.dataobjects {
return Post.LoadById(changes.Key.getId().Value);
}
private readonly object Edit_locker = new object(); //TODO: move locking to DB
public void Edit(User user, string newTitle, string newBody, PostLayer newDesiredLayer) {
if(this.poster.id != user.id) {
throw new AccessViolationException();
}
PostLayer actualLayer = poster.getActualLayer(this.thread.board, newDesiredLayer);
if(actualLayer.id < this.layer.id) {
actualLayer = this.layer;
}
lock(this.Edit_locker) {
ChangeSetUtil.ApplyChanges(
new InsertChange(
Revision.TableSpec.instance,
new Dictionary<string, AbstractFieldValue> {
{ Revision.TableSpec.FIELD_POSTID, new ScalarFieldValue(this.id.ToString()) },
{ Revision.TableSpec.FIELD_CHANGEDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ Revision.TableSpec.FIELD_TITLE, new ScalarFieldValue(newTitle) },
{ Revision.TableSpec.FIELD_BODY, new ScalarFieldValue(newBody) },
{ Revision.TableSpec.FIELD_NUMBER, new ScalarFieldValue((this.revision + 1).ToString()) },
}
),
new UpdateChange(
TableSpec.instance,
new Dictionary<string, AbstractFieldValue> {
{ TableSpec.FIELD_TITLE, new ScalarFieldValue(newTitle) },
{ TableSpec.FIELD_BODY, new ScalarFieldValue(UBBParser.UBBToIntermediate(newBody)) },
{ TableSpec.FIELD_LASTCHANGEDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ TableSpec.FIELD_REVISION, new IncrementFieldValue() },
{ TableSpec.FIELD_LAYERID, new ScalarFieldValue(actualLayer.id.ToString()) },
},
this.id
)
);
}
}
}
}

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FLocal.Core;
using System.Xml.Linq;
namespace FLocal.Common.dataobjects {
public class Revision : SqlObject<Revision> {
public class TableSpec : ISqlObjectTableSpec {
public const string TABLE = "Revisions";
public const string FIELD_ID = "Id";
public const string FIELD_POSTID = "PostId";
public const string FIELD_CHANGEDATE = "ChangeDate";
public const string FIELD_TITLE = "Title";
public const string FIELD_BODY = "Body";
public const string FIELD_NUMBER = "Number";
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) { }
}
protected override ISqlObjectTableSpec table { get { return TableSpec.instance; } }
private int _postId;
public int postId {
get {
this.LoadIfNotLoaded();
return this._postId;
}
}
public Post post {
get {
return Post.LoadById(this.postId);
}
}
private DateTime _changeDate;
public DateTime changeDate {
get {
this.LoadIfNotLoaded();
return this._changeDate;
}
}
private string _title;
public string title {
get {
this.LoadIfNotLoaded();
return this._title;
}
}
private string _body;
public string body {
get {
this.LoadIfNotLoaded();
return this._body;
}
}
private int _number;
public int number {
get {
this.LoadIfNotLoaded();
return this._number;
}
}
protected override void doFromHash(Dictionary<string, string> data) {
this._postId = int.Parse(data[TableSpec.FIELD_POSTID]);
this._changeDate = Util.ParseDateTimeFromTimestamp(data[TableSpec.FIELD_CHANGEDATE]).Value;
this._title = data[TableSpec.FIELD_TITLE];
this._body = data[TableSpec.FIELD_BODY];
this._number = int.Parse(data[TableSpec.FIELD_NUMBER]);
}
public XElement exportToXml(UserContext context) {
return new XElement("revision",
new XElement("id", this.id),
new XElement("title", this.title),
new XElement("body", this.body)
);
}
}
}

@ -410,13 +410,13 @@ namespace FLocal.Common.dataobjects {
AbstractFieldValue postReference = new ReferenceFieldValue(postInsert);
AbstractFieldValue postIndexReference = new TwoWayReferenceFieldValue(postInsert, TwoWayReferenceFieldValue.GREATEST);
AbstractChange revisionInsert = new InsertChange(
Post.RevisionTableSpec.instance,
Revision.TableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ Post.RevisionTableSpec.FIELD_POSTID, postReference },
{ Post.RevisionTableSpec.FIELD_NUMBER, new ScalarFieldValue("0") },
{ Post.RevisionTableSpec.FIELD_CHANGEDATE, new ScalarFieldValue(date.ToUTCString()) },
{ Post.RevisionTableSpec.FIELD_TITLE, new ScalarFieldValue(title) },
{ Post.RevisionTableSpec.FIELD_BODY, new ScalarFieldValue(body) },
{ Revision.TableSpec.FIELD_POSTID, postReference },
{ Revision.TableSpec.FIELD_NUMBER, new ScalarFieldValue("0") },
{ Revision.TableSpec.FIELD_CHANGEDATE, new ScalarFieldValue(date.ToUTCString()) },
{ Revision.TableSpec.FIELD_TITLE, new ScalarFieldValue(title) },
{ Revision.TableSpec.FIELD_BODY, new ScalarFieldValue(body) },
}
);
Dictionary<string, AbstractFieldValue> threadData = new Dictionary<string,AbstractFieldValue> {

@ -57,6 +57,8 @@ namespace FLocal.IISHandler {
return new handlers.PostHandler();
}
switch(context.requestParts[2].ToLower()) {
case "edit":
return new handlers.response.EditHandler();
case "reply":
return new handlers.response.ReplyHandler();
case "pmreply":
@ -129,6 +131,8 @@ namespace FLocal.IISHandler {
return new handlers.request.LogoutHandler();
case "migrateaccount":
return new handlers.request.MigrateAccountHandler();
case "edit":
return new handlers.request.EditHandler();
case "reply":
return new handlers.request.ReplyHandler();
case "newthread":

@ -61,6 +61,7 @@
<Compile Include="handlers\request\AbstractNewMessageHandler.cs" />
<Compile Include="handlers\request\AbstractPostHandler.cs" />
<Compile Include="handlers\request\CreateThreadHandler.cs" />
<Compile Include="handlers\request\EditHandler.cs" />
<Compile Include="handlers\request\LoginHandler.cs" />
<Compile Include="handlers\request\LogoutHandler.cs" />
<Compile Include="handlers\request\MarkThreadAsReadHandler.cs" />
@ -75,6 +76,7 @@
<Compile Include="handlers\response\ConversationHandler.cs" />
<Compile Include="handlers\response\ConversationsHandler.cs" />
<Compile Include="handlers\response\CreateThreadHandler.cs" />
<Compile Include="handlers\response\EditHandler.cs" />
<Compile Include="handlers\response\LegacyPHPHandler.cs" />
<Compile Include="handlers\response\LegacyUploadHandler.cs" />
<Compile Include="handlers\response\LoginHandler.cs" />

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using FLocal.Common.dataobjects;
namespace FLocal.IISHandler.handlers.request {
class EditHandler : AbstractNewMessageHandler {
protected override string templateName {
get {
return "result/MessageEdited.xslt";
}
}
protected override XElement[] Do(WebContext context) {
Post post = Post.LoadById(int.Parse(context.httprequest.Form["postId"]));
XElement postXml = post.exportToXmlWithoutThread(context, false);
post.Edit(
context.session.account.user,
this.getTitle(context),
this.getBody(context),
PostLayer.LoadById(int.Parse(context.httprequest.Form["layerId"]))
);
return new XElement[] {
post.thread.board.exportToXml(context, false),
postXml
};
}
}
}

@ -0,0 +1,36 @@
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 EditHandler : AbstractGetHandler {
override protected string templateName {
get {
return "PostEdit.xslt";
}
}
override protected XElement[] getSpecificData(WebContext context) {
Post post = Post.LoadById(int.Parse(context.requestParts[1]));
return new XElement[] {
post.thread.board.exportToXml(context, false),
post.thread.exportToXml(context, false),
post.exportToXmlWithoutThread(context, false),
post.latestRevision.exportToXml(context),
new XElement("layers",
from layer in PostLayer.allLayers select layer.exportToXml(context)
),
};
}
}
}

@ -0,0 +1,99 @@
<?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>Çàïîëíèòå ïðèâåäåííóþ íèæå ôîðìó äëÿ èçìåíåíèÿ ñîîáùåíèÿ</xsl:text>
</td>
</tr>
<tr>
<td class="lighttable">
<form method="post" action="/do/Edit/" name="replier">
<input type="hidden" name="postId">
<xsl:attribute name="value"><xsl:value-of select="post/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">
<xsl:attribute name="value"><xsl:value-of select="revision/title"/></xsl:attribute>
</input>
<span class="small">Ñëîé ñîîáùåíèÿ:</span>
<select class="formboxes" name="layerId">
<xsl:apply-templates select="layers/layer">
<xsl:with-param name="defaultLayerId"><xsl:value-of select="post/layerId"/></xsl:with-param>
</xsl:apply-templates>
</select>
<br/>
<br/>
<xsl:call-template name="textEditor">
<xsl:with-param name="body"><xsl:value-of select="revision/body"/></xsl:with-param>
</xsl:call-template>
<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>
<br/>
<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>
</td>
</tr>
<tr class="darktable">
<td>
<b>
<xsl:text>Àâòîð: </xsl:text>
<xsl:value-of select="post/poster/user/name"/>
<br/>
<xsl:text>Òåìà: </xsl:text>
<xsl:value-of select="post/title"/>
</b>
</td>
</tr>
<tr>
<td class="lighttable">
<xsl:value-of select="post/body" disable-output-escaping="yes"/>
</td>
</tr>
</table>
</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>

@ -66,6 +66,9 @@
</td>
<td class="navigation">
<a>
<xsl:if test="isOwner='true'">
<xsl:attribute name="href">/Post/<xsl:value-of select="id"/>/Edit/</xsl:attribute>
</xsl:if>
<img src="/static/images/edit.gif" border="0" alt="Ïðàâêà ñîîáùåíèÿ" title="Ïðàâêà ñîîáùåíèÿ" width="21" height="14" style="vertical-align: text-bottom" />
</a>
</td>
@ -116,6 +119,22 @@
</font>
</td>
</tr>
<xsl:if test="revision">
<xsl:if test="(revision != '') and (revision != '0')">
<tr>
<td>
<font size="-2">
<xsl:text>Ýòî ñîîáùåíèå áûëî îòðåäàêòèðîâàíî ïîëüçîâàòåëåì </xsl:text>
<xsl:value-of select="revision"/>
<xsl:text> ðàç, ïîñëåäíèé ðàç </xsl:text>
<xsl:apply-templates select="lastChangeDate/date" mode="dateTime"/>
<br />
<br/>
</font>
</td>
</tr>
</xsl:if>
</xsl:if>
<xsl:if test="poster/user/signature != ''">
<tr>
<td>

@ -0,0 +1,47 @@
<?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: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>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table cellpadding="3" cellspacing="1" width="100%" class="tableborders">
<tr>
<td class="lighttable">
<p>Âàøå ñîîáùåíèå áûëî óñïåøíî èçìåíåíî</p>
<p align="center">
<xsl:text>[&#8592;] </xsl:text>
<a id="actionLink_left">
<xsl:attribute name="href">/Board/<xsl:value-of select="board/id"/>/</xsl:attribute>
<xsl:text>Âåðíóòüñÿ â ðàçäåë</xsl:text>
</a>
<xsl:text> | </xsl:text>
<a id="actionLink_right">
<xsl:attribute name="href">/Thread/<xsl:value-of select="post/threadId"/>/p<xsl:value-of select="post/id"/></xsl:attribute>
<xsl:text>Ïðîñìîòðåòü ñîîáùåíèå</xsl:text>
</a>
<xsl:text> [&#8594;]</xsl:text>
</p>
<script type="text/javascript" language="Javascript">
<xsl:text>assignArrowsHandlers();</xsl:text>
</script>
</td>
</tr>
</table>
</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
Loading…
Cancel
Save