diff --git a/Builder/IISMainHandler/build.txt b/Builder/IISMainHandler/build.txt
index 7ff9fa9..fb9a769 100644
--- a/Builder/IISMainHandler/build.txt
+++ b/Builder/IISMainHandler/build.txt
@@ -1 +1 @@
-1227
\ No newline at end of file
+1233
\ No newline at end of file
diff --git a/Common/Common.csproj b/Common/Common.csproj
index b79817d..cb37d15 100644
--- a/Common/Common.csproj
+++ b/Common/Common.csproj
@@ -108,6 +108,7 @@
+
diff --git a/Common/TableManager.cs b/Common/TableManager.cs
index 2e3636a..3979455 100644
--- a/Common/TableManager.cs
+++ b/Common/TableManager.cs
@@ -26,6 +26,7 @@ namespace FLocal.Common {
dataobjects.PunishmentLayerChange.TableSpec.instance,
dataobjects.PunishmentTransfer.TableSpec.instance,
dataobjects.PunishmentType.TableSpec.instance,
+ dataobjects.Restriction.TableSpec.instance,
dataobjects.QuickLink.TableSpec.instance,
dataobjects.Revision.TableSpec.instance,
dataobjects.Session.TableSpec.instance,
diff --git a/Common/actions/ChangeSet.cs b/Common/actions/ChangeSet.cs
index 740f030..ce40aee 100644
--- a/Common/actions/ChangeSet.cs
+++ b/Common/actions/ChangeSet.cs
@@ -39,6 +39,7 @@ namespace FLocal.Common.actions {
dataobjects.Punishment.TableSpec.TABLE,
dataobjects.PunishmentTransfer.TableSpec.TABLE,
dataobjects.PunishmentLayerChange.TableSpec.TABLE,
+ dataobjects.Restriction.TableSpec.TABLE,
dataobjects.TexImage.TableSpec.TABLE,
dataobjects.Session.TableSpec.TABLE,
}
diff --git a/Common/dataobjects/Board.cs b/Common/dataobjects/Board.cs
index 62ad2bf..7b470ec 100644
--- a/Common/dataobjects/Board.cs
+++ b/Common/dataobjects/Board.cs
@@ -435,5 +435,24 @@ namespace FLocal.Common.dataobjects {
}
}
+ public XElement exportLayersInfoForUser(UserContext context) {
+ Dictionary restrictionData = new Dictionary();
+ if(context.account != null) {
+ restrictionData = Restriction.GetRestrictionData(context.account.user, this);
+ }
+ return new XElement("layers",
+ from layer in PostLayer.allLayers
+ select layer.exportToXml(
+ context,
+ new XElement("isRestricted",
+ (restrictionData.ContainsKey(layer.id) && restrictionData[layer.id].CompareTo(DateTime.Now) >= 0).ToPlainString()
+ ),
+ new XElement("restrictionExpires",
+ restrictionData.ContainsKey(layer.id) ? restrictionData[layer.id].ToXml() : null
+ )
+ )
+ );
+ }
+
}
}
diff --git a/Common/dataobjects/Post.cs b/Common/dataobjects/Post.cs
index 3da57e2..a8e0c9a 100644
--- a/Common/dataobjects/Post.cs
+++ b/Common/dataobjects/Post.cs
@@ -608,6 +608,10 @@ namespace FLocal.Common.dataobjects {
newMessage.conversation.markAsRead(account, newMessage, newMessage);
}
+ HashSet punishmentsBoards = new HashSet(from punishment in this.punishments select punishment.originalBoardId);
+ foreach(int boardId in punishmentsBoards) {
+ Restriction.RecalculateRestrictions(Board.LoadById(boardId), this.poster);
+ }
}
}
}
diff --git a/Common/dataobjects/PostLayer.cs b/Common/dataobjects/PostLayer.cs
index 45b4fd7..8571999 100644
--- a/Common/dataobjects/PostLayer.cs
+++ b/Common/dataobjects/PostLayer.cs
@@ -19,6 +19,7 @@ namespace FLocal.Common.dataobjects {
public const string TABLE = "Layers";
public const string FIELD_ID = "Id";
public const string FIELD_NAME = "Name";
+ public const string FIELD_MAXPUNISHMENTS = "MaxPunishments";
public static readonly TableSpec instance = new TableSpec();
public string name { get { return TABLE; } }
public string idName { get { return FIELD_ID; } }
@@ -35,8 +36,17 @@ namespace FLocal.Common.dataobjects {
}
}
+ private int? _maxPunishments;
+ public int? maxPunishments {
+ get {
+ this.LoadIfNotLoaded();
+ return this._maxPunishments;
+ }
+ }
+
protected override void doFromHash(Dictionary data) {
this._name = data[TableSpec.FIELD_NAME];
+ this._maxPunishments = Util.ParseInt(data[TableSpec.FIELD_MAXPUNISHMENTS]);
}
private static readonly object allLayers_Locker = new object();
@@ -46,11 +56,11 @@ namespace FLocal.Common.dataobjects {
from id in Cache>.instance.get(
allLayers_Locker,
() => {
- IEnumerable ids = from stringId in Config.instance.mainConnection.LoadIdsByConditions(
+ IEnumerable ids = (from stringId in Config.instance.mainConnection.LoadIdsByConditions(
TableSpec.instance,
new FLocal.Core.DB.conditions.EmptyCondition(),
Diapasone.unlimited
- ) select int.Parse(stringId);
+ ) select int.Parse(stringId)).ToList();
PostLayer.LoadByIds(ids);
return ids;
}
@@ -64,11 +74,15 @@ namespace FLocal.Common.dataobjects {
Cache>.instance.delete(allLayers_Locker);
}
- public XElement exportToXml(UserContext context) {
- return new XElement("layer",
+ public XElement exportToXml(UserContext context, params XElement[] additional) {
+ XElement result = new XElement("layer",
new XElement("id", this.id),
new XElement("name", this.name)
);
+ if(additional.Length > 0) {
+ result.Add(additional);
+ }
+ return result;
}
}
diff --git a/Common/dataobjects/Punishment.cs b/Common/dataobjects/Punishment.cs
index 56eb7ec..fab6666 100644
--- a/Common/dataobjects/Punishment.cs
+++ b/Common/dataobjects/Punishment.cs
@@ -185,5 +185,36 @@ namespace FLocal.Common.dataobjects {
);
}
+ public static IEnumerable getEffectivePunishments(User user, Board board) {
+ return
+ from stringId in Config.instance.mainConnection.LoadIdsByConditions(
+ TableSpec.instance,
+ new ComplexCondition(
+ ConditionsJoinType.AND,
+ new ComparisonCondition(
+ TableSpec.instance.getColumnSpec(TableSpec.FIELD_OWNERID),
+ ComparisonType.EQUAL,
+ user.id.ToString()
+ ),
+ new ComparisonCondition(
+ TableSpec.instance.getColumnSpec(TableSpec.FIELD_ORIGINALBOARDID),
+ ComparisonType.EQUAL,
+ board.id.ToString()
+ ),
+ new ComparisonCondition(
+ TableSpec.instance.getColumnSpec(TableSpec.FIELD_ISWITHDRAWED),
+ ComparisonType.EQUAL,
+ "0"
+ ),
+ new ComparisonCondition(
+ TableSpec.instance.getColumnSpec(TableSpec.FIELD_EXPIRES),
+ ComparisonType.GREATEROREQUAL,
+ DateTime.Now.ToUTCString()
+ )
+ ),
+ Diapasone.unlimited
+ ) select Punishment.LoadById(int.Parse(stringId));
+ }
+
}
}
diff --git a/Common/dataobjects/Restriction.cs b/Common/dataobjects/Restriction.cs
new file mode 100644
index 0000000..b7bd023
--- /dev/null
+++ b/Common/dataobjects/Restriction.cs
@@ -0,0 +1,237 @@
+п»ї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 Restriction : SqlObject {
+
+ public class TableSpec : ISqlObjectTableSpec {
+ public const string TABLE = "Restrictions";
+ public const string FIELD_ID = "Id";
+ public const string FIELD_USERID = "UserId";
+ public const string FIELD_BOARDID = "BoardId";
+ public const string FIELD_DATA = "Data";
+ 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 restriction = Restriction.LoadById(id);
+ byUser_Recalculate(restriction.userId);
+ byBoard_Recalculate(restriction.boardId);
+ lock(restrictionId_cache) {
+ if(!restrictionId_cache.ContainsKey(restriction.userId)) restrictionId_cache[restriction.userId] = new Dictionary();
+ restrictionId_cache[restriction.userId][restriction.boardId] = restriction.id;
+ }
+ }
+ }
+
+ protected override ISqlObjectTableSpec table { get { return TableSpec.instance; } }
+
+ private int _userId;
+ public int userId {
+ get {
+ this.LoadIfNotLoaded();
+ return this._userId;
+ }
+ }
+ public User user {
+ get {
+ return User.LoadById(this.userId);
+ }
+ }
+
+ private int _boardId;
+ public int boardId {
+ get {
+ this.LoadIfNotLoaded();
+ return this._boardId;
+ }
+ }
+ public Board board {
+ get {
+ return Board.LoadById(this.boardId);
+ }
+ }
+
+ private Dictionary _data;
+ public Dictionary data {
+ get {
+ this.LoadIfNotLoaded();
+ return this._data;
+ }
+ }
+
+ protected override void doFromHash(Dictionary data) {
+ this._userId = int.Parse(data[TableSpec.FIELD_USERID]);
+ this._boardId = int.Parse(data[TableSpec.FIELD_BOARDID]);
+ this._data = (
+ from part in data[TableSpec.FIELD_DATA].Split(new char[] {';'}, StringSplitOptions.RemoveEmptyEntries)
+ let subparts = part.Split(new char[] {':'}, 2)
+ select new KeyValuePair(
+ int.Parse(subparts[0]),
+ new DateTime(long.Parse(subparts[1]))
+ )
+ ).ToDictionary();
+ }
+
+ private static readonly Dictionary> restrictionId_cache = new Dictionary>();
+ public static Restriction GetRestriction(User user, Board board) {
+
+ if(!restrictionId_cache.ContainsKey(user.id) || !restrictionId_cache[user.id].ContainsKey(board.id)) {
+ lock(restrictionId_cache) {
+ if(!restrictionId_cache.ContainsKey(user.id) || !restrictionId_cache[user.id].ContainsKey(board.id)) {
+ if(!restrictionId_cache.ContainsKey(user.id)) restrictionId_cache[user.id] = new Dictionary();
+
+ List ids = Config.instance.mainConnection.LoadIdsByConditions(
+ TableSpec.instance,
+ new ComplexCondition(
+ ConditionsJoinType.AND,
+ new ComparisonCondition(
+ TableSpec.instance.getColumnSpec(TableSpec.FIELD_USERID),
+ ComparisonType.EQUAL,
+ user.id.ToString()
+ ),
+ new ComparisonCondition(
+ TableSpec.instance.getColumnSpec(TableSpec.FIELD_BOARDID),
+ ComparisonType.EQUAL,
+ board.id.ToString()
+ )
+ ),
+ Diapasone.unlimited
+ );
+ if(ids.Count < 1) {
+ restrictionId_cache[user.id][board.id] = null;
+ } else {
+ restrictionId_cache[user.id][board.id] = Restriction.LoadById(int.Parse(ids.Single())).id;
+ }
+
+ }
+ }
+ }
+
+ if(restrictionId_cache[user.id][board.id].HasValue) {
+ return Restriction.LoadById(restrictionId_cache[user.id][board.id].Value);
+ } else {
+ return null;
+ }
+ }
+
+ public static Dictionary GetRestrictionData(User user, Board board) {
+ Restriction restriction = GetRestriction(user, board);
+ if(restriction != null) {
+ return restriction.data;
+ } else {
+ return new Dictionary();
+ }
+ }
+
+ private static readonly Dictionary> byBoard_cache = new Dictionary>();
+ 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 Restriction.LoadById(int.Parse(stringId)).id;
+ }
+ }
+ public static IEnumerable GetRestrictedUsers(Board board) {
+ if(!byBoard_cache.ContainsKey(board.id)) {
+ byBoard_Recalculate(board.id);
+ }
+ return from id in byBoard_cache[board.id] select Restriction.LoadById(id).user;
+ }
+
+ private static readonly Dictionary> byUser_cache = new Dictionary>();
+ private static void byUser_Recalculate(int userId) {
+ lock(byUser_cache) {
+ byUser_cache[userId] =
+ from stringId in Config.instance.mainConnection.LoadIdsByConditions(
+ TableSpec.instance,
+ new ComparisonCondition(
+ TableSpec.instance.getColumnSpec(TableSpec.FIELD_USERID),
+ ComparisonType.EQUAL,
+ userId.ToString()
+ ),
+ Diapasone.unlimited
+ )
+ select Restriction.LoadById(int.Parse(stringId)).id;
+ }
+ }
+ public static IEnumerable GetRestrictions(User user) {
+ if(!byUser_cache.ContainsKey(user.id)) {
+ byUser_Recalculate(user.id);
+ }
+ return from id in byUser_cache[user.id] select Restriction.LoadById(id).board;
+ }
+
+ public static void RecalculateRestrictions(Board board, User user) {
+ List punishments = (from punishment in Punishment.getEffectivePunishments(user, board) where punishment.punishmentType.weight > 0 orderby punishment.expires descending select punishment).ToList();
+
+ Dictionary layer2expirationDate = new Dictionary();
+ foreach(var layer in PostLayer.allLayers) {
+
+ if(!layer.maxPunishments.HasValue) continue;
+
+ bool restricted = false;
+ int accumulated = 0;
+ DateTime? expirationDate = null;
+ foreach(var punishment in punishments) {
+ accumulated += punishment.punishmentType.weight;
+ if(accumulated >= layer.maxPunishments.Value) {
+ expirationDate = punishment.expires;
+ restricted = true;
+ break;
+ }
+ }
+
+ if(restricted) {
+ layer2expirationDate[layer.id] = expirationDate.Value;
+ }
+ }
+
+ string data = (from kvp in layer2expirationDate select string.Format("{0}:{1}", kvp.Key, kvp.Value.Ticks)).Join(";");
+
+ ChangeSetUtil.ApplyChanges(
+ new InsertOrUpdateChange(
+ TableSpec.instance,
+ new Dictionary {
+ { TableSpec.FIELD_BOARDID, new ScalarFieldValue(board.id.ToString()) },
+ { TableSpec.FIELD_USERID, new ScalarFieldValue(user.id.ToString()) },
+ { TableSpec.FIELD_DATA, new ScalarFieldValue(data) },
+ },
+ new Dictionary {
+ { TableSpec.FIELD_DATA, new ScalarFieldValue(data) },
+ },
+ new ComplexCondition(
+ ConditionsJoinType.AND,
+ new ComparisonCondition(
+ TableSpec.instance.getColumnSpec(TableSpec.FIELD_BOARDID),
+ ComparisonType.EQUAL,
+ board.id.ToString()
+ ),
+ new ComparisonCondition(
+ TableSpec.instance.getColumnSpec(TableSpec.FIELD_USERID),
+ ComparisonType.EQUAL,
+ user.id.ToString()
+ )
+ )
+ )
+ );
+ }
+
+ }
+}
diff --git a/Common/dataobjects/User.cs b/Common/dataobjects/User.cs
index 4862d4f..8c44aaa 100644
--- a/Common/dataobjects/User.cs
+++ b/Common/dataobjects/User.cs
@@ -226,6 +226,8 @@ namespace FLocal.Common.dataobjects {
}
public PostLayer getActualLayer(Board board, PostLayer desiredLayer) {
+ Dictionary restrictionData = Restriction.GetRestrictionData(this, board);
+ if(restrictionData.ContainsKey(desiredLayer.id) && (restrictionData[desiredLayer.id].CompareTo(DateTime.Now) >= 0)) throw new FLocalException("You're restricted from posting in this layer until " + restrictionData[desiredLayer.id].ToString());
return desiredLayer;
}
diff --git a/IISMainHandler/handlers/response/CreateThreadHandler.cs b/IISMainHandler/handlers/response/CreateThreadHandler.cs
index 3eab456..3a13c9a 100644
--- a/IISMainHandler/handlers/response/CreateThreadHandler.cs
+++ b/IISMainHandler/handlers/response/CreateThreadHandler.cs
@@ -23,9 +23,7 @@ namespace FLocal.IISHandler.handlers.response {
return new XElement[] {
board.exportToXml(context, Board.SubboardsOptions.None),
- new XElement("layers",
- from layer in PostLayer.allLayers select layer.exportToXml(context)
- ),
+ board.exportLayersInfoForUser(context),
};
}
}
diff --git a/IISMainHandler/handlers/response/EditHandler.cs b/IISMainHandler/handlers/response/EditHandler.cs
index 59a6e0a..cbd72d9 100644
--- a/IISMainHandler/handlers/response/EditHandler.cs
+++ b/IISMainHandler/handlers/response/EditHandler.cs
@@ -26,9 +26,7 @@ namespace FLocal.IISHandler.handlers.response {
post.thread.exportToXml(context),
post.exportToXml(context),
post.latestRevision.exportToXml(context),
- new XElement("layers",
- from layer in PostLayer.allLayers select layer.exportToXml(context)
- ),
+ post.thread.board.exportLayersInfoForUser(context),
};
}
}
diff --git a/IISMainHandler/handlers/response/ReplyHandler.cs b/IISMainHandler/handlers/response/ReplyHandler.cs
index ddd13b8..805fa27 100644
--- a/IISMainHandler/handlers/response/ReplyHandler.cs
+++ b/IISMainHandler/handlers/response/ReplyHandler.cs
@@ -34,9 +34,7 @@ namespace FLocal.IISHandler.handlers.response {
post.thread.board.exportToXml(context, Board.SubboardsOptions.None),
post.thread.exportToXml(context),
post.exportToXml(context),
- new XElement("layers",
- from layer in PostLayer.allLayers select layer.exportToXml(context)
- ),
+ post.thread.board.exportLayersInfoForUser(context),
new XElement("quoted", quoted),
};
}
diff --git a/templates/Full/NewThread.xslt b/templates/Full/NewThread.xslt
index 31b6a77..0a1eafa 100644
--- a/templates/Full/NewThread.xslt
+++ b/templates/Full/NewThread.xslt
@@ -38,7 +38,7 @@
Слой сообщения:
diff --git a/templates/Full/PostEdit.xslt b/templates/Full/PostEdit.xslt
index 0d41524..f974aa6 100644
--- a/templates/Full/PostEdit.xslt
+++ b/templates/Full/PostEdit.xslt
@@ -40,7 +40,7 @@
Слой сообщения:
diff --git a/templates/Full/PostPunish.xslt b/templates/Full/PostPunish.xslt
index a02df30..067216e 100644
--- a/templates/Full/PostPunish.xslt
+++ b/templates/Full/PostPunish.xslt
@@ -51,7 +51,7 @@
diff --git a/templates/Full/PostReply.xslt b/templates/Full/PostReply.xslt
index 8119fdc..8a604ae 100644
--- a/templates/Full/PostReply.xslt
+++ b/templates/Full/PostReply.xslt
@@ -50,7 +50,7 @@
Слой сообщения:
diff --git a/templates/Full/elems/TextEditor.xslt b/templates/Full/elems/TextEditor.xslt
index 25d9a6d..96e7fdb 100644
--- a/templates/Full/elems/TextEditor.xslt
+++ b/templates/Full/elems/TextEditor.xslt
@@ -319,11 +319,32 @@ function insertInBody(str) {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/Lite/NewThread.xslt b/templates/Lite/NewThread.xslt
index 54e6150..20e167c 100644
--- a/templates/Lite/NewThread.xslt
+++ b/templates/Lite/NewThread.xslt
@@ -33,9 +33,7 @@
Слой сообщения:
diff --git a/templates/Lite/PostEdit.xslt b/templates/Lite/PostEdit.xslt
index d5a6fe9..1ff2dc0 100644
--- a/templates/Lite/PostEdit.xslt
+++ b/templates/Lite/PostEdit.xslt
@@ -35,7 +35,7 @@
Слой сообщения:
diff --git a/templates/Lite/PostPunish.xslt b/templates/Lite/PostPunish.xslt
index d461271..f7b3f2e 100644
--- a/templates/Lite/PostPunish.xslt
+++ b/templates/Lite/PostPunish.xslt
@@ -46,7 +46,7 @@
diff --git a/templates/Lite/PostReply.xslt b/templates/Lite/PostReply.xslt
index 25842a5..10969d4 100644
--- a/templates/Lite/PostReply.xslt
+++ b/templates/Lite/PostReply.xslt
@@ -45,7 +45,7 @@
Слой сообщения:
diff --git a/templates/Lite/elems/TextEditor.xslt b/templates/Lite/elems/TextEditor.xslt
index b7a64ba..7e14da9 100644
--- a/templates/Lite/elems/TextEditor.xslt
+++ b/templates/Lite/elems/TextEditor.xslt
@@ -47,11 +47,32 @@ function insertInBody(str) {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file