OSDN Git Service

implement AJAX style new user registration
authorhylom <hylom@users.sourceforge.jp>
Wed, 24 Oct 2018 12:09:39 +0000 (21:09 +0900)
committerhylom <hylom@users.sourceforge.jp>
Wed, 24 Oct 2018 12:09:39 +0000 (21:09 +0900)
src/newslash_web/lib/Newslash/Web.pm
src/newslash_web/lib/Newslash/Web/Controller/API/User.pm
src/newslash_web/lib/Newslash/Web/Controller/Login.pm
src/newslash_web/public/js/new-user.js
src/newslash_web/public/js/newslash.js
src/newslash_web/templates/login/newuser.html.tt2

index 0c14018..3bd1a39 100644 (file)
@@ -344,6 +344,7 @@ sub startup {
     $api->post('/login')->to('API::Login#login');
 
     $api->post('/newuser/validate')->to('API::User#validate_new_user');
+    $api->post('/newuser/create')->to('API::User#create_new_user');
 
     $api->get('/sidebar/item')->to('API::SidebarItem#get', seclev => 1);
 
index 07b7743..a09a5b9 100644 (file)
@@ -247,4 +247,41 @@ sub validate_new_user {
                          email => $email });
 }
 
+sub create_new_user {
+    my $c = shift;
+    my $params = $c->req->json;
+    my $nickname = $params->{nickname};
+    my $email = $params->{email};
+    my $options = $params->{options} || {};
+
+    if (ref($options) ne "HASH") {
+        $c->render(json => { error => 1,
+                             _message => "options parameter must be dictionary",
+                             nickname => $nickname,
+                             email => $email });
+        $c->rendered(400);
+        return;
+    }
+
+    $email =~ s/\s//g; # strip whitespace
+
+    my ($uid, $error) = $c->users->create_new_user($nickname, $email, $options);
+
+    if ($error) {
+        $c->render(json => { error => 1,
+                             id_error => $error->{id_error} || 0,
+                             email_error => $error->{email_error} || 0,
+                             nickname => $nickname,
+                             email => $email });
+        $c->rendered(400);
+        return;
+    }
+
+    $c->render(json => { nickname => $nickname,
+                         email => $email,
+                         uid => $uid
+                       });
+    return;
+}
+
 1;
index 73cd7d4..2af87b7 100644 (file)
@@ -92,8 +92,10 @@ sub newuser {
         $prefs->{mail_error} = "MAIL_NOT_MATCH";
     }
 
-    $c->render(prefs => $prefs);
-    return;
+    if ($error) {
+        $c->render(prefs => $prefs);
+        return;
+    }
 
     # check ID
 
index 0bd3804..2e6c8b1 100644 (file)
@@ -3,9 +3,9 @@ var newUser = {};
 newUser.run = function run(params) {
   var nickRegex = /^[a-zA-Z_][ a-zA-Z0-9$_.+!*\'(),-]{0,19}$/;
 
-  Vue.component('new-user-error-message', {
-    props: { error: String, target: String },
-    template: '#newuser-error-message',
+  Vue.component('new-user-message', {
+    props: ["target", ],
+    template: '#newuser-message',
   });
 
   var data = {
@@ -16,14 +16,40 @@ newUser.run = function run(params) {
     allowHeadline: 0,
     unicknameError: "BLANK_ID",
     emailError: "BLANK_EMAIL",
+    message: "",
     formError: true,
+    state: "",
   };
 
   var methods = {};
   methods.doPost = function doPost() {
+    this.state = "posting";
     newslash.validateNewUser(this.unickname, this.email).then(
       (resp) => {
         // ok
+        var options = { message: { news_letter: this.allowNewsletter,
+                                   headline: this.allowHeadline, }
+                      };
+        newslash.createNewUser(this.unickname, this.email, options).then(
+          (resp) => {
+            // ok
+            this.state = "succeed";
+            this.message = "SUCCEED";
+          },
+          (resp) => {
+            // failed
+            this.emailError = resp.email_error || false;
+            this.unicknameError = resp.id_error || false;
+            this.unicknane = resp.nickname || "";
+            this.email = resp.email || "";
+            this.formError = true;
+            this.state = "";
+            if (!this.emailError && !this.unicknameError) {
+              this.message = "SERVER_ERROR";
+              this.formError = false;
+            }
+          }
+        );
       },
       (resp) => {
         // failed
@@ -32,6 +58,7 @@ newUser.run = function run(params) {
         this.unicknane = resp.nickname || false;
         this.email = resp.email || false;
         this.formError = true;
+        this.state = "";
       });
   };
 
@@ -43,14 +70,18 @@ newUser.run = function run(params) {
     else { this.unicknameError = false; }
     this.formError = this.emailError || this.unicknameError;
   };
+
   watch.email = function watchEmail(val, oldVal) {
     if (val.length == 0) { this.emailError = "BLANK_EMAIL"; }
     else if (val != this.email2) { this.emailError = "EMAIL_NOT_MATCH"; }
+    else if (val.search(/.@\w/) < 0) { this.emailError = "INVALID_EMAIL"; }
     else { this.emailError = false; }
     this.formError = this.emailError || this.unicknameError;
   };
+
   watch.email2 = function watchEmail2(val, oldVal) {
-    if (val != this.email) { this.mailError = "EMAIL_NOT_MATCH"; }
+    if (val != this.email) { this.emailError = "EMAIL_NOT_MATCH"; }
+    else if (val.search(/.@\w/) < 0) { this.emailError = "INVALID_EMAIL"; }
     else { this.emailError = false; }
     this.formError = this.emailError || this.unicknameError;
   };
index 8b639c7..3f69a84 100644 (file)
@@ -25,18 +25,7 @@ function _initNewslash() {
     return parser;
   };
 
-  Newslash.prototype.login = function (username, password) {
-    var url = "/login";
-    var data = {nickname: username, password: password};
-    return this.post(url, data);
-  };
-
-  Newslash.prototype.validateNewUser = function (username, email) {
-    var url = "/newuser/validate";
-    var data = {nickname: username, email: email};
-    return this.post(url, data);
-  };
-
+  /* base get.post API */
   Newslash.prototype.get = function (path) {
     var url = this.baseUrl + path;
     return new FakePromise(function (resolve, reject) {
@@ -83,7 +72,7 @@ function _initNewslash() {
           }
           // request failed
           if (req.responseType == "json") {
-            if (req.response.reason == "invalid_csrf_token") {
+            if (req.response && req.response.reason == "invalid_csrf_token") {
               if (retryCount >= 3) {
                 return reject({error: 1, reason: "exceeded retry count"});
               }
@@ -109,6 +98,31 @@ function _initNewslash() {
     }
   };
 
+  /* login API */
+  Newslash.prototype.login = function (username, password) {
+    var url = "/login";
+    var data = {nickname: username, password: password};
+    return this.post(url, data);
+  };
+
+  /* newUser API */
+  Newslash.prototype.validateNewUser = function (username, email) {
+    var url = "/newuser/validate";
+    var data = {nickname: username, email: email};
+    return this.post(url, data);
+  };
+
+  Newslash.prototype.createNewUser = function (username, email, options) {
+    options = options || {};
+    var url = "/newuser/create";
+    var data = { nickname: username,
+                 email: email,
+                 options: options,
+               };
+    return this.post(url, data);
+  };
+
+  /* comments API */
   Newslash.prototype.getComments = function getComments (discussionID, parentID) {
     if (!discussionID) return this.fail();
 
index 0ab6c26..f041cb4 100644 (file)
@@ -1,13 +1,15 @@
 [% WRAPPER common/layout %]
 
-<script type="text/x-template" id="newuser-error-message">
-  <div class="alert-error"      v-if="error == 'BLANK_ID'">IDを入力してください</div>
-  <div class="alert-error" v-else-if="error == 'INVALID_ID'">IDに不正な文字が使われています。</div>
-  <div class="alert-error" v-else-if="error == 'ID_EXISTS'">すでにそのIDは使われています。</div>
-  <div class="alert-error" v-else-if="error == 'BLANK_EMAIL'">メールアドレスを入力してください。</div>
-  <div class="alert-error" v-else-if="error == 'INVALID_EMAIL'">不正なメールアドレスです。</div>
-  <div class="alert-error" v-else-if="error == 'EMAIL_NOT_MATCH'">確認用メールアドレスと一致しません。</div>
-  <div class="alert-error" v-else-if="error == 'EMAIL_EXISTS'">すでにそのメールアドレスは登録されています。</div>
+<script type="text/x-template" id="newuser-message">
+  <div class="alert-error"      v-if="target == 'BLANK_ID'">IDを入力してください</div>
+  <div class="alert-error" v-else-if="target == 'INVALID_ID'">IDに不正な文字が使われています。</div>
+  <div class="alert-error" v-else-if="target == 'ID_EXISTS'">すでにそのIDは使われています。</div>
+  <div class="alert-error" v-else-if="target == 'BLANK_EMAIL'">メールアドレスを入力してください。</div>
+  <div class="alert-error" v-else-if="target == 'INVALID_EMAIL'">不正なメールアドレスです。</div>
+  <div class="alert-error" v-else-if="target == 'EMAIL_NOT_MATCH'">確認用メールアドレスと一致しません。</div>
+  <div class="alert-error" v-else-if="target == 'EMAIL_EXISTS'">すでにそのメールアドレスは登録されています。</div>
+  <div class="alert-info" v-else-if="target == 'SUCCEED'">登録メールアドレスに確認メールを送信しました。メールを確認のうえ、記載されているURLにアクセスしてください。</div>
+  <div class="alert-error" v-else-if="target == 'SERVER_ERROR'">サーバーエラーが発生しました。</div>
 </script>
 
 <div class="main-contents" id="new-user-prefs">
@@ -23,8 +25,9 @@
           [%- ELSE %]IDエラー[% END -%]
         </div>
         [%- END %]
-        <new-user-error-message :error="unicknameError"></new-user-error-message>
+        <new-user-message :target="unicknameError"></new-user-message>
         <input id="unickname" type="text" name="unickname" placeholder="ログインID" v-model="unickname"
+               :disabled="state != ''"
                value="[% prefs.unickname %]"/>
       </label>
 
           [%- ELSE %]メールアドレスエラー[% END -%]
         </div>
         [%- END %]
-        <new-user-error-message target="email" :error="emailError"></new-user-error-message>
+        <new-user-message :target="emailError"></new-user-message>
         <input id="email" type="text" name="email" v-model="email" placeholder="メールアドレス"
+               :disabled="state != ''"
                value="[% prefs.email %]"/>
       </label>
 
       <label>メールアドレス(再入力):
         <input id="email2" type="text" name="email2" v-model="email2" placeholder="メールアドレス(再入力)"
+               :disabled="state != ''"
                value="[% prefs.email2 %]"/>
       </label>
 
       <label>
         <input type="checkbox" id="allow-newsletter" name="allow_newsletter"
+               :disabled="state != ''"
                [% IF !prefs.disallow_newsletter %]checked="checked"[% END %] value="1" v-model="allowNewsletter" />
         ニュースレターを受け取る
       </label>
 
       <label>
         <input type="checkbox" id="allow-headline" name="allow_headline"
+               :disabled="state != ''"
                [% IF prefs.allow_headline %]value="1"[% END %] v-model="allowHeadline" />
         毎日のヘッドラインを受け取る
       </label>
 
-      <input class="btn btn-primary" :disabled="formError" type="submit" @click.prevent.stop:="doPost" value="アカウントを作成する" />
+      <input class="btn btn-primary" :disabled="formError || state != ''" type="submit" @click.prevent.stop:="doPost" value="アカウントを作成する" />
+      <new-user-message :target="message"></new-user-message>
     </form>
   </div><!-- .panel -->
 </div>