Changeset 73

Show
Ignore:
Timestamp:
08/06/07 23:37:10 (1 year ago)
Author:
pragma
Message:

Dirty commit, refactored code.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/tracforums/models/__init__.py

    r33 r73  
    1 from tracforums.models.main import * 
     1from tracforums.models.main import MainController 
     2from tracforums.models.message import * 
     3from tracforums.models.topic import * 
    24from tracforums.models.forum import * 
    3 from tracforums.models.topic import * 
    4 from tracforums.models.message import * 
    55from tracforums.models.profile import * 
    6 from tracforums.models.profiles import * 
    76from tracforums.models.avatar import * 
    8 from tracforums.models.recent import * 
    9 from tracforums.models.ajax import * 
    107from tracforums.models.project import * 
  • trunk/tracforums/models/avatar.py

    r71 r73  
    55 
    66from tracforums.model import * 
     7from tracforums.orm import * 
     8 
     9""" 
     10    Avatar 
     11""" 
     12AvatarModel = ORMSchema( 
     13    tablename = "avatar", 
     14    columns   = { 
     15        "id": ORMKey(type='int', auto_increment = True), 
     16        "username":  ORMColumn(), 
     17        "mimetype":  ORMColumn(), 
     18        "name":      ORMColumn(), 
     19        "avatarid":  ORMAlias(sql="id"), 
     20    } 
     21) 
     22 
     23class AvatarController(Controller):         
     24    mimeMap = { 
     25        'bmp':  'image/bmp', 
     26        'cod':  'image/cis-cod', 
     27        'gif':  'image/gif',     
     28        'ief':  'image/ief', 
     29        'jpe':  'image/jpeg',    
     30        'jpeg': 'image/jpeg', 
     31        'jpg':  'image/jpeg', 
     32        'jfif': 'image/pipeg', 
     33        'svg':  'image/svg+xml',     
     34        'tif':  'image/tiff', 
     35        'tiff': 'image/tiff', 
     36        'ras':  'image/x-cmu-raster', 
     37        'cmx':  'image/x-cmx', 
     38        'ico':  'image/x-icon', 
     39        'png':  'iimage/png', 
     40        'pnm':  'image/x-portable-anymap', 
     41        'pbm':  'image/x-portable-bitmap', 
     42        'pgm':  'image/x-portable-graymap', 
     43        'ppm':  'image/x-portable-pixmap',           
     44        'rgb':  'image/x-rgb', 
     45        'xbm':  'image/x-xbitmap', 
     46        'xpm':  'image/x-xpixmap', 
     47        'xwd':  'image/x-xwindowdump'  
     48    } 
     49     
     50    def __init__(self,req,env,db): 
     51        Controller.__init__(self,req,env,db) 
     52         
     53    def doView(self): 
     54        args = self.getArgs() 
     55        avatar = AvatarModel(self.db,self).load({"id":args["viewid"]}) 
     56         
     57        # get the target path 
     58        targetPath = os.path.join(args["profilepath"],'avatar') 
     59         
     60        # make the directory if it's not there 
     61        if not os.access(targetPath, os.F_OK): 
     62            os.makedirs(targetPath) 
     63         
     64        # extend the path to point to the correct file 
     65        targetPath = os.path.join(targetPath,str(avatar.id) + ".data")   
     66               
     67        # render 
     68        self.renderAsFile(targetPath,avatar.mimetype) 
     69        return "", None         
     70         
     71    templates = { 
     72        "img":     doView, 
     73        "default": doView 
     74    } 
     75 
     76#################################################### 
     77 
    778 
    879class Avatar(ISecurityContext,IModel,IUIModel): 
  • trunk/tracforums/models/forum.py

    r72 r73  
    1 import time 
     1print "Importing forum.py" 
     2 
    23from tracforums.model import * 
    3  
    4 class Forum(ISecurityContext,IModel,IUIModel): 
    5     def __init__(self,db): 
    6         self.db = db 
    7         self.instance = {} 
    8         self.moderators = [] 
    9         self.targetForum = None 
    10          
    11     def _getTargetForum(self,context): 
    12         if not self.targetForum: 
    13             self.targetForum = self.getById(context.getArgs()["targetForumId"],context) 
    14              
    15         return self.targetForum         
    16          
    17     def _getDisplayAssets(self,context): 
    18         assets = {} 
    19          
    20         assets['thisProjectId'] = context.getProjectId() 
    21          
    22         self.instance['descriptionhtml'] = context.wikiToOneLiner(self.instance['description']) 
    23         self.instance['hiddenhtml'] = context.boolToHTML(self.instance['hidden']) 
    24         self.instance['lockedhtml'] = context.boolToHTML(self.instance['locked'])         
    25         self.instance['canAppend'] = self.canAppend(context) 
    26         self.instance['canModify'] = self.canModify(context) 
    27         assets['forum'] = self.instance 
    28          
    29         return assets 
    30            
    31     ### IUIModel Methods ### 
    32              
    33     def getViewTemplate(self,context): 
    34         assets = self._getDisplayAssets(context) 
    35          
    36         from tracforums.models.topic import Topic 
    37         assets['topics'] = Topic(self.db).getList(self.instance['id'],context) 
    38          
    39         if self.canModify(context):            
    40             #get forum listing 
    41             from tracforums.models.forum import Forum 
    42             assets['thisForumId'] = self.instance['id'] 
    43             assets['forums'] = self.getList(context)         
    44              
    45             #get project listing 
    46             from tracforums.models.project import Project 
    47             projectObj = Project(self.db) 
    48             assets['projects'] = projectObj.getSimpleList(context) 
    49              
    50             #get permissions 
    51             assets['canMoveTopics'] = self._canModifyContents(self.instance,context) 
    52             assets['canDeleteTopics'] = self._canModifyContents(self.instance,context) 
    53              
    54         return "forum/view.cs",assets 
    55          
    56     def getEditTemplate(self,context):             
    57         assets = self._getDisplayAssets(context) 
    58         assets['forum'] = self.instance 
    59          
    60         assets['forum_description_rows'] = context.getSessionVar('forum_description_rows',8) 
    61         assets['categories'] = self._getCategories(context) 
    62          
    63         return "forum/edit.cs",assets  
    64           
    65     def getSaveTemplate(self,context): 
    66         from tracforums.models.profile import Profile 
    67         profile = Profile(self.db).getById(context.getAuthname(),context) 
    68          
    69         if profile['isexpert']: 
    70             context.forumRedirect("/forum/" + str(self.instance['id'])); 
    71         else: 
    72             assets = self._getDisplayAssets(context) 
    73             self.instance['descriptionhtml'] = context.wikiToOneLiner(self.instance['description']) 
    74             self.instance['hiddenhtml'] = context.boolToHTML(self.instance['hidden']) 
    75             self.instance['lockedhtml'] = context.boolToHTML(self.instance['locked']) 
    76             assets['forum'] = self.instance    
    77             return "forum/save.cs",assets 
    78                  
    79     def getNewTemplate(self,context): 
    80         return self.getEditTemplate(context) 
    81          
    82     def getCreateTemplate(self,context): 
    83         return self.getSaveTemplate(context)                
    84          
    85     def getDeleteTemplate(self,context): 
    86         args = context.getArgs() 
    87         assets = self._getDisplayAssets(context) 
    88          
    89         # forward form data 
    90         selectedTopics = args['selectedTopics'] 
    91         assets['selectedTopics'] = selectedTopics 
    92          
    93         # load the set for display 
    94         from tracforums.models.topic import Topic 
    95         assets['selection'] = Topic(self.db).getSelectionList(selectedTopics.split(','),context) 
    96                  
    97         return "forum/delete.cs", assets 
    98          
    99     def getDeletedTemplate(self,context): 
    100         from tracforums.models.profile import Profile 
    101         profile = Profile(self.db).getById(context.getAuthname(),context) 
    102          
    103         if profile['isexpert']: 
    104             context.forumRedirect("/forum/" + str(self.instance["id"])); 
    105         else: 
    106             args = context.getArgs() 
    107             assets = self._getDisplayAssets(context) 
    108              
    109             # forward form data 
    110             selectedTopics = args['selectedTopics'] 
    111             assets['selectedTopics'] = selectedTopics 
    112              
    113             return "forum/deleted.cs", assets                
    114  
    115     def getMoveTemplate(self,context): 
    116         assets = self._getDisplayAssets(context) 
    117         args = context.getArgs() 
    118          
    119         # forward form data 
    120         selectedTopics = args['selectedTopics'] 
    121         targetForumId = args['targetForumId'] 
    122         assets['selectedTopics'] = selectedTopics 
    123         assets['targetForumId'] = targetForumId 
    124                          
    125         # load the set for display 
    126         from tracforums.models.topic import Topic 
    127         assets['selection'] = Topic(self.db).getSelectionList(selectedTopics.split(','),context) 
    128  
    129         # get the target forum 
    130         assets['targetForum'] = self.getById(targetForumId,context) 
    131          
    132         return "forum/move.cs", assets 
    133          
    134     def getMovedTemplate(self,context): 
    135         assets = self._getDisplayAssets(context) 
    136         args = context.getArgs() 
    137          
    138         # forward form data 
    139         selectedTopics = args['selectedTopics'] 
    140         targetForumId = args['targetForumId'] 
    141         assets['selectedTopics'] = selectedTopics 
    142         assets['targetForumId'] = targetForumId 
    143                  
    144         # get the target forum 
    145         assets['targetForum'] = self.getById(targetForumId,context) 
    146          
    147         return "forum/moved.cs", assets       
    148          
    149     ### ISecurityContext Methods ### 
    150      
    151     def canView(self,context): 
    152         # viewing a forum is restricted to: guest 
    153         # viewing a hidden forum is restricted to: admin, moderators 
    154         if self.instance["hidden"]: 
    155             if context.isForumAdmin(): 
    156                 return True 
    157             if context.isForumUser() and (context.getAuthname() in self.instance["moderatorsArr"]): 
    158                 return True 
    159             else: 
    160                 return False 
    161         else: 
    162             return context.isForumGuest() 
    163                     
    164     def canCreate(self,context): 
    165         # creating a forum is restricted to: admin 
    166         return context.isForumAdmin() 
    167          
    168     def canModify(self,context): 
    169         # modifying a forum is restricted to: admin, moderators 
    170         if context.isForumAdmin(): 
    171             return True 
    172         if context.isForumUser() and (context.getAuthname() in self.instance["moderatorsArr"]): 
    173             return True 
    174         else: 
    175             return False 
    176              
    177     def canAppend(self,context): 
    178         # adding a topic to a locked forum is restricted to: admin, moderators 
    179         # adding a topic to a forum is restricted to: forum users 
    180         if self.instance["locked"]: 
    181             if context.isForumAdmin(): 
    182                 return True 
    183             elif context.isForumUser() and (context.getAuthname() in self.instance["moderatorsArr"]): 
    184                 return True 
    185             else: 
    186                 return False 
    187         elif context.isForumUser(): 
    188             return True 
    189         else: 
    190             return False     
    191          
    192     def canDelete(self,context): 
    193         # deleting portions of this forum: admins, moderators         
    194         return self._canModifyContents(self.instance,context) 
    195  
    196     def canMove(self,context): 
    197         # moving portions of this forum around: admins, moderators 
    198         # - conditional on canMove() being valid on both this and target 
    199         self._getTargetForum(context) 
    200          
    201         return self._canModifyContents(self.instance,context) and self._canModifyContents(self.targetForum,context) 
    202  
    203     def _canModifyContents(self,forum,context): 
    204         if context.isForumAdmin(): 
    205             return True 
    206         if context.isForumUser() and (context.getAuthname() in forum["moderatorsArr"]): 
    207             return True 
    208         else: 
    209             return False           
    210          
    211     ### IModel Methods ### 
    212      
    213     def _getCategories(self,context): 
    214         cursor = self.db.cursor() 
    215         cursor.execute(""" 
    216              SELECT DISTINCT categoryid FROM forum  
    217              WHERE projectid = %(projectid)s AND categoryid > 0 
    218         """,{ 
    219             'projectid': context.getProjectId() 
    220         }) 
    221          
    222         return cursor.fetchall() 
    223      
    224     def _resetModerators(self): 
    225         # flush and rebuild moderators 
    226         cursor = self.db.cursor() 
    227         cursor.execute(""" 
    228              DELETE from moderators WHERE forumid = %(id)s 
    229         """,{ 
    230             'id': self.instance['id'] 
    231         }) 
    232         for mod in self.instance['moderatorsArr']: 
    233             if len(mod) > 0: 
    234                 cursor.execute(""" 
    235                      INSERT into moderators (forumid,username) values (%(id)s,%(mod)s) 
    236                 """,{ 
    237                    'id': self.instance['id'], 
    238                    'mod': mod.strip() 
    239                 }) 
    240                  
    241     def load(self,context): 
    242         args = context.getArgs() 
    243                  
    244         # name-based load 
    245         if args["forum_args"] != '': 
    246             self.instance = self.getById(args["forum_args"],context) 
    247                         
    248         # id-based load 
    249         elif "id" in args: 
    250             self.instance = self.getById(args["id"],context) 
    251          
    252         # failed                        
    253         else: 
    254             raise TracError("cannot load forum") 
    255          
    256  
    257     def set(self,context): 
    258         args = context.getArgs() 
    259         modified = int(time.time()) 
    260                  
    261         if 'forum_description_rows' in args: 
    262             context.setSessionVar('forum_description_rows',args['forum_description_rows']) 
    263          
    264         self.instance = { 
    265             'id':             self.getDefault((args,self.instance),'id',''), 
    266             'name':           self.getDefault((args,self.instance),'name',''), 
    267             'description':    self.getDefault((args,self.instance),'description',''), 
    268             'modified':       self.getDefault((args,self.instance),'modified',modified), 
    269             'created':        self.getDefault((args,self.instance),'created',modified), 
    270             'hidden':         self.getDefault((args,self.instance),'hidden',False), 
    271             'locked':         self.getDefault((args,self.instance),'locked',False), 
    272             'moderators':     self.getDefault((args,self.instance),'moderators',''), 
    273             'categoryid':     self.getDefault((args,self.instance),'categoryid',''), 
    274             'projectid':      args['projectid'] 
    275         } 
    276                  
    277         if self.instance['moderators'] != '': 
    278             self.instance['moderatorsArr'] = self.instance['moderators'].split(',') 
    279         else: 
    280             self.instance['moderatorsArr'] = [] 
    281                  
    282     def create(self,context):         
    283         # create instance 
    284         cursor = self.db.cursor() 
    285         cursor.execute(""" 
    286             INSERT into forum (created,modified,name,description,projectid,hidden,locked,categoryid) 
    287             values (%(modified)s,%(modified)s,%(name)s,%(description)s,%(projectid)s,%(hidden)s,%(locked)s,%(categoryid)s) 
    288         """,{ 
    289             'modified': self.instance['modified'], 
    290             'name': self.instance['name'], 
    291             'description': self.instance['description'], 
    292             'locked': self.instance['locked'], 
    293             'hidden': self.instance['hidden'], 
    294             'id': self.instance['id'], 
    295             'categoryid': self.instance['categoryid'], 
    296             'projectid': context.getProjectId(), 
    297         }) 
    298         self.instance['id'] = self.db.get_last_id(cursor, 'forum') 
    299          
    300         self._resetModerators() 
    301                                  
    302     def save(self,context):         
    303         # save instance 
    304         cursor = self.db.cursor() 
    305                 
    306         cursor.execute(""" 
    307             UPDATE forum SET 
    308             modified=%(modified)s, name=%(name)s, description=%(description)s, locked=%(locked)s, hidden=%(hidden)s, categoryid=%(categoryid)s 
    309             WHERE id = %(id)s AND projectid=%(projectid)s 
    310         """,{ 
    311             'modified': self.instance['modified'], 
    312             'name': self.instance['name'], 
    313             'description': self.instance['description'], 
    314             'locked': self.instance['locked'], 
    315             'hidden': self.instance['hidden'], 
    316             'id': self.instance['id'], 
    317             'categoryid': self.instance['categoryid'], 
    318             'projectid': context.getProjectId(), 
    319         }) 
    320              
    321         self._resetModerators() 
    322          
    323     def delete(self,context): 
    324         args = context.getArgs() 
    325         selectedTopics = args["selectedTopics"].split(",") 
    326         self.deleteTopics(selectedTopics,context) 
    327      
    328     def move(self,context): 
    329         args = context.getArgs() 
    330         selectedTopics = args["selectedTopics"].split(",") 
    331         targetForumId = args["targetForumId"] 
    332         self.moveTopicsTo(selectedTopics,targetForumId,context)         
    333          
    334     def _validate(self,context): 
     4from tracforums.orm import * 
     5from tracforums.models.category import CategoryModel 
     6         
     7""" 
     8    Forum ORM 
     9""" 
     10 
     11class ForumModel(ORMSchema( 
     12    tablename="forum", 
     13    orderby={"rank":"asc"}, 
     14    columns={ 
     15        # normal columns 
     16        "id":          ORMKey(type="int", auto_increment = True, unique = True), 
     17        "projectid":   ORMKey(type="str", force_insert = True), 
     18        "name":        ORMColumn(type="str", unique = True, required = True), 
     19        "created":     ORMColumn(type="int"), 
     20        "modified":    ORMColumn(type="int"), 
     21        "description": ORMColumn(type="str"),  
     22        "locked":      ORMColumn(type="bool"), 
     23        "hidden":      ORMColumn(type="bool"), 
     24        "categoryid":  ORMColumn(type="int"), 
     25        "rank":        ORMColumn(type="int", auto_increment = True, force_update = True), 
     26                 
     27        # aliases 
     28        "forumid":     ORMAlias(sql="forum.id"), 
     29        "topicCount":  ORMAlias(sql="coalesce((select count(id) from topic as t where forum.id = t.forumid),0)"), 
     30        "viewCount":   ORMAlias(sql="coalesce((select sum(views) from topic as t where t.forumid=forum.id),0)"), 
     31        "replyCount":  ORMAlias(sql=""" 
     32            coalesce( 
     33                (select (select count(m.id)  
     34                    from topic as t join message as m on m.topicid = t.id  
     35                    where t.forumid = forum.id 
     36                ) - (select count(id) from topic as t where t.forumid=forum.id) 
     37            ),0) 
     38        """), 
     39        "recentPostId": ORMAlias(sql=""" 
     40            select id from message as m where modified = ( 
     41                select max(modified)  
     42                from message as m join topic as t on m.topicid = t.id 
     43                where t.forumid = forum.id 
     44            ) 
     45        """), 
     46         
     47        "descriptionhtml": ORMAlias( 
     48            name = "description", 
     49            type = "oneliner" 
     50        ), 
     51         
     52        # relations 
     53        "moderators":  ORMRelation( 
     54            model        = ORMImportModel("tracforums.models.moderator","ModeratorModel"), 
     55            relationship = {"forumid":"forumid"}, 
     56        ), 
     57    })): 
     58                     
     59    def format(self): 
     60        print """Custom Format""",self 
     61        self.canModify = toBool(self._canModify()) 
     62        self.canAppend = toBool(self._canAppend()) 
     63        self.canView = toBool(self._canView()) 
     64        self.moderatorsList = ", ".join(map(lambda x: x.username,self.moderators)) 
     65        self.userIsModerator = self.formatContext.getAuthname() in self.moderators 
     66                 
     67    def _canView(self): 
     68        if not self.formatContext.isForumUser(): return False 
     69        elif self.formatContext.isForumAdmin(): return True 
     70        elif self.formatContext.getAuthname() in self.moderators: return True 
     71        else: return not self.hidden 
     72         
     73    def _canModify(self): 
     74        if not self.formatContext.isForumUser(): return False 
     75        elif self.formatContext.isForumAdmin(): return True 
     76        else: return self.formatContext.getAuthname() in self.moderators 
     77         
     78    def _canAppend(self): 
     79        if not self.formatContext.isForumUser(): return False 
     80        elif self.formatContext.isForumAdmin(): return True 
     81        elif self.formatContext.getAuthname() in self.moderators: return True 
     82        else: return not self.locked 
     83         
     84    def validate(self): 
    33585        reasons = [] 
    336         cursor = self.db.cursor() 
    337          
     86        # TODO: validate categoryid   
     87                 
    33888        # ensure name is specified and is unique 
    339         if self.instance['name'] == "":  
     89        if self.name == "":  
    34090            reasons.append("The forum name is required.") 
    34191        else: 
    342             if self.instance['id'] == "": 
    343                 cursor.execute( 
    344                     "SELECT id from forum WHERE name=%(name)s AND projectid=%(projectid)s", 
    345                     {   
    346                        'projectid': context.getProjectId(), 
    347                        'name': self.instance['name'], 
     92            if self.id == '0': 
     93                nameTest = self.getMany({ 
     94                   "projectid": self.formatContext.getProjectId(), 
     95                   "name": self.name, 
     96                }) 
     97            else: 
     98                nameTest = self.getMany({ 
     99                   "projectid": self.formatContext.getProjectId(), 
     100                   "name": self.name, 
     101                   "id": ("<>",self.id), #get everything but this instance 
     102                }) 
     103            if len(nameTest) > 0: 
     104                reasons.append("The forum name, '" + self.name + "', is already taken.")                
     105        return reasons 
     106 
     107    def validateCreate(self): 
     108        return self.validate() 
     109             
     110    def validateSave(self): 
     111        reasons = self.validate() 
     112         
     113        if len(self.getMany({ 
     114            "id":        self.id, 
     115            "projectid": self.formatContext.getProjectId() 
     116        })) == 0: 
     117            raise TracError('Invalid Forum Id %s' % self.id)         
     118         
     119        return reasons 
     120         
     121    def saveModerators(self): 
     122        print map(lambda x: x.username,self.moderators) 
     123        if self.moderators and len(self.moderators) > 0: 
     124            from tracforums.models.moderator import ModeratorModel 
     125            ModeratorModel(self.db,self.formatContext).deleteMany({"forumid":self.id,"username":map(lambda x: x.username,self.moderators)}) 
     126            for username in self.moderators: 
     127                ModeratorModel(self.db,self.formatContext).save({"username":username,"forumid":self.id}) 
     128         
     129    def save(self,data={}): 
     130        self.getBase().save(self,data) 
     131        self.saveModerators() 
     132        from tracforums.models.watch import notifyForumChanged 
     133        notifyForumChanged(self.db,self.formatContext,self) 
     134             
     135    def create(self,data={}): 
     136        self.getBase().create(self,data) 
     137        self.saveModerators() 
     138         
     139    def demoteRank(self): 
     140        # increase rank value to lower it's priority in the list 
     141        ranks = self.getColumnValues("rank",{"projectid":self.projectid,"categoryid":self.categoryid}) 
     142        print "\nRanks:",ranks 
     143        idx = ranks.index(self.rank) 
     144         
     145        if idx < len(ranks)-1: 
     146            # swap with next highest rank 
     147            self.updateMany({"rank":ranks[idx+1]},{"rank":self.rank}) 
     148            self.rank = ranks[idx+1] 
     149                 
     150    def promoteRank(self): 
     151        # decrease rank value to raise it's priority in the list 
     152        ranks = self.getColumnValues("rank",{"projectid":self.projectid,"categoryid":self.categoryid}) 
     153        print "\nRanks:",ranks 
     154        idx = ranks.index(self.rank) 
     155         
     156        if idx > 0: 
     157            # swap with next lowest rank 
     158            self.updateMany({"rank":ranks[idx-1]},{"rank":self.rank}) 
     159            self.rank = ranks[idx-1] 
     160             
     161             
     162ForumModelWithRecentPost = ORMSchema( 
     163    base = ForumModel, 
     164    columns={            
     165        "recentpost":  ORMJoin( 
     166            model         = ORMImportModel("tracforums.models.message","MessageModelWithProfileAndTopic"), 
     167            relationship  = {"recentpostid":"messageid"} 
     168        ) 
     169    } 
     170
     171 
     172from tracforums.models.topic import TopicModel,TopicModelWithLeadMessage 
     173from tracforums.models.watch import ForumWatchModelWithProfile,ProjectWatchModel,ProjectWatchModelWithProfile 
     174 
     175class ForumController(Controller): 
     176    def __init__(self,req,env,db): 
     177        Controller.__init__(self,req,env,db) 
     178                                             
     179    def doView(self): 
     180        args = self.getArgs() 
     181        forum = ForumModel(self.db,self).load({"id": args["viewid"]}) 
     182         
     183        self.assertAction( 
     184            forum != None, 
     185            "Forum does not exist" 
     186        ) 
     187         
     188        self.assertAction( 
     189            forum.canView, 
     190            "You do not have permission to view this item." 
     191        ) 
     192         
     193        # handle watch behavior         
     194        args = self.getArgs() 
     195        action = args.get("action",None) 
     196        try:         
     197            if action == "watch": 
     198                from tracforums.models.watch import ForumWatchModel 
     199                ForumWatchModel(self.db,self).save({ 
     200                    "username":  self.getAuthname(), 
     201                    "forumid":   forum.id 
     202                }) 
     203                                 
     204            #force changes through before display 
     205            self.db.commit() 
     206            validateErrors = None 
     207        except ModelValidateException,e: 
     208            validateErrors = e.reasons         
     209                  
     210        return ("forum/view.cs",{ 
     211            "forum": forum, 
     212            "validateErrors": validateErrors, 
     213            "topics": TopicModelWithLeadMessage(self.db,self).getMany({"forumid": forum.id}), 
     214            "watching": ForumWatchModelWithProfile(self.db,self).getMany({"forumid": forum.id}) 
     215        }) 
     216         
     217    def doEdit(self): 
     218        args = self.getArgs() 
     219        forum = ForumModel(self.db,self) 
     220         
     221        if args.get("viewid",0) != 0: 
     222            forum.load({"id": args["viewid"]}) 
     223        action = args.get("action","edit") 
     224        validateErrors = [] 
     225                     
     226        self.assertAction( 
     227            forum != None, 
     228            "Forum does not exist" 
     229        ) 
     230                 
     231        self.assertAction( 
     232            forum.canModify, 
     233            "You do not have permission to edit this item." 
     234        ) 
     235                 
     236        try: 
     237            if 'forum_description_rows' in args: 
     238                self.setSessionVar('forum_description_rows',args['forum_description_rows']) 
     239            forum.set(args) 
     240            if action == "preview": 
     241                pass 
     242            elif action == "save": 
     243                forum.save() 
     244                self.forumRedirect(args.get("returnto","forum/view/" + str(forum.id))); 
     245        except ModelValidateException,e: 
     246           validateErrors = e.reasons 
     247         
     248        return ("forum/edit.cs",{ 
     249            "returnto": args.get("returnto",None), 
     250            "forum": forum, 
     251            "preview": [forum], 
     252            "categories": CategoryModel(self.db,self).getMany({"projectid": self.getProjectId()}), 
     253            "forum_description_rows": self.getSessionVar("forum_description_rows",8), 
     254            "validateErrors": validateErrors 
     255        }) 
     256         
     257    def doManage(self): 
     258        args = self.getArgs() 
     259        forum = ForumModel(self.db,self).load({"id": args["viewid"]}) 
     260         
     261        self.assertAction( 
     262            forum != None, 
     263            "Forum does not exist" 
     264        ) 
     265         
     266        self.assertAction( 
     267            forum.canView, 
     268            "You do not have permission to view this item." 
     269        ) 
     270         
     271        # handle watch behavior         
     272        args = self.getArgs() 
     273        action = args.get("action",None) 
     274        topicid = args.get("topicid",None) 
     275         
     276        try:     
     277            if action == "changetype": 
     278                topic = TopicModel(self.db,self).load({"id":topicid}) 
     279                topic.type = args.get("type","") 
     280                topic.save() 
     281                 
     282            elif action == "deleteSelection": 
     283                selectedTopics = args.getArray("selectedtopics") 
     284                if len(selectedTopics) > 0: 
     285                    TopicModel(self.db,self).deleteMany({ 
     286                        "id": selectedTopics, 
     287                        "forumid": forum.id 
    348288                    }) 
    349             else:                         
    350                 cursor.execute( 
    351                     "SELECT id from forum WHERE name=%(name)s AND id <> %(id)s AND projectid=%(projectid)s", 
    352                     {   
    353                        'projectid': context.getProjectId(), 
    354                        'name': self.instance['name'], 
    355                        'id': self.instance['id'], 
    356                     }) 
    357             if cursor.fetchone():  
    358                 reasons.append("The forum name, '" + self.instance['name'] + "', is already taken.")                
    359                 
    360         # check moderators 
    361         for moderator in self.instance['moderators'].split(","): 
    362             if len(moderator): 
    363                 moderator = moderator.strip() 
    364                 cursor.execute("SELECT username from profile WHERE username=%s",(moderator,)) 
    365                 if not cursor.fetchone(): 
    366                     reasons.append("The moderator '" + moderator + "' is not a valid username.") 
    367                  
    368         return reasons 
    369          
    370     def validateCreate(self,context): 
    371         reasons = self._validate(context)         
    372         return reasons 
    373              
    374     def validateSave(self,context): 
    375         reasons = self._validate(context) 
    376         cursor = self.db.cursor() 
    377          
    378         cursor.execute( 
    379             "SELECT id from forum WHERE id=%(id)s AND projectid=%(projectid)s", 
    380             { 
    381                 'id': self.instance['id'], 
    382                 'projectid': context.getProjectId() 
    383             } 
    384         ) 
    385         if not cursor.fetchone(): 
    386             raise TracError('Invalid Forum Id %s' % self.instance['id'])         
    387          
    388         return reasons 
    389              
    390     ### Model-to-Model Helpers ### 
    391      
    392     def getViewRestriction(context): 
    393         if context.isForumAdmin(): 
    394             return "" 
    395         elif context.isForumUser(): 
    396             return """AND (not forum.hidden  
    397                 OR %(username)s in (SELECT username FROM moderators WHERE moderators.forumid = forum.id) 
    398             )""" 
    399         elif context.isForumGuest: 
    400             return "AND not forum.hidden" 
    401         else: 
    402             return None 
    403      
    404     getViewRestriction = Callable(getViewRestriction) 
    405      
    406     def getCanModifyRestriction(context): 
    407         if context.isForumAdmin(): 
    408             return "" 
    409         elif context.isForumUser(): 
    410             return """AND ( 
    411                 %(username)s in (SELECT username FROM moderators WHERE moderators.forumid = forum.id) 
    412             )""" 
    413         else: 
    414             return None 
    415              
    416     getCanModifyRestriction = Callable(getCanModifyRestriction) 
    417      
    418     def getCanModifyValue(context): 
    419         if context.isForumAdmin(): 
    420             return "true" 
    421         elif context.isForumUser(): 
    422             return """( 
    423                 %(username)s in (SELECT username FROM moderators WHERE moderators.forumid = forum.id) 
    424             )""" 
    425         else: 
    426             return "false" 
    427              
    428     getCanModifyValue = Callable(getCanModifyValue) 
    429          
    430     def getCanDeleteValue(context): 
    431         if context.isForumAdmin(): 
    432             return "true" 
    433         else: 
    434             return "false" 
    435              
    436     getCanDeleteValue = Callable(getCanDeleteValue)     
    437                  
    438     def getById(self,forumid,context): 
    439         cursor = self.db.cursor() 
    440                  
    441         cursor.execute(""" 
    442             SELECT name, id, created, modified, description, hidden, locked, projectid, categoryid 
    443             FROM forum WHERE id=%(id)s AND projectid=%(projectid)s 
    444         """,{ 
    445             "id":forumid, 
    446             "projectid":context.getProjectId(), 
    447         }) 
    448         row = cursor.fetchone() 
    449         if not row: 
    450             raise TracError("Cannot load forum %s" % forumid) 
    451              
    452         return self._getByRow(row,context) 
    453          
    454     def getByName(self,name,context): 
    455         cursor = self.db.cursor() 
    456          
    457         cursor.execute(""" 
    458             SELECT name, id, created, modified, description, hidden, locked, projectid, categoryid 
    459             FROM forum WHERE name=%(name)s AND projectid=%(projectid)s 
    460         """,{ 
    461             "name":name, 
    462             "projectid":context.getProjectId(), 
    463         }) 
    464         row = cursor.fetchone() 
    465         if not row: 
    466             raise TracError("Cannot load forum %s" % name) 
    467              
    468         return self._getByRow(row,context)    
    469          
    470     def _getByRow(self,row,context): 
    471         columns = ('name', 'id', 'created', 'modified', 'description', 'hidden', 'locked', 'projectid', 'categoryid') 
    472   
    473         forum = dict(zip(columns, row)) 
    474         forum['hidden'] = toBool(forum['hidden']) 
    475         forum['locked'] = toBool(forum['locked']) 
    476          
    477         # get moderators 
    478         forum['moderatorsArr'] = [] 
    479         cursor = self.db.cursor() 
    480         columns = ('username') 
    481         cursor.execute("SELECT username FROM moderators WHERE forumid = %s",(forum['id'],)) 
    482         for row in cursor.fetchall(): 
    483             forum['moderatorsArr'].append(row[0]) 
    484              
    485         if len(forum['moderatorsArr']) > 0: 
    486             forum['moderators'] = ",".join(forum['moderatorsArr']) 
    487         else: 
    488             forum['moderators'] = '' 
    489              
    490         return forum        
    491          
    492     def getList(self,context): 
    493         cursor = self.db.cursor() 
    494         forums = [] 
    495          
    496         viewRestriction = Forum.getViewRestriction(context) 
    497         if viewRestriction == None: 
    498             return forums 
    499              
    500         canModifyValue = Forum.getCanModifyValue(context)         
    501         canDeleteValue = Forum.getCanDeleteValue(context) 
    502                     
    503         columns = ('id', 'created', 'modified', 'name', 'description', 'locked', 'hidden', 'categoryid', 
    504             'canModify', 'canDelete', 
    505             'replies', 'topics', 'views', 
    506             'recentAuthor', 'avatarid', 'recentModified', 'recentId', 'recentTopicId' ) 
    507              
    508         cursor.execute(""" 
    509             SELECT  
    510                 distinct forum.id, forum.created, forum.modified, forum.name, forum.description, forum.locked, forum.hidden, forum.categoryid, 
    511                 """ + canModifyValue + """ as canModify, 
    512                 """ + canDeleteValue + """ as canDelete, 
    513                 (CASE count(message.id) WHEN 0 THEN 0 ELSE count(message.id)-1 END) as replies, 
    514                 COALESCE(topics.total,0) as topics, 
    515                 COALESCE(topicViews.total,0) as views, 
    516                 recentForumMessage.author, 
    517                 recentForumMessage.avatarid, 
    518                 recentForumMessage.modified, 
    519                 recentForumMessage.id, 
    520                 recentForumMessage.topicid 
    521                  
    522             FROM forum  
    523             LEFT JOIN 
    524             (SELECT message.*, recentForumMsg.forumid FROM 
    525                 message JOIN 
    526                 (SELECT recentTopicMessage.forumid,max(modified) AS modified FROM 
    527                     (SELECT topic.forumid,message.modified  
    528                         FROM topic 
    529                         JOIN message ON topic.id = message.topicid  
    530                         JOIN (SELECT topicid, MAX(modified) AS modified  
    531                             FROM message  
    532                             GROUP BY topicid 
    533                         ) recentMsg ON recentMsg.modified = message.modified AND recentMsg.topicid = message.topicid 
    534                     ) AS recentTopicMessage 
    535                     group by recentTopicMessage.forumid 
    536                 ) AS recentForumMsg ON message.modified = recentForumMsg.modified 
    537             ) AS recentForumMessage ON recentForumMessage.forumid = forum.id 
    538  
    539             LEFT JOIN topic ON topic.forumid = forum.id 
    540             LEFT JOIN message ON message.topicid = topic.id 
    541              
    542             LEFT JOIN 
    543             (SELECT topic.forumid, COUNT(topic.id) AS total 
    544                 FROM topic  
    545                 GROUP BY topic.forumid 
    546             ) AS topics ON topics.forumid = forum.id  
    547              
    548             LEFT JOIN 
    549             (SELECT topic.forumid, SUM(topic.views) AS total 
    550                 FROM topic  
    551                 GROUP BY topic.forumid 
    552             ) AS topicViews ON topicViews.forumid = forum.id             
    553              
    554             WHERE projectid=%(projectid)s """ + viewRestriction + """  
    555              
    556             GROUP BY  
    557                 forum.id, forum.created, forum.modified, forum.name, forum.description, forum.locked, forum.hidden, forum.categoryid, 
    558                 topics.total, topicViews.total, 
    559                 recentForumMessage.author, 
    560                 recentForumMessage.avatarid, 
    561                 recentForumMessage.modified, 
    562                 recentForumMessage.id, 
    563                 recentForumMessage.topicid 
    564                  
    565             ORDER BY forum.id 
    566             """, 
    567             { 
    568                 'projectid': context.getProjectId(), 
    569                 'usern