Posting rules

In this section I present the Rule abstract base-class, and how the Can Post adaptor is used to collect the rules for each group. I then provide an example of chaining rules.

Rule abstract base-class

A rule is an adaptor. It takes a user [1] and a group [2]. The BaseRule abstract base-class provides most of what is required to create a rule.

class BaseRule(userInfo, group)
Parameters:
  • userInfo (IGSUserInfo) – The user that is being tested by the rule.
  • group – The group the user is being tested for.
__doc__

(Abstract property: sub-classes must provide a weight.) The documentation on the rule, which is shown on the page rules.html in each group.

weight

(Abstract property: sub-classes must provide a weight.) An integer that has two related functions. First, it is used as the sort-key to determine the order that the rules are checked (see can post adaptor below). Second, the statusNum is set to this value to uniquely identifies the rule. (No two rules should have the same weight as this can lead to ambiguity.)

check()

(Abstract method: sub-classes must supply check().) Perform the actual check to see if a user can post to a group. Based on the result it sets the values in the dictionary self.s:

checked:
True after the check, False initially.
canPost:
True if the user can post (see canPost).
status:
A string that summarises the status (see status).
statusNum:
A number representing the status. Normally this is set to weight (see statusNum).
canPost

(Read only.) A Boolean value that is True if this rule thinks that the user can post the the group.

status

(Read only.) A Unicode value that summaries why the user should be prevented from posting to the group.

statusNum

(Read only.) An integer that is one of three values:

  • -1 if it is unknown whether the user can post to the group (canPost will be False in this case),
  • 0 if the user can post to the group (canPost is True), and
  • Set to the weight value if the user cannot post to the group (canPost is False).

Example

Most rules only provide a doc-string, the BaseRule.weight attribute, and BaseRule.check() method. For example, the BlockedFromPosting rule checks to see if the identifier of the user is in the blocked_members property of the mailing list. It then sets the canPost, status and statusNum values of the self.s dictionary accordingly. Finally, it sets self.s['checked'] to True to prevent the system from performing the check more than once.

class BlockedFromPosting(BaseRule):
  '''A person will be prevented from posting if he or she is
  explicitly blocked by an administrator of the group.'''
  weight = 10

  def check(self):
      if not self.s['checked']:
          ml = self.mailingList
          blockedMemberIds = ml.getProperty('blocked_members', [])
          if (self.userInfo.id in blockedMemberIds):
              self.s['canPost'] = False
              self.s['status'] = 'blocked from posting'
              self.s['statusNum'] = self.weight
          else:
              self.s['canPost'] = True
              self.s['status'] = 'not blocked from posting'
              self.s['statusNum'] = 0
          self.s['checked'] = True

The ZCML sets up each rule as an adaptor [3]. It adapts a userInfo and the specific group type and provides an IGSCanPostRule. The adaptor must be a named adaptor, as multiple rules are used for each group. The names are also shown on the rules.html page in each group.

<adapter
  name="Blocked from Posting"
  for="Products.CustomUserFolder.interfaces.IGSUserInfo
       gs.group.base.interfaces.IGSGroupMarker"
  provides=".interfaces.IGSCanPostRule"
  factory=".rules.BlockedFromPosting" />

Can post adaptor

The CanPost adaptor looks very very very much like the adaptor for the rule abstract base-class. However, rather than providing a single rule it aggregates all the rules for a group, giving the final answer as to weather the user can post. It provides the answer using the same three properties as the rules: CanPost.canPost, CanPost.status and CanPost.statusNum.

class CanPost(userInfo, group)
Parameters:
  • userInfo (IGSUserInfo) – The user that is being tested.
  • group – The group the user is being tested for.
canPost

True if the user can post to the group.

status

A description of the reason the user cannot post, for the most important reason (the rule with lowest weight; see BaseRule.weight). Undefined if canPost is True.

statusNum

A numeric description of the reason the user cannot post, for the most important reason (the rule with lowest weight; see BaseRule.weight). Undefined if canPost is True.

Only one CanPost adaptor is needed for all group-types. That is because the it implements the strategy pattern to determine the applicable rules.

Chaining Rules

The core GroupServer group types use the following inheritance hierarchy for their interfaces:

gs.group.base.interfaces.IGSGroupMarker
   △        △
   │        │
   │       gs.group.type.discussion.interfaces.IGSDiscussionGroup
   │        △
   │        │
   │       gs.group.type.announcement.interfaces.IGSAnnouncementGroup
   │
  gs.group.type.support.interfaces.IGSSupportGroup

This product (gs.group.member.canpost) provides one rule for the IGSGroupMarker — which prevents people who have been explicitly blocked from posting (see the example above). All other group types inherit this rule because their marker-interfaces inherit from the IGSGroupMarker.

The discussion group (IGSDiscussionGroup) provides the most rules: six in all. All these rules are inherited by the announcement group because its marker-interface (IGSAnnouncementGroup) inherits from the discussion group. The announcement group also provides its own rule, to ensure that only posting members can post.

The support group (IGSSupportGroup) provides no extra rules, so it just has the rule that is provided by this package for all the IGSGroupMarker groups.

[1]The user is almost always a Products.CustomUserFolder.interfaces.IGSUserInfo instance.
[2]The group will be a group-folder that has been marked with an interface that is generally specific to the type of group.
[3]It easier to use ZCML to set up the adaptor for each rule because rules can be mixed and matched by different group-types. By using ZCML the mixing-and-matching can be done with very little Python code.