• 
      

    Disable front-end features for read-only mode

    Review Request #8810 — Created March 11, 2017 and submitted

    Information

    khp
    Review Board
    master

    Reviewers

    Read-only mode is a setting an admin can enable to prevent writes to
    the database. This can be used when the site is under maintenence or
    being upgraded. This commit disables most UI features which would
    normally affect Review Board, but under read-only mode, does not.
    Disabling some features such as login and redirection when attempting
    to view a read-only restricted page is contained in the next commit.

    This commit contains changes to the datagrid so changes to it does not
    change profile settings, navbar (hide My Account and New Review Request
    buttons), ETag, ReviewRequestEditor(including actions, comments, and
    the editor itself),

    Other changes include adding read-only mode setting to the context
    processor and adding a utility file containing the logic to determine
    whether a user is in read-only mode or not.

    • Check that caching of read-only mode is done correctly by changing
      modes and refreshing pages
    • Check that profile doesn't save in datagrid when in read-only mode by
      ensuring code doesn't reach the save method
    • Check that the review request actions available are appropriately
      changed when in read-only mode
    • Check that comments can't be added or replied to in read-only mode
    • Check that review requests can not be modified in read-only mode
    • Check that modifying review requests (such as bugs or branch) when in
      read-only mode while the site in browser is still old displayed an
      appropriate error message
    • Check that modifying anything to the webapi doesn't send a request at
      all when in read-only mode
    • Check that trying to add a comment through clicking on a line in diff
      displays appropriate warning about read-only mode
    • Check that clicking on the Star does nothing in read-only mode
    • Check that the above does not occur for admins
    Description From Last Updated

    Mind wrapping your description & testing done at 72 characters? Also, can you change your testing done to be bullet …

    brenniebrennie

    'reverse' imported but unused

    reviewbotreviewbot

    'django_reset' imported but unused

    reviewbotreviewbot

    'from settings_local import *' used; unable to detect undefined names

    reviewbotreviewbot

    Missing args/returns.

    brenniebrennie

    Missing: from __future__ import unicode_literals There must be a blank line between that import and any other imports.

    chipx86chipx86

    If you use the following, it will link to the class "the :py:class:`~djblets.siteconfig.models.SiteConfiguration`"

    brenniebrennie

    We need to use full paths, e.g. django.contrib.auth.models.User

    brenniebrennie

    If user.is_superuser is True, there's no reason to bother with a cache. That's just as fast.

    chipx86chipx86

    Missing args.

    brenniebrennie

    Where does disable_save come from? Is there a change for Djblets that isn't up for review? It's better to have …

    chipx86chipx86

    Missing args.

    brenniebrennie

    Let's pull out context['request'].user into a variable, so we're not doing the (actually fairly expensive, since context is not your …

    chipx86chipx86

    Indent this by two more spaces?

    szhangszhang

    Indent this by one space as well.

    szhangszhang

    Indent this by one space here?

    szhangszhang

    Let's move the read-only check last, as it's going to be less frequently a factor (it's more likely someone will …

    chipx86chipx86

    Same here.

    chipx86chipx86

    Move this after the if statement? Putting it before the if statement subtly gives the impression that the if statement …

    szhangszhang

    'django_reset' imported but unused

    reviewbotreviewbot

    'from settings_local import *' used; unable to detect undefined names

    reviewbotreviewbot

    Can you say why this happens? It's not going to be clear to people in the future that this means …

    chipx86chipx86

    Format as: ```javascript / * Comment * text /

    brenniebrennie

    It'd be nice to only do this at one place. So instead, how about something like: if (rsp.fields === undefined) …

    chipx86chipx86

    No trailing comma. Since this isn't an ES6 JavaScript file, it will end up breaking some browsers (such as IE).

    chipx86chipx86

    Isn't there always a value returned for readOnly? We probably don't need to specify a default.

    chipx86chipx86

    We probably don't need to specify a default, since readOnly should always have a value. It would also be nice …

    chipx86chipx86

    We specify variables with values before variables without values. Also, I don't think we need a default here for readOnly.

    chipx86chipx86

    I'm unsure if this declaration should live here -- I don't think we do this anywhere else, really. Perhaps ping …

    brenniebrennie

    Instead of this, can you just set actions to empty if in this mode? So: this.template({ ... actions: (this.showActions ? …

    chipx86chipx86

    Maybe it'd make more sense to have the other change built on top of this one instead.

    chipx86chipx86

    'django_reset' imported but unused

    reviewbotreviewbot

    'from settings_local import *' used; unable to detect undefined names

    reviewbotreviewbot

    Does is_site_read_only_for correctly handle user=None ?

    brenniebrennie

    'django_reset' imported but unused

    reviewbotreviewbot

    'from settings_local import *' used; unable to detect undefined names

    reviewbotreviewbot

    Do we want this to be user-visible? Or is this only for debugging?

    brenniebrennie

    This needs Args. It should also start off by saying the main thing that it does: "Saves the profile to …

    chipx86chipx86

    Parameters should align. This should also use format strings: console.log('%s request not sent ...', options.type); I'd also maybe use console.error …

    chipx86chipx86

    "The profile will only be saved if the user is not affected by read-only mode."

    brenniebrennie

    Mind adding Args/Returns while you're here?

    brenniebrennie

    Mind adding Args/Returns while you're here?

    brenniebrennie

    Could you put the summary on the first line of the docstring?

    brenniebrennie

    Mind adding Args/Returns while you're here?

    brenniebrennie

    Instead of this, why not just readOnly: {% is_read_only|yesno:'true,false' %}, That way the information is always available.

    brenniebrennie
    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          reviewboard/reviews/views.py
          reviewboard/admin/read_only.py
          reviewboard/reviews/models/base_comment.py
          reviewboard/webapi/base.py
          reviewboard/reviews/default_actions.py
          reviewboard/reviews/models/review_request.py
          reviewboard/datagrids/grids.py
          reviewboard/admin/context_processors.py
          reviewboard/settings.py
          reviewboard/reviews/templatetags/reviewtags.py
      
      Ignored Files:
          reviewboard/static/rb/js/pages/views/dashboardView.js
          reviewboard/static/rb/js/views/starManagerView.js
          reviewboard/templates/reviews/review_reply_section.html
          reviewboard/templates/base/navbar.html
          reviewboard/static/rb/js/utils/apiUtils.es6.js
          reviewboard/templates/base.html
          reviewboard/templates/base/_nav_support_menu.html
          reviewboard/static/rb/js/models/userSessionModel.js
          reviewboard/static/rb/js/views/reviewRequestEditorView.js
          reviewboard/static/rb/js/models/reviewRequestEditorModel.js
          reviewboard/static/rb/js/views/commentDialogView.js
          reviewboard/static/rb/js/models/commentEditorModel.js
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          reviewboard/reviews/views.py
          reviewboard/admin/read_only.py
          reviewboard/reviews/models/base_comment.py
          reviewboard/webapi/base.py
          reviewboard/reviews/default_actions.py
          reviewboard/reviews/models/review_request.py
          reviewboard/datagrids/grids.py
          reviewboard/admin/context_processors.py
          reviewboard/settings.py
          reviewboard/reviews/templatetags/reviewtags.py
      
      Ignored Files:
          reviewboard/static/rb/js/pages/views/dashboardView.js
          reviewboard/static/rb/js/views/starManagerView.js
          reviewboard/templates/reviews/review_reply_section.html
          reviewboard/templates/base/navbar.html
          reviewboard/static/rb/js/utils/apiUtils.es6.js
          reviewboard/templates/base.html
          reviewboard/templates/base/_nav_support_menu.html
          reviewboard/static/rb/js/models/userSessionModel.js
          reviewboard/static/rb/js/views/reviewRequestEditorView.js
          reviewboard/static/rb/js/models/reviewRequestEditorModel.js
          reviewboard/static/rb/js/views/commentDialogView.js
          reviewboard/static/rb/js/models/commentEditorModel.js
      
      
    2. reviewboard/reviews/views.py (Diff revision 1)
       
       
      Show all issues
       'reverse' imported but unused
      
    3. reviewboard/settings.py (Diff revision 1)
       
       
      Show all issues
       'django_reset' imported but unused
      
    4. reviewboard/settings.py (Diff revision 1)
       
       
      Show all issues
       'from settings_local import *' used; unable to detect undefined names
      
    5. 
        
    KH
    KH
    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          reviewboard/reviews/views.py
          reviewboard/admin/read_only.py
          reviewboard/reviews/models/base_comment.py
          reviewboard/webapi/base.py
          reviewboard/reviews/default_actions.py
          reviewboard/reviews/models/review_request.py
          reviewboard/datagrids/grids.py
          reviewboard/admin/context_processors.py
          reviewboard/settings.py
          reviewboard/reviews/templatetags/reviewtags.py
      
      Ignored Files:
          reviewboard/static/rb/js/pages/views/dashboardView.js
          reviewboard/static/rb/js/views/starManagerView.js
          reviewboard/templates/reviews/review_reply_section.html
          reviewboard/templates/base/navbar.html
          reviewboard/static/rb/js/utils/apiUtils.es6.js
          reviewboard/templates/base.html
          reviewboard/templates/base/_nav_support_menu.html
          reviewboard/static/rb/js/models/userSessionModel.js
          reviewboard/static/rb/js/views/reviewRequestEditorView.js
          reviewboard/static/rb/js/models/reviewRequestEditorModel.js
          reviewboard/static/rb/js/views/commentDialogView.js
          reviewboard/static/rb/js/models/commentEditorModel.js
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          reviewboard/reviews/views.py
          reviewboard/admin/read_only.py
          reviewboard/reviews/models/base_comment.py
          reviewboard/webapi/base.py
          reviewboard/reviews/default_actions.py
          reviewboard/reviews/models/review_request.py
          reviewboard/datagrids/grids.py
          reviewboard/admin/context_processors.py
          reviewboard/settings.py
          reviewboard/reviews/templatetags/reviewtags.py
      
      Ignored Files:
          reviewboard/static/rb/js/pages/views/dashboardView.js
          reviewboard/static/rb/js/views/starManagerView.js
          reviewboard/templates/reviews/review_reply_section.html
          reviewboard/templates/base/navbar.html
          reviewboard/static/rb/js/utils/apiUtils.es6.js
          reviewboard/templates/base.html
          reviewboard/templates/base/_nav_support_menu.html
          reviewboard/static/rb/js/models/userSessionModel.js
          reviewboard/static/rb/js/views/reviewRequestEditorView.js
          reviewboard/static/rb/js/models/reviewRequestEditorModel.js
          reviewboard/static/rb/js/views/commentDialogView.js
          reviewboard/static/rb/js/models/commentEditorModel.js
      
      
    2. reviewboard/settings.py (Diff revision 2)
       
       
      Show all issues
       'django_reset' imported but unused
      
    3. reviewboard/settings.py (Diff revision 2)
       
       
      Show all issues
       'from settings_local import *' used; unable to detect undefined names
      
    4. 
        
    szhang
    1. Mostly just small indentation issues. :)

    2. reviewboard/reviews/default_actions.py (Diff revision 2)
       
       
      Show all issues

      Indent this by two more spaces?

      1. See the comment below, but the not is supposed to be inline with the first open parens.

    3. reviewboard/reviews/default_actions.py (Diff revision 2)
       
       
      Show all issues

      Indent this by one space as well.

      1. See the other comments re: indentation.

    4. Show all issues

      Indent this by one space here?

      1. Perhaps I can move the not ... to be the first term of this expression, but these have to be aligned with the inner-most open parenthesis for the value/subexpression. So here, the not is in-line with the first parenthesis right after return.

      2. Ah! My mistake. Too many opening/closing brackets confuse me. Sorry for the bad review.

        I'd say you don't need to move the not ... just to make the indentation a little more clear to people like me. :)

    5. Show all issues

      Move this after the if statement?

      Putting it before the if statement subtly gives the impression that the if statement uses it--which it doesn't.

      1. Hey Simon, I believe the coding style we use is to declare all variables at the top of the function.

    6. 
        
    brennie
    1. 
        
    2. reviewboard/admin/context_processors.py (Diff revision 2)
       
       
      Show all issues

      Missing args/returns.

    3. reviewboard/admin/read_only.py (Diff revision 2)
       
       
      Show all issues

      If you use the following, it will link to the class

      "the :py:class:`~djblets.siteconfig.models.SiteConfiguration`"
      
    4. reviewboard/admin/read_only.py (Diff revision 2)
       
       
      Show all issues

      We need to use full paths, e.g. django.contrib.auth.models.User

    5. reviewboard/datagrids/grids.py (Diff revision 2)
       
       
      Show all issues

      Missing args.

    6. reviewboard/datagrids/grids.py (Diff revision 2)
       
       
      Show all issues

      Missing args.

    7. We only require vars declared at the top for ES5 javascript (due to hoisting). It doesn't matter if you put this before or after the if statement.

    8. Show all issues

      Format as:

      ```javascript
      /
      * Comment
      * text
      /

      1. Do you mean like:

        /*
         * An error can be caused by a 503 when the site is in 
         * read-only mode, in which case the the fields will be
         * empty.
         */
        

        Wasn't too sure what you meant in your comment but I looked around more and most seem to look like this.

    9. Show all issues

      I'm unsure if this declaration should live here -- I don't think we do this anywhere else, really. Perhaps ping some other mentors about this.

    10. 
        
    chipx86
    1. 
        
    2. reviewboard/admin/read_only.py (Diff revision 2)
       
       
       
       
      Show all issues

      Missing:

      from __future__ import unicode_literals
      

      There must be a blank line between that import and any other imports.

    3. reviewboard/admin/read_only.py (Diff revision 2)
       
       
       
       
      Show all issues

      If user.is_superuser is True, there's no reason to bother with a cache. That's just as fast.

    4. reviewboard/datagrids/grids.py (Diff revision 2)
       
       
      Show all issues

      Where does disable_save come from? Is there a change for Djblets that isn't up for review?

      It's better to have options be positive rather than negative. For instance, "enable_save" vs. "disable_save." I'm not sure I love either, though.

      There are lots of places where a Profile gets modified, and I don't think we should special-case every single place in existence. Instead, I'd override Profile.save and have it not save if is_site_read_only_for returns True. That way, the DataGrid can try saving all it wants, but it won't matter.

      1. The change to djblets didn't show up here, but I agree that overriding Profile would be better. I thought it was a django class so it would be less work this way, but I found it in rb models. I changed the Profile.save() and tested that the override worked by ensuring the code reaches the conditional which returns early with a print statement.

    5. reviewboard/reviews/default_actions.py (Diff revision 2)
       
       
       
       
       
       
      Show all issues

      Let's pull out context['request'].user into a variable, so we're not doing the (actually fairly expensive, since context is not your standard kind of dict) lookup more than once.

      Same with all others that use this more than once.

      1. Done. Changed other instances of this below.

    6. reviewboard/reviews/models/review_request.py (Diff revision 2)
       
       
       
       
       
      Show all issues

      Let's move the read-only check last, as it's going to be less frequently a factor (it's more likely someone will fail the other checks before this one).

    7. reviewboard/reviews/models/review_request.py (Diff revision 2)
       
       
       
       
      Show all issues

      Same here.

    8. Show all issues

      Can you say why this happens? It's not going to be clear to people in the future that this means it's in read-only mode.

      1. Done. Updated to:

        /* 
         * An error can be caused by a 503 when the site is in 
         * read-only mode, in which case the the fields will be
         * empty.
         */
        
    9. Show all issues

      It'd be nice to only do this at one place. So instead, how about something like:

      if (rsp.fields === undefined) {
          message = xhr.errorText;
      } else {
          /* All the stuff here for computing an error message below. */
      }
      
      options.error.call(...);
      
    10. Show all issues

      No trailing comma. Since this isn't an ES6 JavaScript file, it will end up breaking some browsers (such as IE).

    11. Show all issues

      Isn't there always a value returned for readOnly? We probably don't need to specify a default.

    12. Show all issues

      We probably don't need to specify a default, since readOnly should always have a value.

      It would also be nice to log something here to let anyone diagnosing things know that the request was ignored because it's in read-only mode.

      1. I've added the following:

                console.log(options.type + " request not sent to the API because " +
                    "the site is in read-only mode");
        
    13. Show all issues

      We specify variables with values before variables without values.

      Also, I don't think we need a default here for readOnly.

    14. Show all issues

      Instead of this, can you just set actions to empty if in this mode? So:

      this.template({
          ...
          actions: (this.showActions ? this.actions : []),
          ...
      });
      
    15. reviewboard/webapi/base.py (Diff revision 2)
       
       
       
       
       
      Show all issues

      Maybe it'd make more sense to have the other change built on top of this one instead.

      1. That's true. I'll try and rearrange this soon.

      2. I've moved this change and read_only.py to the previous @ #8657.

    16. 
        
    KH
    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          reviewboard/reviews/views.py
          reviewboard/admin/read_only.py
          reviewboard/reviews/models/base_comment.py
          reviewboard/webapi/base.py
          reviewboard/reviews/default_actions.py
          reviewboard/reviews/models/review_request.py
          reviewboard/accounts/models.py
          reviewboard/admin/context_processors.py
          reviewboard/settings.py
          reviewboard/reviews/templatetags/reviewtags.py
      
      Ignored Files:
          reviewboard/static/rb/js/pages/views/dashboardView.js
          reviewboard/static/rb/js/views/starManagerView.js
          reviewboard/templates/reviews/review_reply_section.html
          reviewboard/templates/base/navbar.html
          reviewboard/static/rb/js/utils/apiUtils.es6.js
          reviewboard/templates/base.html
          reviewboard/templates/base/_nav_support_menu.html
          reviewboard/static/rb/js/models/userSessionModel.js
          reviewboard/static/rb/js/views/reviewRequestEditorView.js
          reviewboard/static/rb/js/models/reviewRequestEditorModel.js
          reviewboard/static/rb/js/views/commentDialogView.js
          reviewboard/static/rb/js/models/commentEditorModel.js
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          reviewboard/reviews/views.py
          reviewboard/admin/read_only.py
          reviewboard/reviews/models/base_comment.py
          reviewboard/webapi/base.py
          reviewboard/reviews/default_actions.py
          reviewboard/reviews/models/review_request.py
          reviewboard/accounts/models.py
          reviewboard/admin/context_processors.py
          reviewboard/settings.py
          reviewboard/reviews/templatetags/reviewtags.py
      
      Ignored Files:
          reviewboard/static/rb/js/pages/views/dashboardView.js
          reviewboard/static/rb/js/views/starManagerView.js
          reviewboard/templates/reviews/review_reply_section.html
          reviewboard/templates/base/navbar.html
          reviewboard/static/rb/js/utils/apiUtils.es6.js
          reviewboard/templates/base.html
          reviewboard/templates/base/_nav_support_menu.html
          reviewboard/static/rb/js/models/userSessionModel.js
          reviewboard/static/rb/js/views/reviewRequestEditorView.js
          reviewboard/static/rb/js/models/reviewRequestEditorModel.js
          reviewboard/static/rb/js/views/commentDialogView.js
          reviewboard/static/rb/js/models/commentEditorModel.js
      
      
    2. reviewboard/settings.py (Diff revision 3)
       
       
      Show all issues
       'django_reset' imported but unused
      
    3. reviewboard/settings.py (Diff revision 3)
       
       
      Show all issues
       'from settings_local import *' used; unable to detect undefined names
      
    4. 
        
    KH
    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          reviewboard/reviews/views.py
          reviewboard/reviews/models/base_comment.py
          reviewboard/reviews/default_actions.py
          reviewboard/reviews/templatetags/reviewtags.py
          reviewboard/reviews/models/review_request.py
          reviewboard/accounts/models.py
          reviewboard/admin/context_processors.py
          reviewboard/settings.py
      
      Ignored Files:
          reviewboard/static/rb/js/pages/views/dashboardView.js
          reviewboard/static/rb/js/views/starManagerView.js
          reviewboard/templates/reviews/review_reply_section.html
          reviewboard/templates/base/navbar.html
          reviewboard/static/rb/js/utils/apiUtils.es6.js
          reviewboard/templates/base.html
          reviewboard/templates/base/_nav_support_menu.html
          reviewboard/static/rb/js/models/userSessionModel.js
          reviewboard/static/rb/js/views/reviewRequestEditorView.js
          reviewboard/static/rb/js/models/reviewRequestEditorModel.js
          reviewboard/static/rb/js/views/commentDialogView.js
          reviewboard/static/rb/js/models/commentEditorModel.js
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          reviewboard/reviews/views.py
          reviewboard/reviews/models/base_comment.py
          reviewboard/reviews/default_actions.py
          reviewboard/reviews/templatetags/reviewtags.py
          reviewboard/reviews/models/review_request.py
          reviewboard/accounts/models.py
          reviewboard/admin/context_processors.py
          reviewboard/settings.py
      
      Ignored Files:
          reviewboard/static/rb/js/pages/views/dashboardView.js
          reviewboard/static/rb/js/views/starManagerView.js
          reviewboard/templates/reviews/review_reply_section.html
          reviewboard/templates/base/navbar.html
          reviewboard/static/rb/js/utils/apiUtils.es6.js
          reviewboard/templates/base.html
          reviewboard/templates/base/_nav_support_menu.html
          reviewboard/static/rb/js/models/userSessionModel.js
          reviewboard/static/rb/js/views/reviewRequestEditorView.js
          reviewboard/static/rb/js/models/reviewRequestEditorModel.js
          reviewboard/static/rb/js/views/commentDialogView.js
          reviewboard/static/rb/js/models/commentEditorModel.js
      
      
    2. reviewboard/settings.py (Diff revision 4)
       
       
      Show all issues
       'django_reset' imported but unused
      
    3. reviewboard/settings.py (Diff revision 4)
       
       
      Show all issues
       'from settings_local import *' used; unable to detect undefined names
      
    4. 
        
    brennie
    1. 
        
    2. Show all issues

      Mind wrapping your description & testing done at 72 characters? Also, can you change your testing done to be bullet points (- or *).

    3. Show all issues

      Does is_site_read_only_for correctly handle user=None ?

      1. Can user ever not be in context? Especially since this is for rendering a reply, it seems like a place where a user would have to have been determined for the context. Would it be safe to remove the default value of None on context.get('user', None)?

        I can also handle None in is_site_read_only_for just in case it can happen, but I figured user will always be something by the time it gets used by is_site_read_only_for

      2. Since context is a dict, context.get(key) has a default return of None, so you can get rid of that. However, you should just do user = context['user'].

    4. Show all issues

      Do we want this to be user-visible? Or is this only for debugging?

      1. I intended this to be a user-visible message as Christian in a previous review requested for some logging here to help with diagnosing stuff.

    5. 
        
    KH
    KH
    chipx86
    1. 
        
    2. reviewboard/accounts/models.py (Diff revision 5)
       
       
      Show all issues

      This needs Args.

      It should also start off by saying the main thing that it does: "Saves the profile to the database."

      Then, in the description, it should say that if the site is in read-only mode, the profile will not be saved.

    3. Show all issues

      Parameters should align.

      This should also use format strings:

      console.log('%s request not sent ...',
                  options.type);
      

      I'd also maybe use console.error instead.

    4. 
        
    KH
    brennie
    1. 
        
    2. reviewboard/accounts/models.py (Diff revision 6)
       
       
      Show all issues

      "The profile will only be saved if the user is not affected by read-only mode."

    3. Show all issues

      Mind adding Args/Returns while you're here?

    4. Show all issues

      Mind adding Args/Returns while you're here?

    5. Show all issues

      Could you put the summary on the first line of the docstring?

    6. Show all issues

      Mind adding Args/Returns while you're here?

    7. reviewboard/templates/base.html (Diff revision 6)
       
       
       
       
      Show all issues

      Instead of this, why not just

      readOnly: {% is_read_only|yesno:'true,false' %},
      

      That way the information is always available.

    8. 
        
    KH
    KH
    Review request changed
    Status:
    Completed
    Change Summary:
    Pushed to release-4.0.x (f16f83d)