{"action":"created","comment":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/comments/36553527","id":36553527,"diff_hunk":"@@ -0,0 +1,151 @@\n+;(function (define) {\n+    'use strict';\n+\n+    define([\n+        'backbone',\n+        'underscore',\n+        'gettext',\n+        'support/js/collections/certificate',\n+        'text!support/templates/certificates.underscore',\n+        'text!support/templates/certificates_results.underscore'\n+    ], function (Backbone, _, gettext, CertCollection, certificatesTpl, resultsTpl) {\n+        var view = Backbone.View.extend({\n+            events: {\n+                \"submit .certificates-form\": \"search\",\n+                \"click .btn-cert-regenerate\": \"regenerateCertificate\"\n+            },\n+\n+            initialize: function(options) {\n+                _.bindAll(this, \"search\", \"updateCertificates\", \"regenerateCertificate\", \"handleSearchError\");\n+                this.certificates = new CertCollection({});\n+                this.initialQuery = options.userQuery || null;\n+            },\n+\n+            render: function() {\n+                this.$el.html(_.template(certificatesTpl));\n+\n+                // If there is an initial query, then immediately trigger a search.\n+                // This is useful because it allows users to share search results:\n+                // if the URL contains ?query=\"foo\" then anyone who loads that URL\n+                // will automatically search for \"foo\".\n+                if (this.initialQuery) {\n+                    this.setUserQuery(this.initialQuery);\n+                    this.triggerSearch();\n+                }\n+\n+                return this;\n+            },\n+\n+            renderResults: function() {\n+                var context = {\n+                    certificates: this.certificates,\n+                };\n+\n+                this.setResults(_.template(resultsTpl, context));\n+            },\n+\n+            renderError: function(error) {\n+                var errorMsg = error || gettext(\"An unexpected error occurred.  Please try again.\");\n+                this.setResults(errorMsg);\n+            },\n+\n+            search: function(event) {\n+\n+                // Fetch the certificate collection for the given user\n+                var query = this.getUserQuery(),\n+                    url = \"/support/certificates?query=\" + query;\n+\n+                // Prevent form submission, since we're handling it ourselves.\n+                event.preventDefault();\n+\n+                // Push a URL into history with the search query as a GET parameter.\n+                // That way, if the user reloads the page or sends someone the link\n+                // then the same search will be performed on page load.\n+                window.history.pushState({}, window.document.title, url);\n+\n+                // Perform a search for the user's certificates.\n+                this.disableButtons();\n+                this.certificates.setUserQuery(query);\n+                this.certificates.fetch({\n+                    success: this.updateCertificates,\n+                    error: this.handleSearchError\n+                });\n+            },\n+\n+            regenerateCertificate: function(event) {\n+                var $button = $(event.target);\n+\n+                // Regenerate certificates for a particular user and course.\n+                // If this is successful, reload the certificate results so they show\n+                // the updated status.\n+                this.disableButtons();\n+                $.ajax({\n+                    url: \"/certificates/regenerate\",\n+                    type: \"POST\",\n+                    data: {\n+                        username: $button.data(\"username\"),\n+                        course_key: $button.data(\"course-key\"),\n+                    },\n+                    context: this,\n+                    success: function() {\n+                        this.certificates.fetch({\n+                            success: this.updateCertificates,\n+                            error: this.handleSearchError,\n+                        });\n+                    },\n+                    error: this.handleRegenerateError\n+                });\n+            },\n+\n+            updateCertificates: function() {\n+                this.renderResults();\n+                this.enableButtons();\n+            },\n+\n+            handleSearchError: function(jqxhr) {\n+                this.renderError(jqxhr.responseText);\n+                this.enableButtons();\n+            },\n+\n+            handleRegenerateError: function(jqxhr) {\n+                // Since there are multiple \"regenerate\" buttons on the page,\n+                // it's difficult to show the error message in the UI.\n+                // Since this page is used only by internal staff, I think the\n+                // quick-and-easy way is reasonable.\n+                alert(jqxhr.responseText);\n+                this.enableButtons();\n+            },\n+\n+            triggerSearch: function() {\n+                $('.certificates-form').submit();\n+            },\n+\n+            getUserQuery: function() {\n+                return $('.certificates-form input[name=\"query\"]').val();\n+            },\n+\n+            setUserQuery: function(query) {\n+                $('.certificates-form input[name=\"query\"]').val(query);\n+            },\n+\n+            setResults: function(html) {\n+                var $resultsDiv = $(\".certificates-results\", this.$el);\n+                $resultsDiv.html(html);","path":"lms/djangoapps/support/static/support/js/views/certificates.js","position":133,"original_position":133,"commit_id":"78413f075334fcec9e1bd7ae05d75ca621bd9020","original_commit_id":"78413f075334fcec9e1bd7ae05d75ca621bd9020","user":{"login":"AlasdairSwan","id":2052185,"avatar_url":"https://avatars.githubusercontent.com/u/2052185?v=3","gravatar_id":"","url":"https://api.github.com/users/AlasdairSwan","html_url":"https://github.com/AlasdairSwan","followers_url":"https://api.github.com/users/AlasdairSwan/followers","following_url":"https://api.github.com/users/AlasdairSwan/following{/other_user}","gists_url":"https://api.github.com/users/AlasdairSwan/gists{/gist_id}","starred_url":"https://api.github.com/users/AlasdairSwan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AlasdairSwan/subscriptions","organizations_url":"https://api.github.com/users/AlasdairSwan/orgs","repos_url":"https://api.github.com/users/AlasdairSwan/repos","events_url":"https://api.github.com/users/AlasdairSwan/events{/privacy}","received_events_url":"https://api.github.com/users/AlasdairSwan/received_events","type":"User","site_admin":false},"body":"Nitpick: you don't really need to create the var because the selector isn't reused within the function.","created_at":"2015-08-07T19:36:49Z","updated_at":"2015-08-07T19:36:49Z","html_url":"https://github.com/edx/edx-platform/pull/9240#discussion_r36553527","pull_request_url":"https://api.github.com/repos/edx/edx-platform/pulls/9240","_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments/36553527"},"html":{"href":"https://github.com/edx/edx-platform/pull/9240#discussion_r36553527"},"pull_request":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/9240"}}},"pull_request":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/9240","id":41924649,"html_url":"https://github.com/edx/edx-platform/pull/9240","diff_url":"https://github.com/edx/edx-platform/pull/9240.diff","patch_url":"https://github.com/edx/edx-platform/pull/9240.patch","issue_url":"https://api.github.com/repos/edx/edx-platform/issues/9240","number":9240,"state":"open","locked":false,"title":"Support team UI for regenerating certificates","user":{"login":"wedaly","id":2948394,"avatar_url":"https://avatars.githubusercontent.com/u/2948394?v=3","gravatar_id":"","url":"https://api.github.com/users/wedaly","html_url":"https://github.com/wedaly","followers_url":"https://api.github.com/users/wedaly/followers","following_url":"https://api.github.com/users/wedaly/following{/other_user}","gists_url":"https://api.github.com/users/wedaly/gists{/gist_id}","starred_url":"https://api.github.com/users/wedaly/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wedaly/subscriptions","organizations_url":"https://api.github.com/users/wedaly/orgs","repos_url":"https://api.github.com/users/wedaly/repos","events_url":"https://api.github.com/users/wedaly/events{/privacy}","received_events_url":"https://api.github.com/users/wedaly/received_events","type":"User","site_admin":false},"body":"Allow support staff to view and regenerate certificates.\r\n\r\n* Add new role for support staff.\r\n* Move dashboard/support functionality into a new Django app called \"support\".\r\n* Add support view for searching and regenerating certificates.\r\n* Refactor certificates views into separate files.\r\n\r\nJIRA: [ECOM-1781](https://openedx.atlassian.net/browse/ECOM-1781)\r\n\r\n# Screenshots\r\n\r\n* Support index page:\r\n![image](https://cloud.githubusercontent.com/assets/2948394/9143914/1b7162da-3cfe-11e5-8d5f-0757b325e659.png)\r\n\r\n* Certificates page\r\n![image](https://cloud.githubusercontent.com/assets/2948394/9143922/29c27694-3cfe-11e5-98a4-77a687b56ab7.png)\r\n\r\n* After a search\r\n![image](https://cloud.githubusercontent.com/assets/2948394/9143933/427c8d82-3cfe-11e5-99ba-38e851a43829.png)\r\n\r\n# Reviewers\r\n\r\nApologies in advance for the large PR!  I couldn't think of a reasonable way to break these changes into smaller PRs, so I'm going to assign people to review particular parts of this PR.\r\n\r\n@AlasdairSwan could you please review the JavaScript and underscore templates in the `support` app?\r\n@rlucioni could you please review the Python changes in the `certificates` app?\r\n\r\nFYI: @mattdrayer I refactored the certificates views into separate files, since the `views.py` file was getting pretty crowded.  I'm pretty sure this UI will work for webview certificates, with the exception of the \"download URL\" in the search results.  I'm going to set up a sandbox to test this with webview certs before merging.","created_at":"2015-08-07T19:21:54Z","updated_at":"2015-08-07T19:36:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"16151843710076a83f60f4344d9bd018ca90b964","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/edx/edx-platform/pulls/9240/commits","review_comments_url":"https://api.github.com/repos/edx/edx-platform/pulls/9240/comments","review_comment_url":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}","comments_url":"https://api.github.com/repos/edx/edx-platform/issues/9240/comments","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/78413f075334fcec9e1bd7ae05d75ca621bd9020","head":{"label":"edx:will/ecom-1781-rebase","ref":"will/ecom-1781-rebase","sha":"78413f075334fcec9e1bd7ae05d75ca621bd9020","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":10391073,"name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","created_at":"2013-05-30T20:20:38Z","updated_at":"2015-08-07T14:13:34Z","pushed_at":"2015-08-07T19:36:25Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":2082912,"stargazers_count":2068,"watchers_count":2068,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1209,"mirror_url":null,"open_issues_count":149,"forks":1209,"open_issues":149,"watchers":2068,"default_branch":"master"}},"base":{"label":"edx:master","ref":"master","sha":"3efbc369dd0d40d1f170b3b79fffe1e57749877a","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":10391073,"name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","created_at":"2013-05-30T20:20:38Z","updated_at":"2015-08-07T14:13:34Z","pushed_at":"2015-08-07T19:36:25Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":2082912,"stargazers_count":2068,"watchers_count":2068,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1209,"mirror_url":null,"open_issues_count":149,"forks":1209,"open_issues":149,"watchers":2068,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/9240"},"html":{"href":"https://github.com/edx/edx-platform/pull/9240"},"issue":{"href":"https://api.github.com/repos/edx/edx-platform/issues/9240"},"comments":{"href":"https://api.github.com/repos/edx/edx-platform/issues/9240/comments"},"review_comments":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/9240/comments"},"review_comment":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/9240/commits"},"statuses":{"href":"https://api.github.com/repos/edx/edx-platform/statuses/78413f075334fcec9e1bd7ae05d75ca621bd9020"}}}}