Moderation implemented

main
Inga 🏳‍🌈 14 years ago
parent 8252fcdbfc
commit 29f54df117
  1. 2
      Builder/IISMainHandler/build.txt
  2. 3
      Common/Common.csproj
  3. 3
      Common/TableManager.cs
  4. 2
      Common/actions/ChangeSet.cs
  5. 11
      Common/dataobjects/Board.cs
  6. 172
      Common/dataobjects/Moderator.cs
  7. 99
      Common/dataobjects/Post.cs
  8. 146
      Common/dataobjects/Punishment.cs
  9. 92
      Common/dataobjects/PunishmentType.cs
  10. 1
      Common/dataobjects/Thread.cs
  11. 3
      Common/dataobjects/UserGroup.cs
  12. 4
      IISMainHandler/HandlersFactory.cs
  13. 2
      IISMainHandler/IISMainHandler.csproj
  14. 7
      IISMainHandler/handlers/PostHandler.cs
  15. 5
      IISMainHandler/handlers/ThreadHandler.cs
  16. 34
      IISMainHandler/handlers/request/PunishHandler.cs
  17. 42
      IISMainHandler/handlers/response/PunishHandler.cs
  18. 11
      static/css/global.css
  19. BIN
      static/images/mod.gif
  20. BIN
      static/images/punish.gif
  21. 98
      templates/Full/PostPunish.xslt
  22. 4
      templates/Full/elems/BoardInfo.xslt
  23. 3
      templates/Full/elems/Main.xslt
  24. 29
      templates/Full/elems/PostInfo.xslt
  25. 48
      templates/Full/result/MessagePunished.xslt
  26. 94
      templates/Lite/PostPunish.xslt
  27. 3
      templates/Lite/elems/Main.xslt
  28. 7
      templates/Lite/elems/PostInfo.xslt
  29. 48
      templates/Lite/result/MessagePunished.xslt

@ -91,11 +91,14 @@
<Compile Include="dataobjects\IUserSettings.cs" /> <Compile Include="dataobjects\IUserSettings.cs" />
<Compile Include="dataobjects\AnonymousUserSettings.cs" /> <Compile Include="dataobjects\AnonymousUserSettings.cs" />
<Compile Include="dataobjects\LocalNetwork.cs" /> <Compile Include="dataobjects\LocalNetwork.cs" />
<Compile Include="dataobjects\Moderator.cs" />
<Compile Include="dataobjects\PMConversation.cs" /> <Compile Include="dataobjects\PMConversation.cs" />
<Compile Include="dataobjects\PMMessage.cs" /> <Compile Include="dataobjects\PMMessage.cs" />
<Compile Include="dataobjects\Poll.cs" /> <Compile Include="dataobjects\Poll.cs" />
<Compile Include="dataobjects\Post.cs" /> <Compile Include="dataobjects\Post.cs" />
<Compile Include="dataobjects\PostLayer.cs" /> <Compile Include="dataobjects\PostLayer.cs" />
<Compile Include="dataobjects\Punishment.cs" />
<Compile Include="dataobjects\PunishmentType.cs" />
<Compile Include="dataobjects\QuickLink.cs" /> <Compile Include="dataobjects\QuickLink.cs" />
<Compile Include="dataobjects\Revision.cs" /> <Compile Include="dataobjects\Revision.cs" />
<Compile Include="dataobjects\Session.cs" /> <Compile Include="dataobjects\Session.cs" />

@ -15,8 +15,11 @@ namespace FLocal.Common {
dataobjects.Category.TableSpec.instance, dataobjects.Category.TableSpec.instance,
dataobjects.Invite.TableSpec.instance, dataobjects.Invite.TableSpec.instance,
dataobjects.LocalNetwork.TableSpec.instance, dataobjects.LocalNetwork.TableSpec.instance,
dataobjects.Moderator.TableSpec.instance,
dataobjects.PMConversation.TableSpec.instance, dataobjects.PMConversation.TableSpec.instance,
dataobjects.PMMessage.TableSpec.instance, dataobjects.PMMessage.TableSpec.instance,
dataobjects.Punishment.TableSpec.instance,
dataobjects.PunishmentType.TableSpec.instance,
dataobjects.Poll.TableSpec.instance, dataobjects.Poll.TableSpec.instance,
dataobjects.Post.TableSpec.instance, dataobjects.Post.TableSpec.instance,
dataobjects.PostLayer.TableSpec.instance, dataobjects.PostLayer.TableSpec.instance,

@ -34,6 +34,8 @@ namespace FLocal.Common.actions {
dataobjects.Poll.TableSpec.TABLE, dataobjects.Poll.TableSpec.TABLE,
dataobjects.Poll.Vote.TableSpec.TABLE, dataobjects.Poll.Vote.TableSpec.TABLE,
dataobjects.Invite.TableSpec.TABLE, dataobjects.Invite.TableSpec.TABLE,
dataobjects.Moderator.TableSpec.TABLE,
dataobjects.Punishment.TableSpec.TABLE,
dataobjects.Session.TableSpec.TABLE, dataobjects.Session.TableSpec.TABLE,
} }
); );

@ -201,6 +201,14 @@ namespace FLocal.Common.dataobjects {
); );
} }
public XElement exportToXmlSimple(UserContext context) {
return new XElement("board",
new XElement("id", this.id),
new XElement("name", this.name),
new XElement("description", this.description)
);
}
public XElement exportToXml(UserContext context, bool includeSubBoards, params XElement[] additional) { public XElement exportToXml(UserContext context, bool includeSubBoards, params XElement[] additional) {
XElement result = new XElement("board", XElement result = new XElement("board",
new XElement("id", this.id), new XElement("id", this.id),
@ -210,7 +218,8 @@ namespace FLocal.Common.dataobjects {
new XElement("totalThreads", this.totalThreads), new XElement("totalThreads", this.totalThreads),
new XElement("name", this.name), new XElement("name", this.name),
new XElement("description", this.description), new XElement("description", this.description),
new XElement("lastPostInfo", this.exportLastPostInfo(context)) new XElement("lastPostInfo", this.exportLastPostInfo(context)),
new XElement("moderators", from moderator in Moderator.GetModerators(this) select moderator.user.exportToXmlForViewing(context))
); );
if(context.account != null) { if(context.account != null) {

@ -0,0 +1,172 @@
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;
namespace FLocal.Common.dataobjects {
public class Moderator : SqlObject<Moderator> {
public class TableSpec : ISqlObjectTableSpec {
public const string TABLE = "Moderators";
public const string FIELD_ID = "Id";
public const string FIELD_ACCOUNTID = "AccountId";
public const string FIELD_BOARDID = "BoardId";
public const string FIELD_ISACTIVE = "IsActive";
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);
var moderator = Moderator.LoadById(id);
byAccount_Recalculate(moderator.accountId);
byBoard_Recalculate(moderator.boardId);
lock(isModerator_cache) {
if(!isModerator_cache.ContainsKey(moderator.accountId)) isModerator_cache[moderator.accountId] = new Dictionary<int,bool>();
isModerator_cache[moderator.accountId][moderator.boardId] = moderator.isActive;
}
}
}
protected override ISqlObjectTableSpec table { get { return TableSpec.instance; } }
private int _accountId;
public int accountId {
get {
this.LoadIfNotLoaded();
return this._accountId;
}
}
public Account account {
get {
return Account.LoadById(this.accountId);
}
}
private int _boardId;
public int boardId {
get {
this.LoadIfNotLoaded();
return this._boardId;
}
}
public Board board {
get {
return Board.LoadById(this.boardId);
}
}
private bool _isActive;
public bool isActive {
get {
this.LoadIfNotLoaded();
return this._isActive;
}
}
protected override void doFromHash(Dictionary<string, string> data) {
this._accountId = int.Parse(data[TableSpec.FIELD_ACCOUNTID]);
this._boardId = int.Parse(data[TableSpec.FIELD_BOARDID]);
this._isActive = Util.string2bool(data[TableSpec.FIELD_ISACTIVE]);
}
private static readonly Dictionary<int, Dictionary<int, bool>> isModerator_cache = new Dictionary<int,Dictionary<int,bool>>();
public static bool isModerator(Account account, Board board) {
//slight optimisation...
UserGroup group = account.user.userGroup;
if(group.name != UserGroup.NAME_ADMINISTRATORS && group.name != UserGroup.NAME_MODERATORS) return false;
if(!isModerator_cache.ContainsKey(account.id) || !isModerator_cache[account.id].ContainsKey(board.id)) {
lock(isModerator_cache) {
if(!isModerator_cache.ContainsKey(account.id) || !isModerator_cache[account.id].ContainsKey(board.id)) {
if(!isModerator_cache.ContainsKey(account.id)) isModerator_cache[account.id] = new Dictionary<int,bool>();
List<string> ids = Config.instance.mainConnection.LoadIdsByConditions(
TableSpec.instance,
new ComplexCondition(
ConditionsJoinType.AND,
new ComparisonCondition(
TableSpec.instance.getColumnSpec(TableSpec.FIELD_ACCOUNTID),
ComparisonType.EQUAL,
account.id.ToString()
),
new ComparisonCondition(
TableSpec.instance.getColumnSpec(TableSpec.FIELD_BOARDID),
ComparisonType.EQUAL,
board.id.ToString()
)
),
Diapasone.unlimited
);
if(ids.Count < 1) {
isModerator_cache[account.id][board.id] = false;
} else {
isModerator_cache[account.id][board.id] = Moderator.LoadById(int.Parse(ids.Single())).isActive;
}
}
}
}
return isModerator_cache[account.id][board.id];
}
public static bool isModerator(User user, Board board) {
Account account;
try {
account = Account.LoadByUser(user);
} catch(NotFoundInDBException) {
return false;
}
return isModerator(account, board);
}
private static readonly Dictionary<int, IEnumerable<int>> byBoard_cache = new Dictionary<int,IEnumerable<int>>();
private static void byBoard_Recalculate(int boardId) {
lock(byBoard_cache) {
byBoard_cache[boardId] =
from stringId in Config.instance.mainConnection.LoadIdsByConditions(
TableSpec.instance,
new ComparisonCondition(
TableSpec.instance.getColumnSpec(TableSpec.FIELD_BOARDID),
ComparisonType.EQUAL,
boardId.ToString()
),
Diapasone.unlimited
)
select Moderator.LoadById(int.Parse(stringId)).accountId;
}
}
public static IEnumerable<Account> GetModerators(Board board) {
if(!byBoard_cache.ContainsKey(board.id)) {
byBoard_Recalculate(board.id);
}
return from id in byBoard_cache[board.id] select Account.LoadById(id);
}
private static readonly Dictionary<int, IEnumerable<int>> byAccount_cache = new Dictionary<int,IEnumerable<int>>();
private static void byAccount_Recalculate(int accountId) {
lock(byAccount_cache) {
byAccount_cache[accountId] =
from stringId in Config.instance.mainConnection.LoadIdsByConditions(
TableSpec.instance,
new ComparisonCondition(
TableSpec.instance.getColumnSpec(TableSpec.FIELD_ACCOUNTID),
ComparisonType.EQUAL,
accountId.ToString()
),
Diapasone.unlimited
)
select Moderator.LoadById(int.Parse(stringId)).boardId;
}
}
public static IEnumerable<Board> GetModeratedBoards(Account account) {
if(!byAccount_cache.ContainsKey(account.id)) {
byAccount_Recalculate(account.id);
}
return from id in byAccount_cache[account.id] select Board.LoadById(id);
}
}
}

@ -24,6 +24,7 @@ namespace FLocal.Common.dataobjects {
public const string FIELD_BODY = "Body"; public const string FIELD_BODY = "Body";
public const string FIELD_THREADID = "ThreadId"; public const string FIELD_THREADID = "ThreadId";
public const string FIELD_PARENTPOSTID = "ParentPostId"; public const string FIELD_PARENTPOSTID = "ParentPostId";
public const string FIELD_TOTALPUNISHMENTS = "TotalPunishments";
public static readonly TableSpec instance = new TableSpec(); public static readonly TableSpec instance = new TableSpec();
public string name { get { return TABLE; } } public string name { get { return TABLE; } }
public string idName { get { return FIELD_ID; } } public string idName { get { return FIELD_ID; } }
@ -155,6 +156,43 @@ namespace FLocal.Common.dataobjects {
} }
} }
private int _totalPunishments;
public int totalPunishments {
get {
this.LoadIfNotLoaded();
return this._totalPunishments;
}
}
private readonly object punishments_Locker = new object();
public IEnumerable<Punishment> punishments {
get {
return
from id in Cache<IEnumerable<int>>.instance.get(
this.punishments_Locker,
() => {
IEnumerable<int> ids = (from stringId in Config.instance.mainConnection.LoadIdsByConditions(
Punishment.TableSpec.instance,
new ComparisonCondition(
Punishment.TableSpec.instance.getColumnSpec(Punishment.TableSpec.FIELD_POSTID),
ComparisonType.EQUAL,
this.id.ToString()
),
Diapasone.unlimited
) select int.Parse(stringId)).ToList();
Punishment.LoadByIds(ids);
return ids;
}
)
let punishment = Punishment.LoadById(id)
orderby punishment.id
select punishment;
}
}
internal void punishments_Reset() {
Cache<IEnumerable<int>>.instance.delete(this.punishments_Locker);
}
protected override void doFromHash(Dictionary<string, string> data) { protected override void doFromHash(Dictionary<string, string> data) {
this._posterId = int.Parse(data[TableSpec.FIELD_POSTERID]); this._posterId = int.Parse(data[TableSpec.FIELD_POSTERID]);
this._postDate = Util.ParseDateTimeFromTimestamp(data[TableSpec.FIELD_POSTDATE]).Value; this._postDate = Util.ParseDateTimeFromTimestamp(data[TableSpec.FIELD_POSTDATE]).Value;
@ -165,6 +203,7 @@ namespace FLocal.Common.dataobjects {
this._body = data[TableSpec.FIELD_BODY]; this._body = data[TableSpec.FIELD_BODY];
this._threadId = int.Parse(data[TableSpec.FIELD_THREADID]); this._threadId = int.Parse(data[TableSpec.FIELD_THREADID]);
this._parentPostId = Util.ParseInt(data[TableSpec.FIELD_PARENTPOSTID]); this._parentPostId = Util.ParseInt(data[TableSpec.FIELD_PARENTPOSTID]);
this._totalPunishments = Util.ParseInt(data[TableSpec.FIELD_TOTALPUNISHMENTS]).GetValueOrDefault(0);
} }
public XElement exportToXmlSimpleWithParent(UserContext context) { public XElement exportToXmlSimpleWithParent(UserContext context) {
@ -179,7 +218,8 @@ namespace FLocal.Common.dataobjects {
return new XElement("post", return new XElement("post",
new XElement("id", this.id), new XElement("id", this.id),
new XElement("poster", this.poster.exportToXmlForViewing(context)), new XElement("poster", this.poster.exportToXmlForViewing(context)),
new XElement("bodyShort", context.isPostVisible(this) ? this.bodyShort : "") new XElement("bodyShort", context.isPostVisible(this) ? this.bodyShort : ""),
new XElement("title", this.title)
); );
} }
@ -193,7 +233,12 @@ namespace FLocal.Common.dataobjects {
XElement result = new XElement("post", XElement result = new XElement("post",
new XElement("id", this.id), new XElement("id", this.id),
new XElement("poster", this.poster.exportToXmlForViewing(context)), new XElement("poster",
this.poster.exportToXmlForViewing(
context,
new XElement("isModerator", Moderator.isModerator(this.poster, this.thread.board).ToPlainString())
)
),
new XElement("postDate", this.postDate.ToXml()), new XElement("postDate", this.postDate.ToXml()),
new XElement("layerId", this.layerId), new XElement("layerId", this.layerId),
new XElement("layerName", this.layer.name), new XElement("layerName", this.layer.name),
@ -202,6 +247,7 @@ namespace FLocal.Common.dataobjects {
//this.XMLBody(context), //this.XMLBody(context),
new XElement("bodyShort", this.bodyShort), new XElement("bodyShort", this.bodyShort),
new XElement("threadId", this.threadId), new XElement("threadId", this.threadId),
new XElement("isPunishmentEnabled", ((context.account != null) && Moderator.isModerator(context.account, this.thread.board)).ToPlainString()),
new XElement("isOwner", ((context.account != null) && (this.poster.id == context.account.user.id)).ToPlainString()), new XElement("isOwner", ((context.account != null) && (this.poster.id == context.account.user.id)).ToPlainString()),
new XElement( new XElement(
"specific", "specific",
@ -212,6 +258,9 @@ namespace FLocal.Common.dataobjects {
) )
) )
); );
if(this.totalPunishments > 0) {
result.Add(from punishment in punishments select new XElement("specific", punishment.exportToXml(context)));
}
if(this.parentPostId.HasValue) { if(this.parentPostId.HasValue) {
result.Add(new XElement("parentPost", this.parentPost.exportToXmlBase(context))); result.Add(new XElement("parentPost", this.parentPost.exportToXmlBase(context)));
} }
@ -286,5 +335,51 @@ namespace FLocal.Common.dataobjects {
} }
} }
public void Punish(Account account, PunishmentType type, string comment) {
if(!Moderator.isModerator(account, this.thread.board)) throw new FLocalException(account.id + " is not a moderator in board " + this.thread.board.id);
if(account.user.id == this.poster.id) throw new FLocalException("You cannot punish your own posts");
ChangeSetUtil.ApplyChanges(
new UpdateChange(
TableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ TableSpec.FIELD_TOTALPUNISHMENTS, new IncrementFieldValue() },
},
this.id
),
new InsertChange(
Punishment.TableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ Punishment.TableSpec.FIELD_POSTID, new ScalarFieldValue(this.id.ToString()) },
{ Punishment.TableSpec.FIELD_OWNERID, new ScalarFieldValue(this.poster.id.ToString()) },
{ Punishment.TableSpec.FIELD_ORIGINALBOARDID, new ScalarFieldValue(this.thread.board.id.ToString()) },
{ Punishment.TableSpec.FIELD_MODERATORID, new ScalarFieldValue(account.id.ToString()) },
{ Punishment.TableSpec.FIELD_PUNISHMENTDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ Punishment.TableSpec.FIELD_PUNISHMENTTYPE, new ScalarFieldValue(type.id.ToString()) },
{ Punishment.TableSpec.FIELD_ISWITHDRAWED, new ScalarFieldValue("0") },
{ Punishment.TableSpec.FIELD_COMMENT, new ScalarFieldValue(comment) },
}
)
);
Account posterAccount = null;
try {
posterAccount = Account.LoadByUser(this.poster);
} catch(NotFoundInDBException) {
}
if(posterAccount != null) {
PMMessage newMessage = PMConversation.SendPMMessage(
account,
posterAccount,
this.title,
type.description + "\r\n" + this.id
);
newMessage.conversation.markAsRead(account, newMessage, newMessage);
}
}
} }
} }

@ -0,0 +1,146 @@
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;
namespace FLocal.Common.dataobjects {
public class Punishment : SqlObject<Punishment> {
public class TableSpec : ISqlObjectTableSpec {
public const string TABLE = "Punishments";
public const string FIELD_ID = "Id";
public const string FIELD_POSTID = "PostId";
public const string FIELD_OWNERID = "OwnerId";
public const string FIELD_ORIGINALBOARDID = "OriginalBoardId";
public const string FIELD_MODERATORID = "ModeratorId";
public const string FIELD_PUNISHMENTDATE = "PunishmentDate";
public const string FIELD_PUNISHMENTTYPE = "PunishmentType";
public const string FIELD_ISWITHDRAWED = "IsWithdrawed";
public const string FIELD_COMMENT = "Comment";
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); }
}
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 int _ownerId;
public int ownerId {
get {
this.LoadIfNotLoaded();
return this._ownerId;
}
}
public User owner {
get {
return User.LoadById(this.ownerId);
}
}
private int _originalBoardId;
public int originalBoardId {
get {
this.LoadIfNotLoaded();
return this._originalBoardId;
}
}
public Board originalBoard {
get {
return Board.LoadById(this.originalBoardId);
}
}
private int _moderatorId;
public int moderatorId {
get {
this.LoadIfNotLoaded();
return this._moderatorId;
}
}
public Account moderator {
get {
return Account.LoadById(this.moderatorId);
}
}
private DateTime _punishmentDate;
public DateTime punishmentDate {
get {
this.LoadIfNotLoaded();
return this._punishmentDate;
}
}
private int _punishmentTypeId;
public int punishmentTypeId {
get {
this.LoadIfNotLoaded();
return this._punishmentTypeId;
}
}
public PunishmentType punishmentType {
get {
return PunishmentType.LoadById(this.punishmentTypeId);
}
}
private bool _isWithdrawed;
public bool isWithdrawed {
get {
this.LoadIfNotLoaded();
return this._isWithdrawed;
}
}
private string _comment;
public string comment {
get {
this.LoadIfNotLoaded();
return this._comment;
}
}
protected override void doFromHash(Dictionary<string, string> data) {
this._postId = int.Parse(data[TableSpec.FIELD_POSTID]);
this._ownerId = int.Parse(data[TableSpec.FIELD_OWNERID]);
this._originalBoardId = int.Parse(data[TableSpec.FIELD_ORIGINALBOARDID]);
this._moderatorId = int.Parse(data[TableSpec.FIELD_MODERATORID]);
this._punishmentDate = Util.ParseDateTimeFromTimestamp(data[TableSpec.FIELD_PUNISHMENTDATE]).Value;
this._punishmentTypeId = int.Parse(data[TableSpec.FIELD_PUNISHMENTTYPE]);
this._isWithdrawed = Util.string2bool(data[TableSpec.FIELD_ISWITHDRAWED]);
this._comment = data[TableSpec.FIELD_COMMENT];
}
public XElement exportToXml(UserContext context) {
return new XElement("punishment",
this.post.exportToXmlBase(context),
new XElement("owner", this.owner.exportToXmlForViewing(context)),
new XElement("originalBoard", this.originalBoard.exportToXmlSimple(context)),
new XElement("moderator", this.moderator.user.exportToXmlForViewing(context)),
new XElement("punishmentDate", this.punishmentDate.ToXml()),
this.punishmentType.exportToXml(context),
new XElement("isWithdrawed", this.isWithdrawed.ToPlainString()),
new XElement("comment", this.comment)
);
}
}
}

@ -0,0 +1,92 @@
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;
namespace FLocal.Common.dataobjects {
public class PunishmentType : SqlObject<PunishmentType> {
public class TableSpec : ISqlObjectTableSpec {
public const string TABLE = "PunishmentTypes";
public const string FIELD_ID = "Id";
public const string FIELD_DESCRIPTION = "Description";
public const string FIELD_WEIGHT = "Weight";
public const string FIELD_WEIGHTDESCRIPTION = "WeightDescription";
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); }
}
protected override ISqlObjectTableSpec table { get { return TableSpec.instance; } }
private string _description;
public string description {
get {
this.LoadIfNotLoaded();
return this._description;
}
}
private int _weight;
public int weight {
get {
this.LoadIfNotLoaded();
return this._weight;
}
}
private string _weightDescription;
public string weightDescription {
get {
this.LoadIfNotLoaded();
return this._weightDescription;
}
}
protected override void doFromHash(Dictionary<string, string> data) {
this._description = data[TableSpec.FIELD_DESCRIPTION];
this._weight = int.Parse(data[TableSpec.FIELD_WEIGHT]);
this._weightDescription = data[TableSpec.FIELD_WEIGHTDESCRIPTION];
}
public XElement exportToXml(UserContext context) {
return new XElement("punishmentType",
new XElement("id", this.id),
new XElement("description", this.description),
new XElement("weight", this.weight),
new XElement("weightDescription", this.weightDescription)
);
}
private static readonly object allTypes_Locker = new object();
public static IEnumerable<PunishmentType> allTypes {
get {
return
from id in Cache<IEnumerable<int>>.instance.get(
allTypes_Locker,
() => {
IEnumerable<int> ids = (from stringId in Config.instance.mainConnection.LoadIdsByConditions(
TableSpec.instance,
new FLocal.Core.DB.conditions.EmptyCondition(),
Diapasone.unlimited
) select int.Parse(stringId)).ToList();
PunishmentType.LoadByIds(ids);
return ids;
}
)
let type = PunishmentType.LoadById(id)
orderby type.weight
select type;
}
}
internal static void allTypes_Reset() {
Cache<IEnumerable<int>>.instance.delete(allTypes_Locker);
}
}
}

@ -396,6 +396,7 @@ namespace FLocal.Common.dataobjects {
{ Post.TableSpec.FIELD_LAYERID, new ScalarFieldValue(layer.id.ToString()) }, { Post.TableSpec.FIELD_LAYERID, new ScalarFieldValue(layer.id.ToString()) },
{ Post.TableSpec.FIELD_TITLE, new ScalarFieldValue(title) }, { Post.TableSpec.FIELD_TITLE, new ScalarFieldValue(title) },
{ Post.TableSpec.FIELD_BODY, new ScalarFieldValue(bodyIntermediate) }, { Post.TableSpec.FIELD_BODY, new ScalarFieldValue(bodyIntermediate) },
{ Post.TableSpec.FIELD_TOTALPUNISHMENTS, new ScalarFieldValue("0") },
}; };
if(forcedPostId.HasValue) { if(forcedPostId.HasValue) {
postInsertData[Post.TableSpec.FIELD_ID] = new ScalarFieldValue(forcedPostId.Value.ToString()); postInsertData[Post.TableSpec.FIELD_ID] = new ScalarFieldValue(forcedPostId.Value.ToString());

@ -10,6 +10,9 @@ using FLocal.Core.DB.conditions;
namespace FLocal.Common.dataobjects { namespace FLocal.Common.dataobjects {
public class UserGroup : SqlObject<UserGroup> { public class UserGroup : SqlObject<UserGroup> {
public const string NAME_MODERATORS = "Moderators";
public const string NAME_ADMINISTRATORS = "Administrators";
public class TableSpec : ISqlObjectTableSpec { public class TableSpec : ISqlObjectTableSpec {
public const string TABLE = "UserGroups"; public const string TABLE = "UserGroups";
public const string FIELD_ID = "Id"; public const string FIELD_ID = "Id";

@ -71,6 +71,8 @@ namespace FLocal.IISHandler {
return new handlers.response.ReplyHandler(); return new handlers.response.ReplyHandler();
case "pmreply": case "pmreply":
return new handlers.response.PMReplyToPostHandler(); return new handlers.response.PMReplyToPostHandler();
case "punish":
return new handlers.response.PunishHandler();
default: default:
return new handlers.WrongUrlHandler(); return new handlers.WrongUrlHandler();
} }
@ -175,6 +177,8 @@ namespace FLocal.IISHandler {
return new handlers.request.RegisterByInviteHandler(); return new handlers.request.RegisterByInviteHandler();
case "edit": case "edit":
return new handlers.request.EditHandler(); return new handlers.request.EditHandler();
case "punish":
return new handlers.request.PunishHandler();
case "reply": case "reply":
return new handlers.request.ReplyHandler(); return new handlers.request.ReplyHandler();
case "newthread": case "newthread":

@ -71,6 +71,7 @@
<Compile Include="handlers\request\maintenance\CleanCacheHandler.cs" /> <Compile Include="handlers\request\maintenance\CleanCacheHandler.cs" />
<Compile Include="handlers\request\MarkThreadAsReadHandler.cs" /> <Compile Include="handlers\request\MarkThreadAsReadHandler.cs" />
<Compile Include="handlers\request\MigrateAccountHandler.cs" /> <Compile Include="handlers\request\MigrateAccountHandler.cs" />
<Compile Include="handlers\request\PunishHandler.cs" />
<Compile Include="handlers\request\RegisterByInviteHandler.cs" /> <Compile Include="handlers\request\RegisterByInviteHandler.cs" />
<Compile Include="handlers\request\RegisterHandler.cs" /> <Compile Include="handlers\request\RegisterHandler.cs" />
<Compile Include="handlers\request\ReturnPostHandler.cs" /> <Compile Include="handlers\request\ReturnPostHandler.cs" />
@ -98,6 +99,7 @@
<Compile Include="handlers\response\PMReplyToPostHandler.cs" /> <Compile Include="handlers\response\PMReplyToPostHandler.cs" />
<Compile Include="handlers\response\PMSendHandler.cs" /> <Compile Include="handlers\response\PMSendHandler.cs" />
<Compile Include="handlers\response\PollHandler.cs" /> <Compile Include="handlers\response\PollHandler.cs" />
<Compile Include="handlers\response\PunishHandler.cs" />
<Compile Include="handlers\response\QuickLinkHandler.cs" /> <Compile Include="handlers\response\QuickLinkHandler.cs" />
<Compile Include="handlers\response\RedirectGetHandler.cs" /> <Compile Include="handlers\response\RedirectGetHandler.cs" />
<Compile Include="handlers\response\RegisterByInviteHandler.cs" /> <Compile Include="handlers\response\RegisterByInviteHandler.cs" />

@ -29,7 +29,12 @@ namespace FLocal.IISHandler.handlers {
XElement[] result = new XElement[] { XElement[] result = new XElement[] {
new XElement("currentLocation", post.exportToXmlSimpleWithParent(context)), new XElement("currentLocation", post.exportToXmlSimpleWithParent(context)),
post.thread.exportToXml(context), post.thread.exportToXml(context),
new XElement("posts", post.exportToXml(context, new XElement("isUnread", (post.id > lastReadId).ToPlainString()))) new XElement("posts",
post.exportToXml(
context,
new XElement("isUnread", (post.id > lastReadId).ToPlainString())
)
)
}; };
post.thread.incrementViewsCounter(); post.thread.incrementViewsCounter();

@ -59,7 +59,10 @@ namespace FLocal.IISHandler.handlers {
new XElement("currentLocation", thread.exportToXmlSimpleWithParent(context)), new XElement("currentLocation", thread.exportToXmlSimpleWithParent(context)),
thread.exportToXml(context), thread.exportToXml(context),
new XElement("posts", new XElement("posts",
from post in posts select post.exportToXml(context, new XElement("isUnread", (post.id > lastReadId).ToPlainString())), from post in posts select post.exportToXml(
context,
new XElement("isUnread", (post.id > lastReadId).ToPlainString())
),
pageOuter.exportToXml(2, 5, 2) pageOuter.exportToXml(2, 5, 2)
) )
}; };

@ -0,0 +1,34 @@
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 PunishHandler : AbstractPostHandler {
protected override string templateName {
get {
return "result/MessagePunished.xslt";
}
}
protected override XElement[] Do(WebContext context) {
Post post = Post.LoadById(int.Parse(context.httprequest.Form["postId"]));
XElement postXml = post.exportToXml(context);
post.Punish(
context.session.account,
PunishmentType.LoadById(int.Parse(context.httprequest.Form["punishmentTypeId"])),
context.httprequest.Form["comment"]
);
return new XElement[] {
post.thread.board.exportToXml(context, false),
postXml
};
}
}
}

@ -0,0 +1,42 @@
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 PunishHandler : AbstractNewMessageHandler {
override protected string templateName {
get {
return "PostPunish.xslt";
}
}
override protected IEnumerable<XElement> getSpecificNewMessageData(WebContext context) {
Post post = Post.LoadById(int.Parse(context.requestParts[1]));
if(!Moderator.isModerator(context.account, post.thread.board)) throw new FLocalException(context.account.id + " is not a moderator in board " + post.thread.board.id);
if(context.account.user.id == post.poster.id) throw new FLocalException("You cannot punish your own posts");
return new XElement[] {
post.thread.board.exportToXml(context, false),
post.thread.exportToXml(context),
post.exportToXml(context),
post.latestRevision.exportToXml(context),
new XElement("layers",
from layer in PostLayer.allLayers select layer.exportToXml(context)
),
new XElement("punishmentTypes",
from punishmentType in PunishmentType.allTypes select punishmentType.exportToXml(context)
)
};
}
}
}

@ -96,6 +96,17 @@ pre
background-color:yellow !important; background-color:yellow !important;
font-weight: bold !important; font-weight: bold !important;
} }
.UG_Moderators {
color:blue !important;
background-color:white !important;
}
.UG_Administrators {
color:black !important;
background-color:white !important;
}
p { p {
margin:0em; margin:0em;
} }
.punishment {
color:red !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,98 @@
<?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="specificTitle">
<xsl:text>Ìîäåðèðîâàíèå ñîîáùåíèÿ - </xsl:text>
<xsl:value-of select="post/title"/>
</xsl:template>
<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/Punish/" name="replier">
<input type="hidden" name="postId">
<xsl:attribute name="value"><xsl:value-of select="post/id"/></xsl:attribute>
</input>
<xsl:text>Òèï:</xsl:text><br/>
<xsl:apply-templates select="punishmentTypes/punishmentType"/>
<br/>
<br/>
<xsl:text>Êîììåíòàðèé: </xsl:text>
<br/>
<input type="text" tabindex="1" name="comment" class="formboxes" size="60"/>
<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:template match="punishmentType">
<input type="radio" name="punishmentTypeId">
<xsl:attribute name="id">punishmentTypeId_<xsl:value-of select="id"/></xsl:attribute>
<xsl:attribute name="value"><xsl:value-of select="id"/></xsl:attribute>
</input>
<label>
<xsl:attribute name="for">punishmentTypeId_<xsl:value-of select="id"/></xsl:attribute>
<xsl:text> </xsl:text>
<xsl:value-of select="description"/>
<xsl:text>, âåñ íàêàçàíèÿ </xsl:text>
<xsl:value-of select="weight"/>
<xsl:text> (</xsl:text>
<xsl:value-of select="weightDescription"/>
<xsl:text>)</xsl:text>
</label>
<br/>
</xsl:template>
</xsl:stylesheet>

@ -47,9 +47,7 @@
<xsl:apply-templates select="lastPostInfo"/> <xsl:apply-templates select="lastPostInfo"/>
</td> </td>
<td width="10%" class="modcolumn" align="center"> <td width="10%" class="modcolumn" align="center">
<a>Sash</a> <xsl:apply-templates select="moderators/user" mode="userLink"/>
<xsl:text>, </xsl:text>
<a>DeadmoroZ</a>
</td> </td>
</tr> </tr>
</xsl:template> </xsl:template>

@ -176,6 +176,9 @@
</xsl:attribute> </xsl:attribute>
<xsl:attribute name="href">/User/<xsl:value-of select="id"/>/</xsl:attribute> <xsl:attribute name="href">/User/<xsl:value-of select="id"/>/</xsl:attribute>
<xsl:value-of select="name"/> <xsl:value-of select="name"/>
<xsl:if test="isModerator='true'">
<img src="/static/images/mod.gif" border="0"/>
</xsl:if>
</a> </a>
</xsl:template> </xsl:template>

@ -54,6 +54,14 @@
<td align="right"> <td align="right">
<table class="tablesurround" border="0"> <table class="tablesurround" border="0">
<tr> <tr>
<xsl:if test="isPunishmentEnabled='true'">
<td class="navigation">
<a>
<xsl:attribute name="href">/Post/<xsl:value-of select="id"/>/Punish/</xsl:attribute>
<img src="/static/images/punish.gif" border="0" alt="Ìîäåðèðîâàòü ñîîáùåíèå" title="Ìîäåðèðîâàòü ñîîáùåíèå" style="vertical-align: text-bottom" />
</a>
</td>
</xsl:if>
<td class="navigation"> <td class="navigation">
<a> <a>
<xsl:if test="$isReplyDisabled='false'"> <xsl:if test="$isReplyDisabled='false'">
@ -158,6 +166,27 @@
</xsl:if> </xsl:if>
</xsl:template> </xsl:template>
<xsl:template match="specific/punishment">
<tr>
<td>
<font size="-2" class="punishment">
<xsl:value-of select="punishmentType/description"/>
<xsl:text> (</xsl:text>
<xsl:value-of select="punishmentType/weightDescription"/>
<xsl:text>). </xsl:text>
<xsl:value-of select="comment"/>
<xsl:text> (</xsl:text>
<xsl:apply-templates select="moderator/user" mode="userLink"/>
<xsl:text>, </xsl:text>
<xsl:apply-templates select="punishmentDate/date" mode="dateTime"/>
<xsl:text>)</xsl:text>
<br/>
<br/>
</font>
</td>
</tr>
</xsl:template>
<xsl:template match="specific/thread"> <xsl:template match="specific/thread">
<tr> <tr>
<td> <td>

@ -0,0 +1,48 @@
<?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="specificTitle">Ñîîáùåíèå îòìîäåðèðîâàíî</xsl:template>
<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>

@ -0,0 +1,94 @@
<?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="specificTitle">
<xsl:text>Ìîäåðèðîâàíèå ñîîáùåíèÿ - </xsl:text>
<xsl:value-of select="post/title"/>
</xsl:template>
<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>
<td class="lighttable">
<form method="post" action="/do/Punish/" name="replier">
<input type="hidden" name="postId">
<xsl:attribute name="value"><xsl:value-of select="post/id"/></xsl:attribute>
</input>
<xsl:text>Òèï:</xsl:text><br/>
<xsl:apply-templates select="punishmentTypes/punishmentType"/>
<br/>
<br/>
<xsl:text>Êîììåíòàðèé: </xsl:text>
<br/>
<input type="text" tabindex="1" name="comment" class="formboxes" size="60"/>
<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:template match="punishmentType">
<input type="radio" name="punishmentTypeId">
<xsl:attribute name="id">punishmentTypeId_<xsl:value-of select="id"/></xsl:attribute>
<xsl:attribute name="value"><xsl:value-of select="id"/></xsl:attribute>
</input>
<label>
<xsl:attribute name="for">punishmentTypeId_<xsl:value-of select="id"/></xsl:attribute>
<xsl:text> </xsl:text>
<xsl:value-of select="description"/>
<xsl:text>, âåñ íàêàçàíèÿ </xsl:text>
<xsl:value-of select="weight"/>
<xsl:text> (</xsl:text>
<xsl:value-of select="weightDescription"/>
<xsl:text>)</xsl:text>
</label>
<br/>
</xsl:template>
</xsl:stylesheet>

@ -182,6 +182,9 @@
</xsl:attribute> </xsl:attribute>
<xsl:attribute name="href">/User/<xsl:value-of select="id"/>/</xsl:attribute> <xsl:attribute name="href">/User/<xsl:value-of select="id"/>/</xsl:attribute>
<xsl:value-of select="name"/> <xsl:value-of select="name"/>
<xsl:if test="isModerator='true'">
<xsl:text>^M</xsl:text>
</xsl:if>
</a> </a>
</xsl:template> </xsl:template>

@ -31,6 +31,13 @@
</xsl:if> </xsl:if>
<font class="small" style="padding-left:2em"><xsl:apply-templates select="postDate/date" mode="dateTime"/></font> <font class="small" style="padding-left:2em"><xsl:apply-templates select="postDate/date" mode="dateTime"/></font>
<span style="padding-left:1em"> <span style="padding-left:1em">
<xsl:if test="isPunishmentEnabled='true'">
<xsl:text> | </xsl:text>
<a class="mod">
<xsl:attribute name="href">/Post/<xsl:value-of select="id"/>/Punish/</xsl:attribute>
<xsl:text>Mod</xsl:text>
</a>
</xsl:if>
<xsl:text> | </xsl:text> <xsl:text> | </xsl:text>
<a> <a>
<xsl:if test="$isReplyDisabled='false'"> <xsl:if test="$isReplyDisabled='false'">

@ -0,0 +1,48 @@
<?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="specificTitle">Ñîîáùåíèå îòìîäåðèðîâàíî</xsl:template>
<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