You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2015/05/28 22:13:19 UTC
[17/26] allura git commit: [#7868] ticket:760 UI for phone
verification
[#7868] ticket:760 UI for phone verification
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/9c8a9029
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/9c8a9029
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/9c8a9029
Branch: refs/heads/master
Commit: 9c8a9029702bc694469c7ff273ee07ee527a0650
Parents: cb528b7
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon May 11 14:16:27 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu May 28 20:11:46 2015 +0000
----------------------------------------------------------------------
Allura/allura/nf/allura/css/allura.css | 4 +
.../allura/public/nf/js/phone-verification.js | 166 +++++++++++++++++++
.../templates/phone_verification_fragment.html | 21 ++-
3 files changed, 190 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/9c8a9029/Allura/allura/nf/allura/css/allura.css
----------------------------------------------------------------------
diff --git a/Allura/allura/nf/allura/css/allura.css b/Allura/allura/nf/allura/css/allura.css
index 7d7042a..6c5c1f4 100644
--- a/Allura/allura/nf/allura/css/allura.css
+++ b/Allura/allura/nf/allura/css/allura.css
@@ -83,3 +83,7 @@ tr.rev div.markdown_content p {
#phone_verification_overlay iframe {
width: 400px;
}
+
+#phone_verification_overlay iframe {
+ height: 250px;
+}
http://git-wip-us.apache.org/repos/asf/allura/blob/9c8a9029/Allura/allura/public/nf/js/phone-verification.js
----------------------------------------------------------------------
diff --git a/Allura/allura/public/nf/js/phone-verification.js b/Allura/allura/public/nf/js/phone-verification.js
new file mode 100644
index 0000000..225d2a9
--- /dev/null
+++ b/Allura/allura/public/nf/js/phone-verification.js
@@ -0,0 +1,166 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+var dom = React.createElement;
+var grid = 'grid-8';
+
+/* top-level form state */
+var state = {
+ 'step': 'verify', // 'verify' (enter number) or 'check' (enter pin)
+ 'in_progress': false, // API call in progress
+ 'error': null, // error, returned from previous API call
+ 'number': null, // phone number, entered by user on 'verify' step
+ 'pin': null, // PIN, entered by user on 'check' step
+};
+
+
+function set_state(new_state) {
+ /* Set state and re-render entire UI */
+ for (var key in new_state) {
+ state[key] = new_state[key];
+ }
+ render(state);
+}
+
+function render(state) {
+ /* Mount top-level component into the DOM */
+ React.render(
+ dom(PhoneVerificationForm, {state: state}),
+ document.getElementById('phone-verification-form')
+ );
+}
+
+var FormStepMixin = {
+
+ /*
+ * Subclasses must implement:
+ * - getAPIUrl(): return API url this step will submit to
+ * - getAPIData(): returns data to submit to API url
+ * - getLabel(): returns label text for the input
+ * - getKey(): returns key in the global state, which will be updated by the input
+ * - onSuccess(resp): callback wich will be called after successful call to API
+ */
+
+ render: function() {
+ var input_props = {
+ type: 'text',
+ className: grid,
+ value: this.props.state[this.getKey()],
+ disabled: this.isInputDisabled(),
+ onChange: this.handleChange,
+ onKeyDown: this.onKeyDown
+ };
+ var button_props = {
+ onClick: this.handleClick,
+ disabled: this.isButtonDisabled()
+ };
+ var nbsp = String.fromCharCode(160);
+ return dom('div', null,
+ dom('label', {className: grid}, this.getLabel()),
+ dom('input', input_props),
+ dom('div', {className: grid + ' error-text'}, this.props.state.error || nbsp),
+ dom('div', {className: grid},
+ dom('button', button_props, 'Submit')));
+ },
+
+ handleClick: function() {
+ if (!this.isButtonDisabled()) {
+ set_state({error: null});
+ this.callAPI();
+ }
+ },
+
+ handleChange: function(event) {
+ var new_state = {};
+ new_state[this.getKey()] = event.target.value;
+ set_state(new_state);
+ },
+
+ onKeyDown: function(event) {
+ if (event.key == 'Enter') {
+ this.handleClick();
+ }
+ },
+
+ isInputDisabled() { return this.props.state.in_progress; },
+
+ isButtonDisabled() {
+ var input = this.props.state[this.getKey()];
+ var has_input = Boolean(input);
+ return this.isInputDisabled() || !has_input;
+ },
+
+ callAPI: function() {
+ var url = this.getAPIUrl();
+ var data = this.getAPIData();
+ var csrf = $.cookie('_session_id');
+ data._session_id = csrf;
+ set_state({in_progress: true});
+ $.post(url, data, function(resp) {
+ if (resp.status == 'ok') {
+ this.onSuccess(resp);
+ } else {
+ set_state({error: resp.error});
+ }
+ }.bind(this)).fail(function() {
+ var error = 'Request to API failed, please try again';
+ set_state({error: error});
+ }).always(function() {
+ set_state({in_progress: false});
+ });
+ }
+};
+
+
+var StepVerify = React.createClass({
+ mixins: [FormStepMixin],
+
+ getAPIUrl: function() { return 'verify_phone'; },
+ getAPIData: function() { return {'number': this.props.state[this.getKey()]}; },
+ getLabel: function() { return 'Enter phone number'; },
+ getKey: function() { return 'number'; },
+ onSuccess: function() { set_state({step: 'check'}); }
+});
+
+var StepCheck = React.createClass({
+ mixins: [FormStepMixin],
+
+ getAPIUrl: function() { return 'check_phone_verification'; },
+ getAPIData: function() { return {'pin': this.props.state[this.getKey()]}; },
+ getLabel: function() { return 'Enter PIN'; },
+ getKey: function() { return 'pin'; },
+ onSuccess: function() { window.top.location.reload(); }
+});
+
+var PhoneVerificationForm = React.createClass({
+ /*
+ * Top-level component for phone verification form
+ * Used as controller, to render proper form step.
+ */
+ render: function() {
+ var step = this.props.state.step;
+ if (step == 'verify') {
+ return dom(StepVerify, {state: this.props.state});
+ } else if (step == 'check') {
+ return dom(StepCheck, {state: this.props.state});
+ }
+ }
+});
+
+render(state); // initial render
http://git-wip-us.apache.org/repos/asf/allura/blob/9c8a9029/Allura/allura/templates/phone_verification_fragment.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/phone_verification_fragment.html b/Allura/allura/templates/phone_verification_fragment.html
index 87b0f1b..faa0009 100644
--- a/Allura/allura/templates/phone_verification_fragment.html
+++ b/Allura/allura/templates/phone_verification_fragment.html
@@ -23,6 +23,8 @@
{% import g.theme.jinja_macros as theme_macros with context %}
{% endif %}
{% do g.register_forge_js('js/react.min.js', location='head_js') %}
+{% do g.register_forge_js('js/jquery-base.js', location='head_js') %}
+{% do g.register_forge_js('js/phone-verification.js', location='body_js_tail') %}
{% do g.theme.require() %}
{% do g.resource_manager.register_widgets(c) %}
{# paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ #}
@@ -42,10 +44,27 @@
<style type="text/css">
html { overflow: hidden; }
body { padding-top: 1em; }
+ .error-text {
+ color: red;
+ margin-top: .5em;
+ margin-bottom: .5em;
+ }
</style>
</head>
<body>
- TODO: Phone verification UI
+ <div class="grid-9">
+ {% block phone_validation_message %}
+ <p>
+ In order to register a project you need to perform phone validation
+ (just this once). Please, enter your phone number below. You'll
+ receive an SMS message with a PIN you need to provide on the next
+ step. Your phone number will not be stored.
+ </p>
+ {% endblock phone_validation_message %}
+ <div id="phone-verification-form">
+ {# Phone verification form will be mounted here (see phone-verification.js) #}
+ </div>
+ </div>
{% for blob in g.resource_manager.emit('body_js_tail') %}
{{ blob }}