Commits

Julio Flores-Schwarzbeck  committed 750d253

Many bufixes, and ability of editing questions and answers
functionality almost complete.

  • Participants
  • Parent commits 148ce6b

Comments (0)

Files changed (7)

File controllers/default.py

         view_info=view_info)
 
 def tags():
-    """ Returns a simple liet of all the mst popular tags """
+    """ Returns a simple list of all the mst popular tags """
     view_info = {}
     view_info.update(dict(errors=[]))
-    tags = db().select(
-        db.tags.ALL,
-        orderby=~db.tags.tag_cnt,
+    tag_cnt = db.question_tags.id.count()
+    tags = db(db.tags.id==db.question_tags.tag_id).select(
+        db.tags.tagname,
+        tag_cnt,
+        groupby=db.tags.id,
+        orderby=~tag_cnt,
         limitby=(0, 30))
     return dict(tags=tags,
                 view_info=view_info)

File controllers/members.py

         title = req.question_title
         question = req.question
         tags = '' if req.tags is None else req.tags.lower()
-        tag_lst = tags.split(',')
+        tag_lst = list(set(tags.split(',')))
         preview_question = req.preview_question
         post_question = req.post_question
         if title and question and tags:
                     views=0)
                 # Next, add the specified tags
                 for tag in tag_lst:
-                    tag_r = db(db.tags.tagname==tag).select(db.tags.ALL)
-                    if tag_r:
-                        tag_id = tag_r[0].id
-                        # Tag Exists, increment its value
-                        db(db.tags.id==tag_id).update(
-                            tag_cnt=tag_r[0].tag_cnt + 1)
-                    else:
-                        # This tag does not exist, add it to our tags table
-                        tag_id = db.tags.insert(tagname=tag, is_enabled=True)
-
-                    # Now assign this tag to the question
-                    db.question_tags.insert(question_id=question_id,
-                                            tag_id=tag_id)
+                    if tag.strip():
+                        tag_r = db(db.tags.tagname==tag).select(db.tags.ALL)
+                        if tag_r:
+                            tag_id = tag_r[0].id
+                        else:
+                            # This tag does not exist, add it to our tags table
+                            tag_id = db.tags.insert(tagname=tag, is_enabled=True)
+    
+                        # Now assign this tag to the question
+                        db.question_tags.insert(question_id=question_id,
+                                                tag_id=tag_id)
                 # Now subscribe this user if hs/she requested to do so
                 if req.subscribe:
                     db.question_subscriptions.insert(auth_user_id=user_id,
                 # wipe out all the tags for this question
                 db(db.question_tags.question_id==qid).delete()
                 new_tag_ids = []
-                for tag in tags.split(','):
+                # Remove duplicates, don'twant to say it is a "hack" but it
+                # just might be...
+                tag_list = list(set(tags.lower().split(',')))
+                for tag in tag_list:
                     # if this tag does not exist in the master tags table,
                     # add it, along with a reference to the ID into our
                     # question_tags table
-                    is_tag = db(db.tags.tagname==tag).select(db.tags.id)
-                    if is_tag:
-                        # Tag exists already, use its id to add the new
-                        # tag pertaining this question
-                        tag_id = is_tag[0].id
-                    else:
-                        # No master tag exists, create it
-                        tag_id = db.tags.insert(tagname=tag,
-                                                is_enabled=True,
-                                                tag_cnt=1)
-                    # Finally, add this tag_id to our question_tags table
-                    db.question_tags.insert(question_id=qid,
-                                            tag_id=tag_id)
-                    new_tag_ids.append(tag_id)
-                    
-                # Here we need to do a bit of clean-up on the master tags,
-                # if the user removed any of the existing tags in the question,
-                # I need to decrement the tag_cnt value of the master tags
-                # table, however, if the tag_cnt value reaches 0, then I need
-                # to actually delete the tag record itself...
-                # for this, I am relying on my current_tag_ids/new_tag_ids
-                # variables..
-                # The elements in current_tag_ids that are NOT in new_tag_ids
-                # will have to be subtracted or deleted from table tags
-                old_set = set(current_tag_ids)
-                new_set = set(new_tag_ids)
-                remove_tag_list = list(old_set.difference(new_set))
-                for tag_id in remove_tag_list:
-                    cur_tag_cnt = db(db.tags.id==tag_id).select(
-                        db.tags.tag_cnt)[0].tag_cnt
-                    if cur_tag_cnt - 1 == 0:
-                        # Remove it from master tag list
-                        db(db.tags.id==tag_id).delete()
-                    else:
-                        # Update the tag_cnt only, don't delete it
-                        db(db.tags.id==tag_id).update(tag_cnt=cur_tag_cnt-1)
+                    if tag.strip():
+                        is_tag = db(db.tags.tagname==tag).select(db.tags.id)
+                        if is_tag:
+                            # Tag exists already, use its id to add the new
+                            # tag pertaining this question
+                            tag_id = is_tag[0].id
+                        else:
+                            # No master tag exists, create it
+                            tag_id = db.tags.insert(tagname=tag,
+                                                    is_enabled=True)
+                        # Finally, add this tag_id to our question_tags table
+                        db.question_tags.insert(question_id=qid,
+                                                tag_id=tag_id)
+                        new_tag_ids.append(tag_id)
                 redirect(URL(r=request, c='default', f='view', args=[qid]))
             else:
                 wiew_info['errors'].append("Please make sure you specify "
     return dict(question=question,
                 view_info=view_info,
                 tags=tags,
-                can_edit=can_edit)
+                can_edit=can_edit)
+
+
+@auth_user.requires_login()
+def edit_answer():
+    """ Answers can be edited:
+    - By the SysAdmin, regardless
+    - By the Answer owner
+    Answers may not be edited (SysAdmin except):
+    - If te answer is marked as "answered" by the question originator/SysAdmin
+    
+    """
+    req = request.vars
+    view_info = {'errors': []}
+    auth_user_id = auth_user.get_user_id()
+    can_edit = False
+    redir = False # Flag to trigger redirection back to View Question page
+    if req.form_submitted:
+        aid = req.aid
+        description = req.description.strip()
+        if description:
+            # All seems fine, update
+            db(db.answers.id==aid).update(description=description,
+                                          modified_by=auth_user_id,
+                                          modified_on=request.now)
+            # Then get out of here
+            redir = True
+        else:
+            wiew_info['errors'].append("Please make sure you specify "
+                                       "a valid content for your answer.")
+    else:
+        aid = request.args[0]
+
+    answer = db(db.answers.id==aid).select(db.answers.ALL)[0]
+    question = db(db.questions.id==answer.question_id).select(
+        db.questions.ALL)[0]
+    tags = db((db.tags.id==db.question_tags.tag_id) &\
+              (db.question_tags.question_id==question.id)).select(
+                  db.tags.tagname,
+                  orderby=db.tags.tagname)
+    
+    if redir:
+        redirect(URL(r=request, c='default', f='view', args=[question.id]))
+    
+    if auth_user.is_admin():
+        can_edit = True
+    else:
+        # No admin, can you still edit?
+        if answer.created_by == auth_user_id and\
+           answer.is_visible and not\
+               answer.is_answer:
+            can_edit = True
+            
+    return dict(answer=answer,
+                question=question,
+                tags=tags,
+                view_info=view_info,
+                can_edit=can_edit)
+

File models/db.py

 db.define_table('tags',
     Field('tagname', 'string', required=True, length=255),
     Field('is_enabled', 'boolean', required=True, default=True),
-    Field('tag_cnt', 'integer', required=True, default=1),
     migrate=migrate)
 
 # Tag/Question relationship

File modules/QAStackHelper.py

         start_px = 22
         cnt = 0
         bold_toggle = True
-        tags = self.db(self.db.tags.is_enabled==True).select(
+        tag_cnt = self.db.question_tags.id.count()
+        tags = self.db(self.db.tags.id==self.db.question_tags.tag_id).select(
             self.db.tags.tagname,
-            self.db.tags.tag_cnt,
-            limitby=(0,12),
-            orderby=~self.db.tags.tag_cnt|self.db.tags.tagname)
+            tag_cnt,
+            groupby=self.db.tags.id,
+            orderby=~tag_cnt,
+            limitby=(0, 12))
         html = ''
         for tag in tags:
-            tagname = tag.tagname
-            tag_cnt = tag.tag_cnt
+            tagname = tag.tags.tagname
+            tag_cnt = tag['COUNT(question_tags.id)']
             if bold_toggle:
                 html_stub = 'font-weight:bold;'
                 bold_toggle = False

File views/default/tags.html

 <p>{{=XML(T('The following list shows the 30 most popular tags, click on any of them to view questions pertaining these tags'))}}.</p>
 
 <div class="paragraph-all">
-    <table cellspacing="0" cellpadding="2" style="margin:0 0 10px 10px;border:1px solid #888888;">
+    <table cellspacing="0" cellpadding="0" class="ftable">
         <thead>
-            <tr style="background-color:#AAAAAA;border-bottom:1px solid black;">
+            <tr class="shadedarker">
                 <th>Rank</th>
                 <th style="text-align:left;">{{=XML(T('Tagname'))}}</th>
                 <th>{{=XML(T('Count'))}}</th>
         </thead>
         <tbody>
             {{for idx,tag in enumerate(tags):}}
-                <tr {{if idx%2:}}style="background-color:#eaeaea;"{{pass}}>
-                    <td style="border-right:1px solid #dddddd;">{{=idx+1}}</td>
-                    <td style="border-right:1px solid #dddddd;">
-                        <a href="{{=URL(r=request, c='default', f='index', args=['tags'], vars=dict(tag=tag.tagname))}}"
-                        title="{{=XML(T('View questions related to'))}} {{=tag.tagname}}">{{=tag.tagname}}</a>
+                <tr {{if idx%2:}}class="shadedark"{{pass}}>
+                    <td style="text-align:center;color:#cacaca;">{{=idx+1}}</td>
+                    <td>
+                        <a href="{{=URL(r=request, c='default', f='index', args=['tags'], vars=dict(tag=tag.tags.tagname))}}"
+                        title="{{=XML(T('View questions related to'))}} {{=tag.tags.tagname}}">{{=tag.tags.tagname}}</a>
                     </td>
-                    <td style="text-align:center;">{{=tag.tag_cnt}}</td>
+                    <td style="text-align:center;">{{=tag['COUNT(question_tags.id)']}}</td>
                 </tr>
             {{pass}}
         </tbody>

File views/default/view.html

                                        onclick="return confirm('This will change the answer status for this question, Please Confirm');"
                                        class="bold">{{=XML(T('Accept this Answer'))}}</a>
                                 {{pass}}
+                                {{if auth_user.has_role('Manager,SysAdmin') or (auth_user.get_user_id() == answers.answer.created_by and not answer.answers.is_answer):}}
+                                    |
+                                    <a href="{{=URL(r=request, c='members', f='edit_answer', args=[answer.answers.id])}}"
+                                       title="{{=XML(T('Edit this Answer'))}}"
+                                       class="bold">{{=XML(T('Edit this Answer'))}}</a>
+                                {{pass}}
                                 {{if auth_user.is_auth():}}
                                     |
                                     <a title=""

File views/members/edit_answer.html

+{{extend 'qastack_layout.html'}}
+
+<h2 class="fancy">{{=XML(T('Edit Answer'))}}</h2>
+
+{{if view_info['errors']:}}
+    <p class="err">{{=XML(T('There was one or more messages generated while processing your request, please review the following'))}}</p>
+    <ul>
+        {{for error in view_info['errors']:}}
+            <li class="err">{{=XML(T(error))}}</li>
+        {{pass}}
+    </ul>
+{{pass}}
+
+<h3>Question</h3>
+
+<div class="paragraph-big">
+    <table cellspacing="1" cellpadding="1" style="width:100%;margin-bottom:10px;">
+        <tbody>
+            <tr>
+                <td style="text-align:center;vertical-align:top;width:100px;">
+                    <img src="{{=URL(r=request, c='static/images/medium', f='checkmark_disabled.png')}}"
+                    alt="{{=XML(T('Up/Down voting is unavailable on this page'))}}"
+                    title="{{=XML(T('Up/Down voting is unavailable on this page'))}}" /><br />
+                    <span class="votes-display">{{=question.votes_up - question.votes_dn}}</span><br />
+                    <img src="{{=URL(r=request, c='static/images/medium', f='x_disabled.png')}}"
+                    alt="{{=XML(T('Up/Down voting is unavailable on this page'))}}"
+                    title="{{=XML(T('Up/Down voting is unavailable on this page'))}}" />
+                </td>
+                <td style="vertical-align:top;">
+                    <div class="lsep">
+                        <div class="explain">
+                        {{if stackhelper.has_member_avatar(question.created_by):}}
+                            <img src="{{=URL(r=request, c='members', f='get_avatar_image', args=[question.created_by])}}"
+                                 alt=""
+                                 style="vertical-align:top;" />
+                        {{pass}}
+                        {{=XML(T('By'))}}: {{=stackhelper.get_member_property('m_display_name', question.created_by, 'Anonymous')}}
+                        ({{=stackhelper.get_user_role(question.created_by)}}),
+                        {{=XML(T('Created'))}}
+                        {{=prettydate(question.created_on, T)}},
+                        {{=XML(T('Updated'))}}
+                        {{=prettydate(question.modified_on, T)}}.</div>
+                        <div class="paragraph-all">
+                            {{=XML(parse_content(question.description))}}
+                        </div>
+                        <img src="{{=URL(r=request, c='static/images/small', f='tag.png')}}"
+                             alt="Tags"
+                             title="Tags"
+                             style="float:left;margin:0 5px 0 0;vertical-algin:middle;" />
+                        <div class="tags">
+                            {{for tag in tags:}}
+                                <a class="tag" href="{{=URL(r=request, c='default', f='index', args=['tags'], vars=dict(tag=tag.tagname))}}"
+                                   title="{{=tag.tagname}}">{{=tag.tagname}}</a>
+                            {{pass}}
+                        </div>
+                    </div>
+                </td>
+            </tr>
+        </tbody>
+    </table>
+</div>
+
+
+<h3>Answer</h3>
+
+{{if can_edit:}}
+    <form id="edit_form" method="post" action="">
+        <fieldset id="ask_form_fields">
+            <input type="hidden" id="form_submitted" name="form_submitted" value="1" />
+            <input type="hidden" id="aid" name="aid" value="{{=answer.id}}" />
+            <table cellspacing="1" cellpadding="1" style="width:100%;">
+                <tbody>
+                    <tr>
+                        <td style="width:20%;">{{=XML(T('Answer'))}}:</td>
+                        <td style="width:80%;">
+                            <textarea id="description" name="description"
+                            cols="0" rows="0"
+                            style="width:80%;height:250px;">{{=request.vars.get('description', answer.description)}}</textarea>
+                        </td>
+                    </tr>
+                </tbody>
+            </table>
+        </fieldset>
+        
+        <div class="paragraph-all">
+            <input type="submit" id="update_answer name="answer_question" value="{{=XML(T('Update Answer'))}}"
+                   onclick="return confirm('Please confirm you wish to edit this answer.');" />
+            <input type="submit" id="cancel_edit" name="cancel_edit" value="{{=XML(T('Cancel'))}}" />
+        </div>
+    
+        <div class="paragraph">
+            Basic html is allowed, use of <span class="explain">&lt;b&gt; &lt;i&gt;</span> ok to use, in addition, use the following <i>pseudo-code</i>
+            to highlight and nicely format your code if you post any code snippet <i>(Click to Insert)</i>:
+            <a href="javascript:void(0);" onclick="add_helper('[code-python][/code-python]');return false;" title="">[code-python]</a>,
+            <a href="javascript:void(0);" onclick="add_helper('[code-c][/code-c]');return false;" title="">[code-c]</a>,
+            <a href="javascript:void(0);" onclick="add_helper('[code-c++][/code-c++]');return false;" title="">[code-c++]</a>,
+            <a href="javascript:void(0);" onclick="add_helper('[code-c#][/code-c#]');return false;" title="">[code-c#]</a>,
+            <a href="javascript:void(0);" onclick="add_helper('[code-php][/code-php]');return false;" title="">[code-php]</a>,
+            <a href="javascript:void(0);" onclick="add_helper('[code-java][/code-java]');return false;" title="">[code-java]</a>,
+            or
+            <a href="javascript:void(0);" onclick="add_helper('[code][/code]');return false;" title="">[code]</a>
+            (for a language not included in the previous code definitions).<br />Close your code snippet
+            with a matching <span class="explain">[/code-<i>language</i>]</span> if applicable or manually added.
+        </div>
+    
+    </form>
+    
+    <script type="text/javascript">
+    <!--
+        function add_helper(s_value) {
+            var oanswer = document.getElementById('description');
+    
+            // IE support
+            if (document.selection) {
+                oanswer.focus();
+                sel = document.selection.createRange();
+                sel.text = s_value;
+            }
+            // MOZILLA/NETSCAPE support
+            else if (oanswer.selectionStart || oanswer.selectionStart == 0) {
+                var start_pos = oanswer.selectionStart;
+                var end_pos = oanswer.selectionEnd;
+                oanswer.value = oanswer.value.substring(0, start_pos) + s_value + oanswer.value.substring(end_pos, oanswer.value.length);
+            } else {
+                oanswer.value += s_value;
+            }    
+        }
+        // Put the focus on the comment textarea
+        document.getElementById('description').focus();
+    //-->
+    </script>
+{{else:}}
+    <p class="error">Unable to edit answer, this may be due to the following reasons:</p>
+    <ul>
+        <li class="error">You are not authorized to edit this answer.</li>
+        <li class="error">This answe has been disabled by the System Administrator.</li>
+        <li class="error">This answer *is* an answer for this question.</li>
+    </ul>
+    <p><a href="javascript:void(0);" title="" onclick="history.go(-1);">Go Back</a></p>
+{{pass}}