mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Large refactor of accounts-ui
- Split login_buttons.html and login_buttons.js into multiple files - Specifically, make it easier to reason about the display of loginButtons whether it is in dropdown mode or not - Split templates into subtemplates to make it easier to read the login button .html files as "tables of contents" - Introduce Meteor._loginButtonsSession, which makes it easier to access internal session fields for loginButtons - Unify code that calls the various Meteor.loginWithFoo() functions Breaking change: rename "configureLoginServicesDialogForFoo" to "configureLoginServiceDialogForFoo" (in packages such as accounts-facebook)
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
<template name="configureLoginServicesDialogForFacebook">
|
<template name="configureLoginServiceDialogForFacebook">
|
||||||
<p>
|
<p>
|
||||||
First, you'll need to register your app on Facebook. Follow these steps:
|
First, you'll need to register your app on Facebook. Follow these steps:
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
Template.configureLoginServicesDialogForFacebook.siteUrl = function () {
|
Template.configureLoginServiceDialogForFacebook.siteUrl = function () {
|
||||||
return Meteor.absoluteUrl();
|
return Meteor.absoluteUrl();
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.configureLoginServicesDialogForFacebook.fields = function () {
|
Template.configureLoginServiceDialogForFacebook.fields = function () {
|
||||||
return [
|
return [
|
||||||
{property: 'appId', label: 'App ID'},
|
{property: 'appId', label: 'App ID'},
|
||||||
{property: 'secret', label: 'App Secret'}
|
{property: 'secret', label: 'App Secret'}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template name="configureLoginServicesDialogForGithub">
|
<template name="configureLoginServiceDialogForGithub">
|
||||||
<p>
|
<p>
|
||||||
First, you'll need to get a Github Client ID. Follow these steps:
|
First, you'll need to get a Github Client ID. Follow these steps:
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
Template.configureLoginServicesDialogForGithub.siteUrl = function () {
|
Template.configureLoginServiceDialogForGithub.siteUrl = function () {
|
||||||
return Meteor.absoluteUrl();
|
return Meteor.absoluteUrl();
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.configureLoginServicesDialogForGithub.fields = function () {
|
Template.configureLoginServiceDialogForGithub.fields = function () {
|
||||||
return [
|
return [
|
||||||
{property: 'clientId', label: 'Client ID'},
|
{property: 'clientId', label: 'Client ID'},
|
||||||
{property: 'secret', label: 'Client Secret'}
|
{property: 'secret', label: 'Client Secret'}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template name="configureLoginServicesDialogForGoogle">
|
<template name="configureLoginServiceDialogForGoogle">
|
||||||
<p>
|
<p>
|
||||||
First, you'll need to get a Google Client ID. Follow these steps:
|
First, you'll need to get a Google Client ID. Follow these steps:
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
Template.configureLoginServicesDialogForGoogle.siteUrl = function () {
|
Template.configureLoginServiceDialogForGoogle.siteUrl = function () {
|
||||||
return Meteor.absoluteUrl();
|
return Meteor.absoluteUrl();
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.configureLoginServicesDialogForGoogle.fields = function () {
|
Template.configureLoginServiceDialogForGoogle.fields = function () {
|
||||||
return [
|
return [
|
||||||
{property: 'clientId', label: 'Client ID'},
|
{property: 'clientId', label: 'Client ID'},
|
||||||
{property: 'secret', label: 'Client secret'}
|
{property: 'secret', label: 'Client secret'}
|
||||||
|
|||||||
@@ -156,6 +156,7 @@
|
|||||||
if (error || !result) {
|
if (error || !result) {
|
||||||
error = error || new Error("No result from call to resetPassword");
|
error = error || new Error("No result from call to resetPassword");
|
||||||
callback && callback(error);
|
callback && callback(error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Accounts._makeClientLoggedIn(result.id, result.token);
|
Accounts._makeClientLoggedIn(result.id, result.token);
|
||||||
@@ -178,6 +179,7 @@
|
|||||||
if (error || !result) {
|
if (error || !result) {
|
||||||
error = error || new Error("No result from call to validateUser");
|
error = error || new Error("No result from call to validateUser");
|
||||||
callback && callback(error);
|
callback && callback(error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Accounts._makeClientLoggedIn(result.id, result.token);
|
Accounts._makeClientLoggedIn(result.id, result.token);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template name="configureLoginServicesDialogForTwitter">
|
<template name="configureLoginServiceDialogForTwitter">
|
||||||
<p>
|
<p>
|
||||||
First, you'll need to register your app on Twitter. Follow these steps:
|
First, you'll need to register your app on Twitter. Follow these steps:
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
Template.configureLoginServicesDialogForTwitter.siteUrl = function () {
|
Template.configureLoginServiceDialogForTwitter.siteUrl = function () {
|
||||||
// Twitter doesn't recognize localhost as a domain name
|
// Twitter doesn't recognize localhost as a domain name
|
||||||
return Meteor.absoluteUrl({replaceLocalhost: true});
|
return Meteor.absoluteUrl({replaceLocalhost: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.configureLoginServicesDialogForTwitter.fields = function () {
|
Template.configureLoginServiceDialogForTwitter.fields = function () {
|
||||||
return [
|
return [
|
||||||
{property: 'consumerKey', label: 'Consumer key'},
|
{property: 'consumerKey', label: 'Consumer key'},
|
||||||
{property: 'secret', label: 'Consumer secret'}
|
{property: 'secret', label: 'Consumer secret'}
|
||||||
|
|||||||
@@ -1,294 +1,50 @@
|
|||||||
<template name="loginButtons">
|
<template name="loginButtons">
|
||||||
<div id="login-buttons">
|
<div id="login-buttons">
|
||||||
{{#if currentUser}}
|
{{#if currentUser}}
|
||||||
{{#if dropdown}}
|
{{> loginButtonsLoggedIn}}
|
||||||
{{> loginButtonsLoggedInDropdown}}
|
|
||||||
{{else}}
|
|
||||||
{{> loginButtonsLoggedInRow}}
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if services}}
|
{{> loginButtonsLoggedOut}}
|
||||||
{{#if configurationLoaded}}
|
|
||||||
{{#if dropdown}}
|
|
||||||
{{> loginButtonsServicesDropdown}}
|
|
||||||
{{else}}
|
|
||||||
{{> loginButtonsServicesRow}}
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
<div class="no-services">No login services configured</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="loginButtonsLoggedInDropdown">
|
<template name="loginButtonsLoggedIn">
|
||||||
<div class="login-link-and-dropdown-list">
|
{{#if dropdown}}
|
||||||
{{#if currentUser.loading}}
|
{{> loginButtonsLoggedInDropdown}}
|
||||||
<div class="loading"></div>
|
{{else}}
|
||||||
{{else}}
|
<div class="login-header">
|
||||||
<a class="login-link-text" id="login-name-link">
|
{{#if currentUser.loading}} {{! XXX this will change }}
|
||||||
{{displayName}} ▾
|
<div class="loading"></div>
|
||||||
</a>
|
|
||||||
{{#if dropdownVisible}}
|
|
||||||
<div id="login-dropdown-list" class="accounts-dialog">
|
|
||||||
<a class="login-close-text">Close</a>
|
|
||||||
<div class="login-close-text-clear"></div>
|
|
||||||
|
|
||||||
{{#if inChangePasswordFlow}}
|
|
||||||
{{> loginButtonsChangePassword}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if allowChangingPassword}}
|
{{displayName}}
|
||||||
<div class="login-button" id="login-buttons-open-change-password">Change password</div>
|
|
||||||
{{/if}}
|
|
||||||
<div class="login-button" id="login-buttons-logout">Logout</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
<div class="login-button" id="login-buttons-logout">Logout</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="loginButtonsLoggedInRow">
|
<template name="loginButtonsLoggedOut">
|
||||||
<div class="login-header">
|
{{#if services}} {{! if at least one service is configured }}
|
||||||
{{#if currentUser.loading}}
|
{{#if configurationLoaded}}
|
||||||
<div class="loading"></div>
|
{{#if dropdown}} {{! if more than one service configured, or password is configured}}
|
||||||
{{else}}
|
{{> loginButtonsLoggedOutDropdown}}
|
||||||
{{displayName}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="login-button" id="login-buttons-logout">Logout</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="loginButtonsServicesRow">
|
|
||||||
{{#each services}}
|
|
||||||
{{#if isPasswordService}}
|
|
||||||
{{#if hasOtherServices}}
|
|
||||||
<div class="or">
|
|
||||||
<span class="hline"> </span>
|
|
||||||
<span class="or-text">or</span>
|
|
||||||
<span class="hline"> </span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{#if inForgotPasswordFlow}}
|
|
||||||
{{> forgotPasswordForm}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="login-form login-password-form">
|
{{#with singleService}} {{! at this point there must be only one configured services }}
|
||||||
{{#each fields}}
|
{{> loginButtonsLoggedOutSingleLoginButton}}
|
||||||
{{> loginButtonsFormField}}
|
{{/with}}
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{> loginButtonsMessages}}
|
|
||||||
|
|
||||||
<div class="login-button login-button-form-submit" id="login-buttons-password">
|
|
||||||
{{#if inSignupFlow}}
|
|
||||||
Create account
|
|
||||||
{{else}}
|
|
||||||
Sign in
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if inLoginFlow}}
|
|
||||||
<div class="additional-link-container">
|
|
||||||
<a id="signup-link" class="additional-link">Create account</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if showForgotPasswordLink}}
|
|
||||||
<div class="additional-link-container">
|
|
||||||
<a id="forgot-password-link" class="additional-link">Forgot password</a>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
{{#if configured}}
|
|
||||||
<div class="login-button" id="login-buttons-{{name}}">
|
|
||||||
<div class="login-image" id="login-buttons-image-{{name}}"></div>
|
|
||||||
Sign in with {{capitalizedName}}
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="login-button configure-button" id="login-buttons-{{name}}">
|
|
||||||
<div class="login-image" id="login-buttons-image-{{name}}"></div>
|
|
||||||
Configure {{capitalizedName}} Login
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/each}}
|
{{else}}
|
||||||
</template>
|
<div class="no-services">No login services configured</div>
|
||||||
|
{{/if}}
|
||||||
<template name="loginButtonsFormField">
|
|
||||||
<label id="login-{{fieldName}}-label" for="login-{{fieldName}}">
|
|
||||||
{{fieldLabel}}
|
|
||||||
</label>
|
|
||||||
<input id="login-{{fieldName}}" type="{{inputType}}" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- used in various places to display messages to user -->
|
||||||
<template name="loginButtonsMessages">
|
<template name="loginButtonsMessages">
|
||||||
{{#if errorMessage}}
|
{{#if errorMessage}}
|
||||||
<div class="message error-message">{{errorMessage}}</div>
|
<div class="message error-message">{{errorMessage}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if infoMessage}}
|
{{#if infoMessage}}
|
||||||
<div class="message info-message">{{infoMessage}}</div>
|
<div class="message info-message">{{infoMessage}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="loginButtonsServicesDropdown">
|
|
||||||
<div class="login-link-and-dropdown-list">
|
|
||||||
<a class="login-link-text" id="login-sign-in-link">Sign in ▾</a>
|
|
||||||
{{#if dropdownVisible}}
|
|
||||||
<div id="login-dropdown-list" class="accounts-dialog">
|
|
||||||
<a class="login-close-text">Close</a>
|
|
||||||
<div class="login-close-text-clear"></div>
|
|
||||||
{{> loginButtonsServicesRow}}
|
|
||||||
|
|
||||||
{{#unless hasPasswordService}}
|
|
||||||
{{> loginButtonsMessages}}
|
|
||||||
{{/unless}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="forgotPasswordForm">
|
|
||||||
<div class="login-form">
|
|
||||||
<label id="forgot-password-email-label" for="forgot-password-email">Email</label>
|
|
||||||
<input id="forgot-password-email"/>
|
|
||||||
|
|
||||||
{{> loginButtonsMessages}}
|
|
||||||
|
|
||||||
<div class="login-button login-button-form-submit" id="login-buttons-forgot-password">
|
|
||||||
Reset password
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="loginButtonsChangePassword">
|
|
||||||
{{#each fields}}
|
|
||||||
{{> loginButtonsFormField}}
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{> loginButtonsMessages}}
|
|
||||||
|
|
||||||
<div class="login-button login-button-form-submit" id="login-buttons-do-change-password">
|
|
||||||
Change password
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="resetPasswordForm">
|
|
||||||
{{#if inResetPasswordFlow}}
|
|
||||||
<div class="hide-background"></div>
|
|
||||||
|
|
||||||
<div class="accounts-dialog accounts-centered-dialog">
|
|
||||||
<label id="reset-password-new-password-label" for="reset-password-new-password">
|
|
||||||
New password
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input id="reset-password-new-password" type="password" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{> loginButtonsMessages}}
|
|
||||||
|
|
||||||
<div class="login-button login-button-form-submit" id="login-buttons-reset-password-button">
|
|
||||||
Reset password
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a class="additional-link" id="login-buttons-cancel-reset-password">
|
|
||||||
Cancel
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="enrollAccountForm">
|
|
||||||
{{#if inEnrollAccountFlow}}
|
|
||||||
<div class="hide-background"></div>
|
|
||||||
|
|
||||||
<div class="accounts-dialog accounts-centered-dialog">
|
|
||||||
<label id="enroll-account-password-label" for="enroll-account-password">
|
|
||||||
Choose a password
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input id="enroll-account-password" type="password" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{> loginButtonsMessages}}
|
|
||||||
|
|
||||||
<div class="login-button login-button-form-submit" id="login-buttons-enroll-account-button">
|
|
||||||
Create account
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a class="additional-link" id="login-buttons-cancel-enroll-account">
|
|
||||||
Cancel
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="justValidatedUserForm">
|
|
||||||
{{#if visible}}
|
|
||||||
<div class="accounts-dialog accounts-centered-dialog">
|
|
||||||
Email validated
|
|
||||||
<div class="login-button" id="just-validated-dismiss-button">Dismiss</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- if we're not showing a dropdown, we need some other place to show messages -->
|
|
||||||
<template name="loginButtonsMessagesDialog">
|
|
||||||
{{#if visible}}
|
|
||||||
<div class="accounts-dialog accounts-centered-dialog">
|
|
||||||
{{> loginButtonsMessages}}
|
|
||||||
<div class="login-button" id="messages-dialog-dismiss-button">Dismiss</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="configureLoginServicesDialog">
|
|
||||||
{{#if visible}}
|
|
||||||
<div id="configure-login-services-dialog" class="accounts-dialog accounts-centered-dialog">
|
|
||||||
{{{configurationSteps}}}
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Now, copy over some details.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<table>
|
|
||||||
<colgroup>
|
|
||||||
<col span="1" class="configuration_labels">
|
|
||||||
<col span="1" class="configuration_inputs">
|
|
||||||
</colgroup>
|
|
||||||
{{#each configurationFields}}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<label for="configure-login-services-dialog-{{property}}">{{label}}</label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input id="configure-login-services-dialog-{{property}}" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</table>
|
|
||||||
</p>
|
|
||||||
<div class="new-section">
|
|
||||||
<div class="login-button" id="configure-login-services-dismiss-button">I'll do this later</div>
|
|
||||||
{{#isolate}}
|
|
||||||
<div class="login-button login-button-configure {{#if saveDisabled}}login-button-disabled{{/if}}"
|
|
||||||
id="configure-login-services-dialog-save-configuration">
|
|
||||||
Save Configuration
|
|
||||||
</div>
|
|
||||||
{{/isolate}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
{{> resetPasswordForm}}
|
|
||||||
{{> enrollAccountForm}}
|
|
||||||
{{> justValidatedUserForm}}
|
|
||||||
{{> loginButtonsMessagesDialog}}
|
|
||||||
{{> configureLoginServicesDialog}}
|
|
||||||
</body>
|
|
||||||
|
|||||||
@@ -1,324 +1,59 @@
|
|||||||
(function () {
|
(function () {
|
||||||
//
|
if (!Accounts._loginButtons)
|
||||||
// Session
|
Accounts._loginButtons = {};
|
||||||
//
|
|
||||||
|
|
||||||
var DROPDOWN_VISIBLE_KEY = 'Meteor.loginButtons.dropdownVisible';
|
// for convenience
|
||||||
|
var loginButtonsSession = Accounts._loginButtonsSession;
|
||||||
// XXX consider replacing these with one key that has an enum for values.
|
|
||||||
var IN_SIGNUP_FLOW_KEY = 'Meteor.loginButtons.inSignupFlow';
|
|
||||||
var IN_FORGOT_PASSWORD_FLOW_KEY = 'Meteor.loginButtons.inForgotPasswordFlow';
|
|
||||||
var IN_CHANGE_PASSWORD_FLOW_KEY = 'Meteor.loginButtons.inChangePasswordFlow';
|
|
||||||
|
|
||||||
var ERROR_MESSAGE_KEY = 'Meteor.loginButtons.errorMessage';
|
|
||||||
var INFO_MESSAGE_KEY = 'Meteor.loginButtons.infoMessage';
|
|
||||||
var SHOW_MESSAGES_DIALOG_KEY = 'Meteor.loginButtons.showMessagesDialog';
|
|
||||||
|
|
||||||
var RESET_PASSWORD_TOKEN_KEY = 'Meteor.loginButtons.resetPasswordToken';
|
|
||||||
var ENROLL_ACCOUNT_TOKEN_KEY = 'Meteor.loginButtons.enrollAccountToken';
|
|
||||||
var JUST_VALIDATED_USER_KEY = 'Meteor.loginButtons.justValidatedUser';
|
|
||||||
var CONFIGURE_LOGIN_SERVICES_DIALOG_VISIBLE = 'Meteor.loginButtons.configureLoginServicesDialogVisible';
|
|
||||||
var CONFIGURE_LOGIN_SERVICES_DIALOG_SERVICE_NAME = "Meteor.loginButtons.configureLoginServicesDialogServiceName";
|
|
||||||
var CONFIGURE_LOGIN_SERVICES_DIALOG_SAVE_ENABLED = "Meteor.loginButtons.saveEnabled";
|
|
||||||
|
|
||||||
|
|
||||||
var resetSession = function () {
|
|
||||||
Session.set(IN_SIGNUP_FLOW_KEY, false);
|
|
||||||
Session.set(IN_FORGOT_PASSWORD_FLOW_KEY, false);
|
|
||||||
Session.set(IN_CHANGE_PASSWORD_FLOW_KEY, false);
|
|
||||||
Session.set(DROPDOWN_VISIBLE_KEY, false);
|
|
||||||
resetMessages();
|
|
||||||
};
|
|
||||||
|
|
||||||
var resetMessages = function () {
|
|
||||||
Session.set(ERROR_MESSAGE_KEY, null);
|
|
||||||
Session.set(INFO_MESSAGE_KEY, null);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// loginButtons template
|
|
||||||
//
|
|
||||||
|
|
||||||
configureService = function(name) {
|
|
||||||
Session.set(CONFIGURE_LOGIN_SERVICES_DIALOG_VISIBLE, true);
|
|
||||||
Session.set(CONFIGURE_LOGIN_SERVICES_DIALOG_SERVICE_NAME, name);
|
|
||||||
Session.set(CONFIGURE_LOGIN_SERVICES_DIALOG_SAVE_ENABLED, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtons.events = {
|
|
||||||
'click #login-buttons-facebook': function () {
|
|
||||||
resetMessages();
|
|
||||||
Meteor.loginWithFacebook(makeLoginCallback('facebook'));
|
|
||||||
},
|
|
||||||
|
|
||||||
'click #login-buttons-google': function () {
|
|
||||||
resetMessages();
|
|
||||||
Meteor.loginWithGoogle(makeLoginCallback('google'));
|
|
||||||
},
|
|
||||||
|
|
||||||
'click #login-buttons-github': function () {
|
|
||||||
resetMessages();
|
|
||||||
Meteor.loginWithGithub(makeLoginCallback('github'))
|
|
||||||
},
|
|
||||||
|
|
||||||
'click #login-buttons-weibo': function () {
|
|
||||||
resetMessages();
|
|
||||||
Meteor.loginWithWeibo(makeLoginCallback('weibo'));
|
|
||||||
},
|
|
||||||
|
|
||||||
'click #login-buttons-twitter': function () {
|
|
||||||
resetMessages();
|
|
||||||
Meteor.loginWithTwitter(makeLoginCallback('twitter'));
|
|
||||||
},
|
|
||||||
|
|
||||||
'click #login-name-link': function () {
|
|
||||||
Session.set(DROPDOWN_VISIBLE_KEY, true);
|
|
||||||
Meteor.flush();
|
|
||||||
correctDropdownZIndexes();
|
|
||||||
},
|
|
||||||
|
|
||||||
'click .login-close-text': function () {
|
|
||||||
resetSession();
|
|
||||||
},
|
|
||||||
|
|
||||||
'click #login-buttons-open-change-password': function() {
|
|
||||||
resetMessages();
|
|
||||||
Session.set(IN_CHANGE_PASSWORD_FLOW_KEY, true);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
// shared between dropdown and single mode
|
||||||
|
Template.loginButtons.events({
|
||||||
'click #login-buttons-logout': function() {
|
'click #login-buttons-logout': function() {
|
||||||
Meteor.logout(resetSession);
|
Meteor.logout(function () {
|
||||||
|
loginButtonsSession.closeDropdown();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// loginButtonLoggedOut template
|
||||||
|
//
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOut.dropdown = function () {
|
||||||
|
return Accounts._loginButtons.dropdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeLoginCallback = function(service) {
|
Template.loginButtonsLoggedOut.services = function () {
|
||||||
return function (err) {
|
return Accounts._loginButtons.getLoginServices();
|
||||||
if (!err) {
|
|
||||||
resetSession();
|
|
||||||
} else if (err instanceof Accounts.LoginCancelledError) {
|
|
||||||
// do nothing
|
|
||||||
} else if (err instanceof Accounts.ConfigError) {
|
|
||||||
configureService(service);
|
|
||||||
} else {
|
|
||||||
Session.set(ERROR_MESSAGE_KEY, err.reason || "Unknown error");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// decide whether we should show a dropdown rather than a row of
|
Template.loginButtonsLoggedOut.singleService = function () {
|
||||||
// buttons
|
var services = Accounts._loginButtons.getLoginServices();
|
||||||
Template.loginButtons.dropdown = function () {
|
if (services.length !== 1)
|
||||||
return dropdown();
|
throw new Error(
|
||||||
|
"Shouldn't be rendering this template with more than one configured service");
|
||||||
|
return services[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.loginButtons.services = function () {
|
Template.loginButtonsLoggedOut.configurationLoaded = function () {
|
||||||
return getLoginServices();
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtons.configurationLoaded = function () {
|
|
||||||
return Accounts.loginServicesConfigured();
|
return Accounts.loginServicesConfigured();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// loginButtonsLoggedInRow template
|
// loginButtonsLoggedIn template
|
||||||
//
|
|
||||||
Template.loginButtonsLoggedInRow.displayName = function () {
|
|
||||||
return displayName();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// loginButtonsLoggedInDropdown template
|
|
||||||
//
|
//
|
||||||
|
|
||||||
Template.loginButtonsLoggedInDropdown.displayName = function () {
|
// decide whether we should show a dropdown rather than a row of
|
||||||
return displayName();
|
// buttons
|
||||||
|
Template.loginButtonsLoggedIn.dropdown = function () {
|
||||||
|
return Accounts._loginButtons.dropdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.loginButtonsLoggedInDropdown.inChangePasswordFlow = function () {
|
Template.loginButtonsLoggedIn.displayName = function () {
|
||||||
return Session.get(IN_CHANGE_PASSWORD_FLOW_KEY);
|
return Accounts._loginButtons.displayName();
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.loginButtonsLoggedInDropdown.dropdownVisible = function () {
|
|
||||||
return Session.get(DROPDOWN_VISIBLE_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsLoggedInDropdown.allowChangingPassword = function () {
|
|
||||||
// it would be more correct to check whether the user has a password set,
|
|
||||||
// but in order to do that we'd have to send more data down to the client,
|
|
||||||
// and it'd be preferable not to send down the entire service.password document.
|
|
||||||
//
|
|
||||||
// instead we use the heuristic: if the user has a username or email set.
|
|
||||||
var user = Meteor.user();
|
|
||||||
return user.username || (user.emails && user.emails[0] && user.emails[0].address);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// loginButtonsServiceRow template
|
|
||||||
//
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.events = {
|
|
||||||
'click #login-buttons-password': function () {
|
|
||||||
loginOrSignup();
|
|
||||||
},
|
|
||||||
'click #signup-link': function () {
|
|
||||||
resetMessages();
|
|
||||||
|
|
||||||
// store values of fields before swtiching to the signup form
|
|
||||||
var username = elementValueById('login-username');
|
|
||||||
var email = elementValueById('login-email');
|
|
||||||
var usernameOrEmail = elementValueById('login-username-or-email');
|
|
||||||
var password = elementValueById('login-password');
|
|
||||||
|
|
||||||
Session.set(IN_SIGNUP_FLOW_KEY, true);
|
|
||||||
Session.set(IN_FORGOT_PASSWORD_FLOW_KEY, false);
|
|
||||||
// force the ui to update so that we have the approprate fields to fill in
|
|
||||||
Meteor.flush();
|
|
||||||
|
|
||||||
// update new fields with appropriate defaults
|
|
||||||
if (username !== null)
|
|
||||||
document.getElementById('login-username').value = username;
|
|
||||||
else if (email !== null)
|
|
||||||
document.getElementById('login-email').value = email;
|
|
||||||
else if (usernameOrEmail !== null)
|
|
||||||
if (usernameOrEmail.indexOf('@') === -1)
|
|
||||||
document.getElementById('login-username').value = usernameOrEmail;
|
|
||||||
else
|
|
||||||
document.getElementById('login-email').value = usernameOrEmail;
|
|
||||||
|
|
||||||
document.getElementById('login-password').value = password;
|
|
||||||
|
|
||||||
// Forge redrawing the `login-dropdown-list` element because of
|
|
||||||
// a bizarre Chrome bug in which part of the DIV is not redrawn
|
|
||||||
// in case you had tried to unsuccessfully log in before
|
|
||||||
// switching to the signup form.
|
|
||||||
//
|
|
||||||
// Found tip on how to force a redraw on
|
|
||||||
// http://stackoverflow.com/questions/3485365/how-can-i-force-webkit-to-redraw-repaint-to-propagate-style-changes/3485654#3485654
|
|
||||||
var redraw = document.getElementById('login-dropdown-list');
|
|
||||||
redraw.style.display = 'none';
|
|
||||||
redraw.offsetHeight; // it seems that this line does nothing but is necessary for the redraw to work
|
|
||||||
redraw.style.display = 'block';
|
|
||||||
},
|
|
||||||
'click #forgot-password-link': function () {
|
|
||||||
resetMessages();
|
|
||||||
|
|
||||||
// store values of fields before swtiching to the signup form
|
|
||||||
var email = elementValueById('login-email');
|
|
||||||
var usernameOrEmail = elementValueById('login-username-or-email');
|
|
||||||
|
|
||||||
Session.set(IN_SIGNUP_FLOW_KEY, false);
|
|
||||||
Session.set(IN_FORGOT_PASSWORD_FLOW_KEY, true);
|
|
||||||
// force the ui to update so that we have the approprate fields to fill in
|
|
||||||
Meteor.flush();
|
|
||||||
|
|
||||||
// update new fields with appropriate defaults
|
|
||||||
if (email !== null)
|
|
||||||
document.getElementById('forgot-password-email').value = email;
|
|
||||||
else if (usernameOrEmail !== null)
|
|
||||||
if (usernameOrEmail.indexOf('@') !== -1)
|
|
||||||
document.getElementById('forgot-password-email').value = usernameOrEmail;
|
|
||||||
|
|
||||||
},
|
|
||||||
'keypress #login-username, keypress #login-email, keypress #login-username-or-email, keypress #login-password, keypress #login-password-again': function (event) {
|
|
||||||
if (event.keyCode === 13)
|
|
||||||
loginOrSignup();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.fields = function () {
|
|
||||||
var loginFields = [
|
|
||||||
{fieldName: 'username-or-email', fieldLabel: 'Username or Email',
|
|
||||||
visible: function () {
|
|
||||||
return Accounts._options.requireUsername
|
|
||||||
&& Accounts._options.requireEmail;
|
|
||||||
}},
|
|
||||||
{fieldName: 'username', fieldLabel: 'Username',
|
|
||||||
visible: function () {
|
|
||||||
return Accounts._options.requireUsername
|
|
||||||
&& !Accounts._options.requireEmail;
|
|
||||||
}},
|
|
||||||
{fieldName: 'email', fieldLabel: 'Email',
|
|
||||||
visible: function () {
|
|
||||||
return !Accounts._options.requireUsername;
|
|
||||||
}},
|
|
||||||
{fieldName: 'password', fieldLabel: 'Password', inputType: 'password',
|
|
||||||
visible: function () {
|
|
||||||
return true;
|
|
||||||
}}
|
|
||||||
];
|
|
||||||
|
|
||||||
var signupFields = [
|
|
||||||
{fieldName: 'username', fieldLabel: 'Username',
|
|
||||||
visible: function () {
|
|
||||||
return Accounts._options.requireUsername;
|
|
||||||
}},
|
|
||||||
{fieldName: 'email', fieldLabel: 'Email',
|
|
||||||
visible: function () {
|
|
||||||
return !Accounts._options.requireUsername
|
|
||||||
|| Accounts._options.requireEmail;
|
|
||||||
}},
|
|
||||||
{fieldName: 'password', fieldLabel: 'Password', inputType: 'password',
|
|
||||||
visible: function () {
|
|
||||||
return true;
|
|
||||||
}},
|
|
||||||
{fieldName: 'password-again', fieldLabel: 'Password (again)',
|
|
||||||
inputType: 'password',
|
|
||||||
visible: function () {
|
|
||||||
return Accounts._options.requireUsername
|
|
||||||
&& !Accounts._options.requireEmail;
|
|
||||||
}}
|
|
||||||
];
|
|
||||||
|
|
||||||
var fields = Session.get(IN_SIGNUP_FLOW_KEY) ? signupFields : loginFields;
|
|
||||||
return _.filter(fields, function(info) {
|
|
||||||
return info.visible();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.services = function () {
|
|
||||||
return getLoginServices();
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.isPasswordService = function () {
|
|
||||||
return this.name === 'password';
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.hasOtherServices = function () {
|
|
||||||
return getLoginServices().length > 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.hasPasswordService = function () {
|
|
||||||
return hasPasswordService();
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.inForgotPasswordFlow = function () {
|
|
||||||
return Session.get(IN_FORGOT_PASSWORD_FLOW_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.inLoginFlow = function () {
|
|
||||||
return !Session.get(IN_SIGNUP_FLOW_KEY) && !Session.get(IN_FORGOT_PASSWORD_FLOW_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.inSignupFlow = function () {
|
|
||||||
return Session.get(IN_SIGNUP_FLOW_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.showForgotPasswordLink = function () {
|
|
||||||
return Accounts._options.requireEmail
|
|
||||||
|| !Accounts._options.requireUsername;
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.configured = function () {
|
|
||||||
return !!Accounts.configuration.findOne({service: this.name.toLowerCase()});
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesRow.capitalizedName = function () {
|
|
||||||
return capitalize(this.name);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -326,328 +61,19 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
Template.loginButtonsMessages.errorMessage = function () {
|
Template.loginButtonsMessages.errorMessage = function () {
|
||||||
return Session.get(ERROR_MESSAGE_KEY);
|
return loginButtonsSession.get('errorMessage');
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.loginButtonsMessages.infoMessage = function () {
|
Template.loginButtonsMessages.infoMessage = function () {
|
||||||
return Session.get(INFO_MESSAGE_KEY);
|
return loginButtonsSession.get('infoMessage');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// forgotPasswordForm template
|
|
||||||
//
|
|
||||||
Template.forgotPasswordForm.events = {
|
|
||||||
'keypress #forgot-password-email': function (event) {
|
|
||||||
if (event.keyCode === 13)
|
|
||||||
forgotPassword();
|
|
||||||
},
|
|
||||||
'click #login-buttons-forgot-password': function () {
|
|
||||||
forgotPassword();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var forgotPassword = function () {
|
|
||||||
resetMessages();
|
|
||||||
|
|
||||||
var email = document.getElementById("forgot-password-email").value;
|
|
||||||
if (email.indexOf('@') !== -1) {
|
|
||||||
Accounts.forgotPassword({email: email}, function (error) {
|
|
||||||
if (error)
|
|
||||||
Session.set(ERROR_MESSAGE_KEY, error.reason || "Unknown error");
|
|
||||||
else
|
|
||||||
Session.set(INFO_MESSAGE_KEY, "Email sent");
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Session.set(ERROR_MESSAGE_KEY, "Invalid email");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// loginButtonsServicesDropdown template
|
|
||||||
//
|
|
||||||
|
|
||||||
Template.loginButtonsServicesDropdown.events = {
|
|
||||||
'click #login-sign-in-link': function () {
|
|
||||||
Session.set(DROPDOWN_VISIBLE_KEY, true);
|
|
||||||
Meteor.flush();
|
|
||||||
correctDropdownZIndexes();
|
|
||||||
},
|
|
||||||
'click .login-close-text': function () {
|
|
||||||
resetSession();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesDropdown.dropdownVisible = function () {
|
|
||||||
return Session.get(DROPDOWN_VISIBLE_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.loginButtonsServicesDropdown.hasPasswordService = function () {
|
|
||||||
return hasPasswordService();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// resetPasswordForm template
|
|
||||||
//
|
|
||||||
|
|
||||||
Template.resetPasswordForm.events = {
|
|
||||||
'click #login-buttons-reset-password-button': function () {
|
|
||||||
resetPassword();
|
|
||||||
},
|
|
||||||
'keypress #reset-password-new-password': function (event) {
|
|
||||||
if (event.keyCode === 13)
|
|
||||||
resetPassword();
|
|
||||||
},
|
|
||||||
'click #login-buttons-cancel-reset-password': function () {
|
|
||||||
Session.set(RESET_PASSWORD_TOKEN_KEY, null);
|
|
||||||
Accounts._enableAutoLogin();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var resetPassword = function () {
|
|
||||||
resetMessages();
|
|
||||||
var newPassword = document.getElementById('reset-password-new-password').value;
|
|
||||||
if (!validatePassword(newPassword))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Accounts.resetPassword(
|
|
||||||
Session.get(RESET_PASSWORD_TOKEN_KEY), newPassword,
|
|
||||||
function (error) {
|
|
||||||
if (error) {
|
|
||||||
Session.set(ERROR_MESSAGE_KEY, error.reason || "Unknown error");
|
|
||||||
} else {
|
|
||||||
Session.set(RESET_PASSWORD_TOKEN_KEY, null);
|
|
||||||
Accounts._enableAutoLogin();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.resetPasswordForm.inResetPasswordFlow = function () {
|
|
||||||
return Session.get(RESET_PASSWORD_TOKEN_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Accounts._resetPasswordToken) {
|
|
||||||
Session.set(RESET_PASSWORD_TOKEN_KEY, Accounts._resetPasswordToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// enrollAccountForm template
|
|
||||||
//
|
|
||||||
|
|
||||||
Template.enrollAccountForm.events = {
|
|
||||||
'click #login-buttons-enroll-account-button': function () {
|
|
||||||
enrollAccount();
|
|
||||||
},
|
|
||||||
'keypress #enroll-account-password': function (event) {
|
|
||||||
if (event.keyCode === 13)
|
|
||||||
enrollAccount();
|
|
||||||
},
|
|
||||||
'click #login-buttons-cancel-enroll-account': function () {
|
|
||||||
Session.set(ENROLL_ACCOUNT_TOKEN_KEY, null);
|
|
||||||
Accounts._enableAutoLogin();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var enrollAccount = function () {
|
|
||||||
resetMessages();
|
|
||||||
var password = document.getElementById('enroll-account-password').value;
|
|
||||||
if (!validatePassword(password))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Accounts.resetPassword(
|
|
||||||
Session.get(ENROLL_ACCOUNT_TOKEN_KEY), password,
|
|
||||||
function (error) {
|
|
||||||
if (error) {
|
|
||||||
Session.set(ERROR_MESSAGE_KEY, error.reason || "Unknown error");
|
|
||||||
} else {
|
|
||||||
Session.set(ENROLL_ACCOUNT_TOKEN_KEY, null);
|
|
||||||
Accounts._enableAutoLogin();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.enrollAccountForm.inEnrollAccountFlow = function () {
|
|
||||||
return Session.get(ENROLL_ACCOUNT_TOKEN_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Accounts._enrollAccountToken) {
|
|
||||||
Session.set(ENROLL_ACCOUNT_TOKEN_KEY, Accounts._enrollAccountToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// justValidatedUserForm template
|
|
||||||
//
|
|
||||||
|
|
||||||
Template.justValidatedUserForm.events = {
|
|
||||||
'click #just-validated-dismiss-button': function () {
|
|
||||||
Session.set(JUST_VALIDATED_USER_KEY, false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.justValidatedUserForm.visible = function () {
|
|
||||||
return Session.get(JUST_VALIDATED_USER_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// loginButtonsMessagesDialog template
|
|
||||||
//
|
|
||||||
|
|
||||||
Template.loginButtonsMessagesDialog.events({
|
|
||||||
'click #messages-dialog-dismiss-button': function () {
|
|
||||||
resetMessages();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.loginButtonsMessagesDialog.visible = function () {
|
|
||||||
var hasMessage = Session.get(INFO_MESSAGE_KEY) || Session.get(ERROR_MESSAGE_KEY);
|
|
||||||
return !dropdown() && hasMessage;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Needs to be in Meteor.startup because of a package loading order
|
|
||||||
// issue. We can't be sure that accounts-password is loaded earlier
|
|
||||||
// than accounts-ui so Accounts.validateEmail might not be defined.
|
|
||||||
Meteor.startup(function () {
|
|
||||||
if (Accounts._validateEmailToken) {
|
|
||||||
Accounts.validateEmail(Accounts._validateEmailToken, function(error) {
|
|
||||||
Accounts._enableAutoLogin();
|
|
||||||
if (!error)
|
|
||||||
Session.set(JUST_VALIDATED_USER_KEY, true);
|
|
||||||
// XXX show something if there was an error.
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
|
||||||
// loginButtonsChangePassword template
|
|
||||||
//
|
|
||||||
|
|
||||||
Template.loginButtonsChangePassword.events({
|
|
||||||
'keypress #login-old-password, keypress #login-password, keypress #login-password-again': function (event) {
|
|
||||||
if (event.keyCode === 13)
|
|
||||||
changePassword();
|
|
||||||
},
|
|
||||||
'click #login-buttons-do-change-password': function () {
|
|
||||||
changePassword();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.loginButtonsChangePassword.fields = function () {
|
|
||||||
return [
|
|
||||||
{fieldName: 'old-password', fieldLabel: 'Current Password', inputType: 'password',
|
|
||||||
visible: function () {
|
|
||||||
return true;
|
|
||||||
}},
|
|
||||||
{fieldName: 'password', fieldLabel: 'New Password', inputType: 'password',
|
|
||||||
visible: function () {
|
|
||||||
return true;
|
|
||||||
}},
|
|
||||||
{fieldName: 'password-again', fieldLabel: 'New Password (again)',
|
|
||||||
inputType: 'password',
|
|
||||||
visible: function () {
|
|
||||||
return Meteor.accounts._options.requireUsername
|
|
||||||
&& !Meteor.accounts._options.requireEmail;
|
|
||||||
}}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// configureLoginServicesDialog template
|
|
||||||
//
|
|
||||||
|
|
||||||
Template.configureLoginServicesDialog.events({
|
|
||||||
'click #configure-login-services-dismiss-button': function () {
|
|
||||||
Session.set(CONFIGURE_LOGIN_SERVICES_DIALOG_VISIBLE, false);
|
|
||||||
},
|
|
||||||
'click #configure-login-services-dialog-save-configuration': function () {
|
|
||||||
if (Session.get(CONFIGURE_LOGIN_SERVICES_DIALOG_SAVE_ENABLED)) {
|
|
||||||
// Prepare the configuration document for this login service
|
|
||||||
var serviceName = Session.get(CONFIGURE_LOGIN_SERVICES_DIALOG_SERVICE_NAME);
|
|
||||||
var configuration = {
|
|
||||||
service: serviceName
|
|
||||||
};
|
|
||||||
_.each(configurationFields(), function(field) {
|
|
||||||
configuration[field.property] = document.getElementById(
|
|
||||||
'configure-login-services-dialog-' + field.property).value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Configure this login service
|
|
||||||
Meteor.call("configureLoginService", configuration, function (error, result) {
|
|
||||||
if (error)
|
|
||||||
Meteor._debug("Error configurating login service " + serviceName, error);
|
|
||||||
else
|
|
||||||
Session.set(CONFIGURE_LOGIN_SERVICES_DIALOG_VISIBLE, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.configureLoginServicesDialog.events({
|
|
||||||
'input': function (event) {
|
|
||||||
// if the event fired on one of the configuration input fields,
|
|
||||||
// check whether we should enable the 'save configuration' button
|
|
||||||
if (event.target.id.indexOf('configure-login-services-dialog') === 0)
|
|
||||||
updateSaveDisabled();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// check whether the 'save configuration' button should be enabled.
|
|
||||||
// this is a really strange way to implement this and a Forms
|
|
||||||
// Abstraction would make all of this reactive, and simpler.
|
|
||||||
var updateSaveDisabled = function () {
|
|
||||||
var saveEnabled = true;
|
|
||||||
_.any(configurationFields(), function(field) {
|
|
||||||
if (document.getElementById(
|
|
||||||
'configure-login-services-dialog-' + field.property).value === '') {
|
|
||||||
saveEnabled = false;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Session.set(CONFIGURE_LOGIN_SERVICES_DIALOG_SAVE_ENABLED, saveEnabled);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns the appropriate template for this login service. This
|
|
||||||
// template should be defined in the service's package
|
|
||||||
var configureLoginServicesDialogTemplateForService = function () {
|
|
||||||
var serviceName = Session.get(CONFIGURE_LOGIN_SERVICES_DIALOG_SERVICE_NAME);
|
|
||||||
return Template['configureLoginServicesDialogFor' + capitalize(serviceName)];
|
|
||||||
};
|
|
||||||
|
|
||||||
var configurationFields = function () {
|
|
||||||
var template = configureLoginServicesDialogTemplateForService();
|
|
||||||
return template.fields();
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.configureLoginServicesDialog.configurationFields = function () {
|
|
||||||
return configurationFields();
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.configureLoginServicesDialog.visible = function () {
|
|
||||||
return Session.get(CONFIGURE_LOGIN_SERVICES_DIALOG_VISIBLE);
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.configureLoginServicesDialog.configurationSteps = function () {
|
|
||||||
// renders the appropriate template
|
|
||||||
return configureLoginServicesDialogTemplateForService()();
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.configureLoginServicesDialog.saveDisabled = function () {
|
|
||||||
return !Session.get(CONFIGURE_LOGIN_SERVICES_DIALOG_SAVE_ENABLED);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// helpers
|
// helpers
|
||||||
//
|
//
|
||||||
|
|
||||||
var displayName = function () {
|
Accounts._loginButtons.displayName = function () {
|
||||||
var user = Meteor.user();
|
var user = Meteor.user();
|
||||||
if (!user)
|
if (!user)
|
||||||
return '';
|
return '';
|
||||||
@@ -662,141 +88,7 @@
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
var elementValueById = function(id) {
|
Accounts._loginButtons.getLoginServices = function () {
|
||||||
var element = document.getElementById(id);
|
|
||||||
if (!element)
|
|
||||||
return null;
|
|
||||||
else
|
|
||||||
return element.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
var login = function () {
|
|
||||||
resetMessages();
|
|
||||||
|
|
||||||
var username = elementValueById('login-username');
|
|
||||||
var email = elementValueById('login-email');
|
|
||||||
var usernameOrEmail = elementValueById('login-username-or-email');
|
|
||||||
var password = elementValueById('login-password');
|
|
||||||
|
|
||||||
var loginSelector;
|
|
||||||
if (username !== null)
|
|
||||||
loginSelector = {username: username};
|
|
||||||
else if (email !== null)
|
|
||||||
loginSelector = {email: email};
|
|
||||||
else if (usernameOrEmail !== null)
|
|
||||||
loginSelector = usernameOrEmail;
|
|
||||||
else
|
|
||||||
throw new Error("Unexpected -- no element to use as a login user selector");
|
|
||||||
|
|
||||||
Meteor.loginWithPassword(loginSelector, password, function (error, result) {
|
|
||||||
if (error) {
|
|
||||||
Session.set(ERROR_MESSAGE_KEY, error.reason || "Unknown error");
|
|
||||||
} else {
|
|
||||||
resetSession();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var signup = function () {
|
|
||||||
resetMessages();
|
|
||||||
|
|
||||||
var options = {}; // to be passed to Meteor.createUser
|
|
||||||
|
|
||||||
var username = elementValueById('login-username');
|
|
||||||
if (username !== null) {
|
|
||||||
if (!validateUsername(username))
|
|
||||||
return;
|
|
||||||
else
|
|
||||||
options.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
var email = elementValueById('login-email');
|
|
||||||
if (email !== null) {
|
|
||||||
if (!validateEmail(email))
|
|
||||||
return;
|
|
||||||
else
|
|
||||||
options.email = email;
|
|
||||||
}
|
|
||||||
|
|
||||||
var password = elementValueById('login-password');
|
|
||||||
if (!validatePassword(password))
|
|
||||||
return;
|
|
||||||
else
|
|
||||||
options.password = password;
|
|
||||||
|
|
||||||
if (!matchPasswordAgainIfPresent())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Accounts._options.validateEmails)
|
|
||||||
options.validation = true;
|
|
||||||
|
|
||||||
Accounts.createUser(options, function (error) {
|
|
||||||
if (error) {
|
|
||||||
Session.set(ERROR_MESSAGE_KEY, error.reason || "Unknown error");
|
|
||||||
} else {
|
|
||||||
resetSession();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var loginOrSignup = function () {
|
|
||||||
if (Session.get(IN_SIGNUP_FLOW_KEY))
|
|
||||||
signup();
|
|
||||||
else
|
|
||||||
login();
|
|
||||||
};
|
|
||||||
|
|
||||||
var changePassword = function () {
|
|
||||||
resetMessages();
|
|
||||||
|
|
||||||
var oldPassword = elementValueById('login-old-password');
|
|
||||||
|
|
||||||
var password = elementValueById('login-password');
|
|
||||||
if (!validatePassword(password))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!matchPasswordAgainIfPresent())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Accounts.changePassword(oldPassword, password, function (error) {
|
|
||||||
if (error) {
|
|
||||||
Session.set(ERROR_MESSAGE_KEY, error.reason || "Unknown error");
|
|
||||||
} else {
|
|
||||||
Session.set(INFO_MESSAGE_KEY, "Password changed");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var matchPasswordAgainIfPresent = function () {
|
|
||||||
var passwordAgain = elementValueById('login-password-again');
|
|
||||||
if (passwordAgain !== null) {
|
|
||||||
var password = elementValueById('login-password');
|
|
||||||
if (password !== passwordAgain) {
|
|
||||||
Session.set(ERROR_MESSAGE_KEY, "Passwords don't match");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
var correctDropdownZIndexes = function () {
|
|
||||||
// IE <= 7 has a z-index bug that means we can't just give the
|
|
||||||
// dropdown a z-index and expect it to stack above the rest of
|
|
||||||
// the page even if nothing else has a z-index. The nature of
|
|
||||||
// the bug is that all positioned elements are considered to
|
|
||||||
// have z-index:0 (not auto) and therefore start new stacking
|
|
||||||
// contexts, with ties broken by page order.
|
|
||||||
//
|
|
||||||
// The fix, then is to give z-index:1 to all ancestors
|
|
||||||
// of the dropdown having z-index:0.
|
|
||||||
for(var n = document.getElementById('login-dropdown-list').parentNode;
|
|
||||||
n.nodeName !== 'BODY';
|
|
||||||
n = n.parentNode)
|
|
||||||
if (n.style.zIndex === 0)
|
|
||||||
n.style.zIndex = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
var getLoginServices = function () {
|
|
||||||
var ret = [];
|
var ret = [];
|
||||||
// make sure to put password last, since this is how it is styled
|
// make sure to put password last, since this is how it is styled
|
||||||
// in the ui as well.
|
// in the ui as well.
|
||||||
@@ -810,50 +102,42 @@
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var hasPasswordService = function () {
|
Accounts._loginButtons.hasPasswordService = function () {
|
||||||
return _.any(getLoginServices(), function (service) {
|
return Accounts.password;
|
||||||
return service.name === 'password';
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var dropdown = function () {
|
Accounts._loginButtons.dropdown = function () {
|
||||||
return hasPasswordService() || getLoginServices().length > 1;
|
return Accounts._loginButtons.hasPasswordService() || Accounts._loginButtons.getLoginServices().length > 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// XXX improve these. should this be in accounts-password instead?
|
||||||
// XXX improve these? should this be in accounts-password instead?
|
|
||||||
//
|
//
|
||||||
// XXX these will become configurable, and will be validated on
|
// XXX these will become configurable, and will be validated on
|
||||||
// the server as well.
|
// the server as well.
|
||||||
var validateUsername = function (username) {
|
Accounts._loginButtons.validateUsername = function (username) {
|
||||||
if (username.length >= 3) {
|
if (username.length >= 3) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Session.set(ERROR_MESSAGE_KEY, "Username must be at least 3 characters long");
|
loginButtonsSession.set('errorMessage', "Username must be at least 3 characters long");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var validateEmail = function (email) {
|
Accounts._loginButtons.validateEmail = function (email) {
|
||||||
if (email.indexOf('@') !== -1) {
|
if (email.indexOf('@') !== -1) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Session.set(ERROR_MESSAGE_KEY, "Invalid email");
|
loginButtonsSession.set('errorMessage', "Invalid email");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var validatePassword = function (password) {
|
Accounts._loginButtons.validatePassword = function (password) {
|
||||||
if (password.length >= 6) {
|
if (password.length >= 6) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Session.set(ERROR_MESSAGE_KEY, "Password must be at least 6 characters long");
|
loginButtonsSession.set('errorMessage', "Password must be at least 6 characters long");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// XXX from http://epeli.github.com/underscore.string/lib/underscore.string.js
|
|
||||||
var capitalize = function(str){
|
|
||||||
str = str == null ? '' : String(str);
|
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
||||||
};
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|||||||
122
packages/accounts-ui-unstyled/login_buttons_dialogs.html
Normal file
122
packages/accounts-ui-unstyled/login_buttons_dialogs.html
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<body>
|
||||||
|
{{> resetPasswordDialog}}
|
||||||
|
{{> enrollAccountDialog}}
|
||||||
|
{{> justValidatedUserDialog}}
|
||||||
|
{{> configureLoginServiceDialog}}
|
||||||
|
|
||||||
|
<!-- if we're not showing a dropdown, we need some other place to show messages -->
|
||||||
|
{{> loginButtonsMessagesDialog}}
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<template name="resetPasswordDialog">
|
||||||
|
{{#if inResetPasswordFlow}}
|
||||||
|
<div class="hide-background"></div>
|
||||||
|
|
||||||
|
<div class="accounts-dialog accounts-centered-dialog">
|
||||||
|
<label id="reset-password-new-password-label" for="reset-password-new-password">
|
||||||
|
New password
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input id="reset-password-new-password" type="password" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{> loginButtonsMessages}}
|
||||||
|
|
||||||
|
<div class="login-button login-button-form-submit" id="login-buttons-reset-password-button">
|
||||||
|
Set password
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a class="additional-link" id="login-buttons-cancel-reset-password">
|
||||||
|
Cancel
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="enrollAccountDialog">
|
||||||
|
{{#if inEnrollAccountFlow}}
|
||||||
|
<div class="hide-background"></div>
|
||||||
|
|
||||||
|
<div class="accounts-dialog accounts-centered-dialog">
|
||||||
|
<label id="enroll-account-password-label" for="enroll-account-password">
|
||||||
|
Choose a password
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input id="enroll-account-password" type="password" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{> loginButtonsMessages}}
|
||||||
|
|
||||||
|
<div class="login-button login-button-form-submit" id="login-buttons-enroll-account-button">
|
||||||
|
Create account
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a class="additional-link" id="login-buttons-cancel-enroll-account">
|
||||||
|
Cancel
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="justValidatedUserDialog">
|
||||||
|
{{#if visible}}
|
||||||
|
<div class="accounts-dialog accounts-centered-dialog">
|
||||||
|
Email validated
|
||||||
|
<div class="login-button" id="just-validated-dismiss-button">Dismiss</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="configureLoginServiceDialog">
|
||||||
|
{{#if visible}}
|
||||||
|
<div id="configure-login-service-dialog" class="accounts-dialog accounts-centered-dialog">
|
||||||
|
{{{configurationSteps}}}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Now, copy over some details.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col span="1" class="configuration_labels">
|
||||||
|
<col span="1" class="configuration_inputs">
|
||||||
|
</colgroup>
|
||||||
|
{{#each configurationFields}}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<label for="configure-login-service-dialog-{{property}}">{{label}}</label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input id="configure-login-service-dialog-{{property}}" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
<div class="new-section">
|
||||||
|
<div class="login-button" id="configure-login-service-dismiss-button">
|
||||||
|
I'll do this later
|
||||||
|
</div>
|
||||||
|
{{#isolate}}
|
||||||
|
<div class="login-button login-button-configure {{#if saveDisabled}}login-button-disabled{{/if}}"
|
||||||
|
id="configure-login-service-dialog-save-configuration">
|
||||||
|
Save Configuration
|
||||||
|
</div>
|
||||||
|
{{/isolate}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="loginButtonsMessagesDialog">
|
||||||
|
{{#if visible}}
|
||||||
|
<div class="accounts-dialog accounts-centered-dialog" id="login-buttons-message-dialog">
|
||||||
|
{{> loginButtonsMessages}}
|
||||||
|
<div class="login-button" id="messages-dialog-dismiss-button">Dismiss</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
234
packages/accounts-ui-unstyled/login_buttons_dialogs.js
Normal file
234
packages/accounts-ui-unstyled/login_buttons_dialogs.js
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
(function () {
|
||||||
|
// for convenience
|
||||||
|
var loginButtonsSession = Accounts._loginButtonsSession;
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// populate the session so that the appropriate dialogs are
|
||||||
|
// displayed by reading variables set by accounts-urls, which parses
|
||||||
|
// special URLs. since accounts-ui depends on accounts-urls, we are
|
||||||
|
// guaranteed to have these set at this point.
|
||||||
|
//
|
||||||
|
|
||||||
|
if (Accounts._resetPasswordToken) {
|
||||||
|
loginButtonsSession.set('resetPasswordToken', Accounts._resetPasswordToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Accounts._enrollAccountToken) {
|
||||||
|
loginButtonsSession.set('enrollAccountToken', Accounts._enrollAccountToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs to be in Meteor.startup because of a package loading order
|
||||||
|
// issue. We can't be sure that accounts-password is loaded earlier
|
||||||
|
// than accounts-ui so Accounts.validateEmail might not be defined.
|
||||||
|
Meteor.startup(function () {
|
||||||
|
if (Accounts._validateEmailToken) {
|
||||||
|
Accounts.validateEmail(Accounts._validateEmailToken, function(error) {
|
||||||
|
Accounts._enableAutoLogin();
|
||||||
|
if (!error)
|
||||||
|
loginButtonsSession.set('justValidatedUser', true);
|
||||||
|
// XXX show something if there was an error.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// resetPasswordDialog template
|
||||||
|
//
|
||||||
|
|
||||||
|
Template.resetPasswordDialog.events({
|
||||||
|
'click #login-buttons-reset-password-button': function () {
|
||||||
|
resetPassword();
|
||||||
|
},
|
||||||
|
'keypress #reset-password-new-password': function (event) {
|
||||||
|
if (event.keyCode === 13)
|
||||||
|
resetPassword();
|
||||||
|
},
|
||||||
|
'click #login-buttons-cancel-reset-password': function () {
|
||||||
|
loginButtonsSession.set('resetPasswordToken', null);
|
||||||
|
Accounts._enableAutoLogin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var resetPassword = function () {
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
var newPassword = document.getElementById('reset-password-new-password').value;
|
||||||
|
if (!Accounts._loginButtons.validatePassword(newPassword))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Accounts.resetPassword(
|
||||||
|
loginButtonsSession.get('resetPasswordToken'), newPassword,
|
||||||
|
function (error) {
|
||||||
|
if (error) {
|
||||||
|
loginButtonsSession.set('errorMessage', error.reason || "Unknown error");
|
||||||
|
} else {
|
||||||
|
loginButtonsSession.set('resetPasswordToken', null);
|
||||||
|
Accounts._enableAutoLogin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.resetPasswordDialog.inResetPasswordFlow = function () {
|
||||||
|
return loginButtonsSession.get('resetPasswordToken');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// enrollAccountDialog template
|
||||||
|
//
|
||||||
|
|
||||||
|
Template.enrollAccountDialog.events({
|
||||||
|
'click #login-buttons-enroll-account-button': function () {
|
||||||
|
enrollAccount();
|
||||||
|
},
|
||||||
|
'keypress #enroll-account-password': function (event) {
|
||||||
|
if (event.keyCode === 13)
|
||||||
|
enrollAccount();
|
||||||
|
},
|
||||||
|
'click #login-buttons-cancel-enroll-account': function () {
|
||||||
|
loginButtonsSession.set('enrollAccountToken', null);
|
||||||
|
Accounts._enableAutoLogin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var enrollAccount = function () {
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
var password = document.getElementById('enroll-account-password').value;
|
||||||
|
if (!Accounts._loginButtons.validatePassword(password))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Accounts.resetPassword(
|
||||||
|
loginButtonsSession.get('enrollAccountToken'), password,
|
||||||
|
function (error) {
|
||||||
|
if (error) {
|
||||||
|
loginButtonsSession.set('errorMessage', error.reason || "Unknown error");
|
||||||
|
} else {
|
||||||
|
loginButtonsSession.set('enrollAccountToken', null);
|
||||||
|
Accounts._enableAutoLogin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.enrollAccountDialog.inEnrollAccountFlow = function () {
|
||||||
|
return loginButtonsSession.get('enrollAccountToken');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// justValidatedUserDialog template
|
||||||
|
//
|
||||||
|
|
||||||
|
Template.justValidatedUserDialog.events({
|
||||||
|
'click #just-validated-dismiss-button': function () {
|
||||||
|
loginButtonsSession.set('justValidatedUser', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.justValidatedUserDialog.visible = function () {
|
||||||
|
return loginButtonsSession.get('justValidatedUser');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// loginButtonsMessagesDialog template
|
||||||
|
//
|
||||||
|
|
||||||
|
Template.loginButtonsMessagesDialog.events({
|
||||||
|
'click #messages-dialog-dismiss-button': function () {
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.loginButtonsMessagesDialog.visible = function () {
|
||||||
|
var hasMessage = loginButtonsSession.get('infoMessage') || loginButtonsSession.get('errorMessage');
|
||||||
|
return !Accounts._loginButtons.dropdown() && hasMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// configureLoginServiceDialog template
|
||||||
|
//
|
||||||
|
|
||||||
|
Template.configureLoginServiceDialog.events({
|
||||||
|
'click #configure-login-service-dismiss-button': function () {
|
||||||
|
loginButtonsSession.set('configureLoginServiceDialogVisible', false);
|
||||||
|
},
|
||||||
|
'click #configure-login-service-dialog-save-configuration': function () {
|
||||||
|
if (loginButtonsSession.get('configureLoginServiceDialogVisible')) {
|
||||||
|
// Prepare the configuration document for this login service
|
||||||
|
var serviceName = loginButtonsSession.get('configureLoginServiceDialogServiceName');
|
||||||
|
var configuration = {
|
||||||
|
service: serviceName
|
||||||
|
};
|
||||||
|
_.each(configurationFields(), function(field) {
|
||||||
|
configuration[field.property] = document.getElementById(
|
||||||
|
'configure-login-service-dialog-' + field.property).value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure this login service
|
||||||
|
Meteor.call("configureLoginService", configuration, function (error, result) {
|
||||||
|
if (error)
|
||||||
|
Meteor._debug("Error configurating login service " + serviceName, error);
|
||||||
|
else
|
||||||
|
loginButtonsSession.set('configureLoginServiceDialogVisible', false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'input': function (event) {
|
||||||
|
// if the event fired on one of the configuration input fields,
|
||||||
|
// check whether we should enable the 'save configuration' button
|
||||||
|
if (event.target.id.indexOf('configure-login-service-dialog') === 0)
|
||||||
|
updateSaveDisabled();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// check whether the 'save configuration' button should be enabled.
|
||||||
|
// this is a really strange way to implement this and a Forms
|
||||||
|
// Abstraction would make all of this reactive, and simpler.
|
||||||
|
var updateSaveDisabled = function () {
|
||||||
|
var anyFieldEmpty = _.any(configurationFields(), function(field) {
|
||||||
|
return document.getElementById(
|
||||||
|
'configure-login-service-dialog-' + field.property).value === '';
|
||||||
|
});
|
||||||
|
|
||||||
|
loginButtonsSession.set('configureLoginServiceDialogSaveDisabled', anyFieldEmpty);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the appropriate template for this login service. This
|
||||||
|
// template should be defined in the service's package
|
||||||
|
var configureLoginServiceDialogTemplateForService = function () {
|
||||||
|
var serviceName = loginButtonsSession.get('configureLoginServiceDialogServiceName');
|
||||||
|
return Template['configureLoginServiceDialogFor' + capitalize(serviceName)];
|
||||||
|
};
|
||||||
|
|
||||||
|
var configurationFields = function () {
|
||||||
|
var template = configureLoginServiceDialogTemplateForService();
|
||||||
|
return template.fields();
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.configureLoginServiceDialog.configurationFields = function () {
|
||||||
|
return configurationFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.configureLoginServiceDialog.visible = function () {
|
||||||
|
return loginButtonsSession.get('configureLoginServiceDialogVisible');
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.configureLoginServiceDialog.configurationSteps = function () {
|
||||||
|
// renders the appropriate template
|
||||||
|
return configureLoginServiceDialogTemplateForService()();
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.configureLoginServiceDialog.saveDisabled = function () {
|
||||||
|
return loginButtonsSession.get('configureLoginServiceDialogSaveDisabled');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// XXX from http://epeli.github.com/underscore.string/lib/underscore.string.js
|
||||||
|
var capitalize = function(str){
|
||||||
|
str = str == null ? '' : String(str);
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
}) ();
|
||||||
145
packages/accounts-ui-unstyled/login_buttons_dropdown.html
Normal file
145
packages/accounts-ui-unstyled/login_buttons_dropdown.html
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<!-- -->
|
||||||
|
<!-- LOGGED IN -->
|
||||||
|
<!-- -->
|
||||||
|
<template name="loginButtonsLoggedInDropdown">
|
||||||
|
<div class="login-link-and-dropdown-list">
|
||||||
|
{{#if currentUser.loading}}
|
||||||
|
<div class="loading"></div>
|
||||||
|
{{else}}
|
||||||
|
<a class="login-link-text" id="login-name-link">
|
||||||
|
{{displayName}} ▾
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{{#if dropdownVisible}}
|
||||||
|
<div id="login-dropdown-list" class="accounts-dialog">
|
||||||
|
<a class="login-close-text">Close</a>
|
||||||
|
<div class="login-close-text-clear"></div>
|
||||||
|
|
||||||
|
{{#if inChangePasswordFlow}}
|
||||||
|
{{> loginButtonsChangePassword}}
|
||||||
|
{{else}}
|
||||||
|
{{> loginButtonsLoggedInDropdownActions}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="loginButtonsLoggedInDropdownActions">
|
||||||
|
{{#if allowChangingPassword}}
|
||||||
|
<div class="login-button" id="login-buttons-open-change-password">
|
||||||
|
Change password
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="login-button" id="login-buttons-logout">
|
||||||
|
Logout
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- -->
|
||||||
|
<!-- LOGGED OUT -->
|
||||||
|
<!-- -->
|
||||||
|
<template name="loginButtonsLoggedOutDropdown">
|
||||||
|
<div class="login-link-and-dropdown-list">
|
||||||
|
<a class="login-link-text" id="login-sign-in-link">Sign in ▾</a>
|
||||||
|
{{#if dropdownVisible}}
|
||||||
|
<div id="login-dropdown-list" class="accounts-dialog">
|
||||||
|
<a class="login-close-text">Close</a>
|
||||||
|
<div class="login-close-text-clear"></div>
|
||||||
|
{{> loginButtonsLoggedOutAllServices}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="loginButtonsLoggedOutAllServices">
|
||||||
|
{{#each services}}
|
||||||
|
{{#if isPasswordService}}
|
||||||
|
{{#if hasOtherServices}} {{! the password service will always come last }}
|
||||||
|
<div class="or">
|
||||||
|
<span class="hline"> </span>
|
||||||
|
<span class="or-text">or</span>
|
||||||
|
<span class="hline"> </span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{> loginButtonsLoggedOutPasswordService}}
|
||||||
|
{{else}}
|
||||||
|
{{> loginButtonsLoggedOutSingleLoginButton}}
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
{{#unless hasPasswordService}}
|
||||||
|
{{> loginButtonsMessages}}
|
||||||
|
{{/unless}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="loginButtonsLoggedOutPasswordService">
|
||||||
|
{{#if inForgotPasswordFlow}}
|
||||||
|
{{> forgotPasswordForm}}
|
||||||
|
{{else}}
|
||||||
|
<div class="login-form login-password-form">
|
||||||
|
{{#each fields}}
|
||||||
|
{{> loginButtonsFormField}}
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
{{> loginButtonsMessages}}
|
||||||
|
|
||||||
|
<div class="login-button login-button-form-submit" id="login-buttons-password">
|
||||||
|
{{#if inSignupFlow}}
|
||||||
|
Create account
|
||||||
|
{{else}}
|
||||||
|
Sign in
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if inLoginFlow}}
|
||||||
|
<div class="additional-link-container">
|
||||||
|
<a id="signup-link" class="additional-link">Create account</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if showForgotPasswordLink}}
|
||||||
|
<div class="additional-link-container">
|
||||||
|
<a id="forgot-password-link" class="additional-link">Forgot password</a>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="loginButtonsFormField">
|
||||||
|
{{#if visible}}
|
||||||
|
<label id="login-{{fieldName}}-label" for="login-{{fieldName}}">
|
||||||
|
{{fieldLabel}}
|
||||||
|
</label>
|
||||||
|
<input id="login-{{fieldName}}" type="{{inputType}}" />
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="forgotPasswordForm">
|
||||||
|
<div class="login-form">
|
||||||
|
<label id="forgot-password-email-label" for="forgot-password-email">Email</label>
|
||||||
|
<input id="forgot-password-email"/>
|
||||||
|
|
||||||
|
{{> loginButtonsMessages}}
|
||||||
|
|
||||||
|
<div class="login-button login-button-form-submit" id="login-buttons-forgot-password">
|
||||||
|
Reset password
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="loginButtonsChangePassword">
|
||||||
|
{{#each fields}}
|
||||||
|
{{> loginButtonsFormField}}
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
{{> loginButtonsMessages}}
|
||||||
|
|
||||||
|
<div class="login-button login-button-form-submit" id="login-buttons-do-change-password">
|
||||||
|
Change password
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
414
packages/accounts-ui-unstyled/login_buttons_dropdown.js
Normal file
414
packages/accounts-ui-unstyled/login_buttons_dropdown.js
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
(function () {
|
||||||
|
// for convenience
|
||||||
|
var loginButtonsSession = Accounts._loginButtonsSession;
|
||||||
|
|
||||||
|
// events shared between loginButtonsLoggedOutDropdown and
|
||||||
|
// loginButtonsLoggedInDropdown
|
||||||
|
Template.loginButtons.events({
|
||||||
|
'click #login-name-link, click #login-sign-in-link': function () {
|
||||||
|
loginButtonsSession.set('dropdownVisible', true);
|
||||||
|
Meteor.flush();
|
||||||
|
correctDropdownZIndexes();
|
||||||
|
},
|
||||||
|
'click .login-close-text': function () {
|
||||||
|
loginButtonsSession.closeDropdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// loginButtonsLoggedInDropdown template and related
|
||||||
|
//
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedInDropdown.events({
|
||||||
|
'click #login-buttons-open-change-password': function() {
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
loginButtonsSession.set('inChangePasswordFlow', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedInDropdown.displayName = function () {
|
||||||
|
return Accounts._loginButtons.displayName();
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedInDropdown.inChangePasswordFlow = function () {
|
||||||
|
return loginButtonsSession.get('inChangePasswordFlow');
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedInDropdown.dropdownVisible = function () {
|
||||||
|
return loginButtonsSession.get('dropdownVisible');
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedInDropdownActions.allowChangingPassword = function () {
|
||||||
|
// it would be more correct to check whether the user has a password set,
|
||||||
|
// but in order to do that we'd have to send more data down to the client,
|
||||||
|
// and it'd be preferable not to send down the entire service.password document.
|
||||||
|
//
|
||||||
|
// instead we use the heuristic: if the user has a username or email set.
|
||||||
|
var user = Meteor.user();
|
||||||
|
return user.username || (user.emails && user.emails[0] && user.emails[0].address);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// loginButtonsLoggedOutDropdown template and related
|
||||||
|
//
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutDropdown.events({
|
||||||
|
'click #login-buttons-password': function () {
|
||||||
|
loginOrSignup();
|
||||||
|
},
|
||||||
|
|
||||||
|
'keypress #forgot-password-email': function (event) {
|
||||||
|
if (event.keyCode === 13)
|
||||||
|
forgotPassword();
|
||||||
|
},
|
||||||
|
|
||||||
|
'click #login-buttons-forgot-password': function () {
|
||||||
|
forgotPassword();
|
||||||
|
},
|
||||||
|
|
||||||
|
'click #signup-link': function () {
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
|
||||||
|
// store values of fields before swtiching to the signup form
|
||||||
|
var username = elementValueById('login-username');
|
||||||
|
var email = elementValueById('login-email');
|
||||||
|
var usernameOrEmail = elementValueById('login-username-or-email');
|
||||||
|
var password = elementValueById('login-password');
|
||||||
|
|
||||||
|
loginButtonsSession.set('inSignupFlow', true);
|
||||||
|
loginButtonsSession.set('inForgotPasswordFlow', false);
|
||||||
|
// force the ui to update so that we have the approprate fields to fill in
|
||||||
|
Meteor.flush();
|
||||||
|
|
||||||
|
// update new fields with appropriate defaults
|
||||||
|
if (username !== null)
|
||||||
|
document.getElementById('login-username').value = username;
|
||||||
|
else if (email !== null)
|
||||||
|
document.getElementById('login-email').value = email;
|
||||||
|
else if (usernameOrEmail !== null)
|
||||||
|
if (usernameOrEmail.indexOf('@') === -1)
|
||||||
|
document.getElementById('login-username').value = usernameOrEmail;
|
||||||
|
else
|
||||||
|
document.getElementById('login-email').value = usernameOrEmail;
|
||||||
|
|
||||||
|
document.getElementById('login-password').value = password;
|
||||||
|
|
||||||
|
// Forge redrawing the `login-dropdown-list` element because of
|
||||||
|
// a bizarre Chrome bug in which part of the DIV is not redrawn
|
||||||
|
// in case you had tried to unsuccessfully log in before
|
||||||
|
// switching to the signup form.
|
||||||
|
//
|
||||||
|
// Found tip on how to force a redraw on
|
||||||
|
// http://stackoverflow.com/questions/3485365/how-can-i-force-webkit-to-redraw-repaint-to-propagate-style-changes/3485654#3485654
|
||||||
|
var redraw = document.getElementById('login-dropdown-list');
|
||||||
|
redraw.style.display = 'none';
|
||||||
|
redraw.offsetHeight; // it seems that this line does nothing but is necessary for the redraw to work
|
||||||
|
redraw.style.display = 'block';
|
||||||
|
},
|
||||||
|
'click #forgot-password-link': function () {
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
|
||||||
|
// store values of fields before swtiching to the signup form
|
||||||
|
var email = elementValueById('login-email');
|
||||||
|
var usernameOrEmail = elementValueById('login-username-or-email');
|
||||||
|
|
||||||
|
loginButtonsSession.set('inSignupFlow', false);
|
||||||
|
loginButtonsSession.set('inForgotPasswordFlow', true);
|
||||||
|
// force the ui to update so that we have the approprate fields to fill in
|
||||||
|
Meteor.flush();
|
||||||
|
|
||||||
|
// update new fields with appropriate defaults
|
||||||
|
if (email !== null)
|
||||||
|
document.getElementById('forgot-password-email').value = email;
|
||||||
|
else if (usernameOrEmail !== null)
|
||||||
|
if (usernameOrEmail.indexOf('@') !== -1)
|
||||||
|
document.getElementById('forgot-password-email').value = usernameOrEmail;
|
||||||
|
|
||||||
|
},
|
||||||
|
'keypress #login-username, keypress #login-email, keypress #login-username-or-email, keypress #login-password, keypress #login-password-again': function (event) {
|
||||||
|
if (event.keyCode === 13)
|
||||||
|
loginOrSignup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutDropdown.dropdownVisible = function () {
|
||||||
|
return loginButtonsSession.get('dropdownVisible');
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutDropdown.hasPasswordService = function () {
|
||||||
|
return Accounts._loginButtons.hasPasswordService();
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutAllServices.services = function () {
|
||||||
|
return Accounts._loginButtons.getLoginServices();
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutAllServices.isPasswordService = function () {
|
||||||
|
return this.name === 'password';
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutAllServices.hasOtherServices = function () {
|
||||||
|
return Accounts._loginButtons.getLoginServices().length > 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutAllServices.hasPasswordService = function () {
|
||||||
|
return Accounts._loginButtons.hasPasswordService();
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutPasswordService.fields = function () {
|
||||||
|
var loginFields = [
|
||||||
|
{fieldName: 'username-or-email', fieldLabel: 'Username or Email',
|
||||||
|
visible: function () {
|
||||||
|
return Accounts._options.requireUsername
|
||||||
|
&& Accounts._options.requireEmail;
|
||||||
|
}},
|
||||||
|
{fieldName: 'username', fieldLabel: 'Username',
|
||||||
|
visible: function () {
|
||||||
|
return Accounts._options.requireUsername
|
||||||
|
&& !Accounts._options.requireEmail;
|
||||||
|
}},
|
||||||
|
{fieldName: 'email', fieldLabel: 'Email',
|
||||||
|
visible: function () {
|
||||||
|
return !Accounts._options.requireUsername;
|
||||||
|
}},
|
||||||
|
{fieldName: 'password', fieldLabel: 'Password', inputType: 'password',
|
||||||
|
visible: function () {
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
];
|
||||||
|
|
||||||
|
var signupFields = [
|
||||||
|
{fieldName: 'username', fieldLabel: 'Username',
|
||||||
|
visible: function () {
|
||||||
|
return Accounts._options.requireUsername;
|
||||||
|
}},
|
||||||
|
{fieldName: 'email', fieldLabel: 'Email',
|
||||||
|
visible: function () {
|
||||||
|
return !Accounts._options.requireUsername
|
||||||
|
|| Accounts._options.requireEmail;
|
||||||
|
}},
|
||||||
|
{fieldName: 'password', fieldLabel: 'Password', inputType: 'password',
|
||||||
|
visible: function () {
|
||||||
|
return true;
|
||||||
|
}},
|
||||||
|
{fieldName: 'password-again', fieldLabel: 'Password (again)',
|
||||||
|
inputType: 'password',
|
||||||
|
visible: function () {
|
||||||
|
return Accounts._options.requireUsername
|
||||||
|
&& !Accounts._options.requireEmail;
|
||||||
|
}}
|
||||||
|
];
|
||||||
|
|
||||||
|
return loginButtonsSession.get('inSignupFlow') ? signupFields : loginFields;
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutPasswordService.inForgotPasswordFlow = function () {
|
||||||
|
return loginButtonsSession.get('inForgotPasswordFlow');
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutPasswordService.inLoginFlow = function () {
|
||||||
|
return !loginButtonsSession.get('inSignupFlow') && !loginButtonsSession.get('inForgotPasswordFlow');
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutPasswordService.inSignupFlow = function () {
|
||||||
|
return loginButtonsSession.get('inSignupFlow');
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutPasswordService.showForgotPasswordLink = function () {
|
||||||
|
return Accounts._options.requireEmail
|
||||||
|
|| !Accounts._options.requireUsername;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// loginButtonsChangePassword template
|
||||||
|
//
|
||||||
|
|
||||||
|
Template.loginButtonsChangePassword.events({
|
||||||
|
'keypress #login-old-password, keypress #login-password, keypress #login-password-again': function (event) {
|
||||||
|
if (event.keyCode === 13)
|
||||||
|
changePassword();
|
||||||
|
},
|
||||||
|
'click #login-buttons-do-change-password': function () {
|
||||||
|
changePassword();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.loginButtonsChangePassword.fields = function () {
|
||||||
|
return [
|
||||||
|
{fieldName: 'old-password', fieldLabel: 'Current Password', inputType: 'password',
|
||||||
|
visible: function () {
|
||||||
|
return true;
|
||||||
|
}},
|
||||||
|
{fieldName: 'password', fieldLabel: 'New Password', inputType: 'password',
|
||||||
|
visible: function () {
|
||||||
|
return true;
|
||||||
|
}},
|
||||||
|
{fieldName: 'password-again', fieldLabel: 'New Password (again)',
|
||||||
|
inputType: 'password',
|
||||||
|
visible: function () {
|
||||||
|
return Meteor.accounts._options.requireUsername
|
||||||
|
&& !Meteor.accounts._options.requireEmail;
|
||||||
|
}}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// helpers
|
||||||
|
//
|
||||||
|
|
||||||
|
var elementValueById = function(id) {
|
||||||
|
var element = document.getElementById(id);
|
||||||
|
if (!element)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return element.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
var loginOrSignup = function () {
|
||||||
|
if (loginButtonsSession.get('inSignupFlow'))
|
||||||
|
signup();
|
||||||
|
else
|
||||||
|
login();
|
||||||
|
};
|
||||||
|
|
||||||
|
var login = function () {
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
|
||||||
|
var username = elementValueById('login-username');
|
||||||
|
var email = elementValueById('login-email');
|
||||||
|
var usernameOrEmail = elementValueById('login-username-or-email');
|
||||||
|
var password = elementValueById('login-password');
|
||||||
|
|
||||||
|
var loginSelector;
|
||||||
|
if (username !== null)
|
||||||
|
loginSelector = {username: username};
|
||||||
|
else if (email !== null)
|
||||||
|
loginSelector = {email: email};
|
||||||
|
else if (usernameOrEmail !== null)
|
||||||
|
loginSelector = usernameOrEmail;
|
||||||
|
else
|
||||||
|
throw new Error("Unexpected -- no element to use as a login user selector");
|
||||||
|
|
||||||
|
Meteor.loginWithPassword(loginSelector, password, function (error, result) {
|
||||||
|
if (error) {
|
||||||
|
loginButtonsSession.set('errorMessage', error.reason || "Unknown error");
|
||||||
|
} else {
|
||||||
|
loginButtonsSession.closeDropdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var signup = function () {
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
|
||||||
|
var options = {}; // to be passed to Meteor.createUser
|
||||||
|
|
||||||
|
var username = elementValueById('login-username');
|
||||||
|
if (username !== null) {
|
||||||
|
if (!Accounts._loginButtons.validateUsername(username))
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
options.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
var email = elementValueById('login-email');
|
||||||
|
if (email !== null) {
|
||||||
|
if (!Accounts._loginButtons.validateEmail(email))
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
options.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
var password = elementValueById('login-password');
|
||||||
|
if (!Accounts._loginButtons.validatePassword(password))
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
options.password = password;
|
||||||
|
|
||||||
|
if (!matchPasswordAgainIfPresent())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Accounts._options.validateEmails)
|
||||||
|
options.validation = true;
|
||||||
|
|
||||||
|
Accounts.createUser(options, function (error) {
|
||||||
|
if (error) {
|
||||||
|
loginButtonsSession.set('errorMessage', error.reason || "Unknown error");
|
||||||
|
} else {
|
||||||
|
loginButtonsSession.closeDropdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var forgotPassword = function () {
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
|
||||||
|
var email = document.getElementById("forgot-password-email").value;
|
||||||
|
if (email.indexOf('@') !== -1) {
|
||||||
|
Accounts.forgotPassword({email: email}, function (error) {
|
||||||
|
if (error)
|
||||||
|
loginButtonsSession.set('errorMessage', error.reason || "Unknown error");
|
||||||
|
else
|
||||||
|
loginButtonsSession.set('infoMessage', "Email sent");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
loginButtonsSession.set('errorMessage', "Invalid email");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var changePassword = function () {
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
|
||||||
|
var oldPassword = elementValueById('login-old-password');
|
||||||
|
|
||||||
|
var password = elementValueById('login-password');
|
||||||
|
if (!Accounts._loginButtons.validatePassword(password))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!matchPasswordAgainIfPresent())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Accounts.changePassword(oldPassword, password, function (error) {
|
||||||
|
if (error) {
|
||||||
|
loginButtonsSession.set('errorMessage', error.reason || "Unknown error");
|
||||||
|
} else {
|
||||||
|
loginButtonsSession.set('infoMessage', "Password changed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var matchPasswordAgainIfPresent = function () {
|
||||||
|
var passwordAgain = elementValueById('login-password-again');
|
||||||
|
if (passwordAgain !== null) {
|
||||||
|
var password = elementValueById('login-password');
|
||||||
|
if (password !== passwordAgain) {
|
||||||
|
loginButtonsSession.set('errorMessage', "Passwords don't match");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
var correctDropdownZIndexes = function () {
|
||||||
|
// IE <= 7 has a z-index bug that means we can't just give the
|
||||||
|
// dropdown a z-index and expect it to stack above the rest of
|
||||||
|
// the page even if nothing else has a z-index. The nature of
|
||||||
|
// the bug is that all positioned elements are considered to
|
||||||
|
// have z-index:0 (not auto) and therefore start new stacking
|
||||||
|
// contexts, with ties broken by page order.
|
||||||
|
//
|
||||||
|
// The fix, then is to give z-index:1 to all ancestors
|
||||||
|
// of the dropdown having z-index:0.
|
||||||
|
for(var n = document.getElementById('login-dropdown-list').parentNode;
|
||||||
|
n.nodeName !== 'BODY';
|
||||||
|
n = n.parentNode)
|
||||||
|
if (n.style.zIndex === 0)
|
||||||
|
n.style.zIndex = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}) ();
|
||||||
60
packages/accounts-ui-unstyled/login_buttons_session.js
Normal file
60
packages/accounts-ui-unstyled/login_buttons_session.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
(function () {
|
||||||
|
var VALID_KEYS = [
|
||||||
|
'dropdownVisible',
|
||||||
|
|
||||||
|
// XXX consider replacing these with one key that has an enum for values.
|
||||||
|
'inSignupFlow',
|
||||||
|
'inForgotPasswordFlow',
|
||||||
|
'inChangePasswordFlow',
|
||||||
|
|
||||||
|
'errorMessage',
|
||||||
|
'infoMessage',
|
||||||
|
|
||||||
|
'resetPasswordToken',
|
||||||
|
'enrollAccountToken',
|
||||||
|
'justValidatedUser',
|
||||||
|
|
||||||
|
'configureLoginServiceDialogVisible',
|
||||||
|
'configureLoginServiceDialogServiceName',
|
||||||
|
'configureLoginServiceDialogSaveDisabled'
|
||||||
|
];
|
||||||
|
|
||||||
|
var validateKey = function (key) {
|
||||||
|
if (!_.contains(VALID_KEYS, key))
|
||||||
|
throw new Error("Invalid key in loginButtonsSession: " + key);
|
||||||
|
};
|
||||||
|
|
||||||
|
KEY_PREFIX = "Meteor.loginButtons.";
|
||||||
|
|
||||||
|
// XXX we should have a better pattern for code private to a package like this one
|
||||||
|
Accounts._loginButtonsSession = {
|
||||||
|
set: function(key, value) {
|
||||||
|
validateKey(key);
|
||||||
|
Session.set(KEY_PREFIX + key, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
get: function(key) {
|
||||||
|
validateKey(key);
|
||||||
|
return Session.get(KEY_PREFIX + key);
|
||||||
|
},
|
||||||
|
|
||||||
|
closeDropdown: function () {
|
||||||
|
this.set('inSignupFlow', false);
|
||||||
|
this.set('inForgotPasswordFlow', false);
|
||||||
|
this.set('inChangePasswordFlow', false);
|
||||||
|
this.set('dropdownVisible', false);
|
||||||
|
this.resetMessages();
|
||||||
|
},
|
||||||
|
|
||||||
|
resetMessages: function () {
|
||||||
|
this.set("errorMessage", null);
|
||||||
|
this.set("infoMessage", null);
|
||||||
|
},
|
||||||
|
|
||||||
|
configureService: function (name) {
|
||||||
|
this.set('configureLoginServiceDialogVisible', true);
|
||||||
|
this.set('configureLoginServiceDialogServiceName', name);
|
||||||
|
this.set('configureLoginServiceDialogSaveDisabled', true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}) ();
|
||||||
11
packages/accounts-ui-unstyled/login_buttons_single.html
Normal file
11
packages/accounts-ui-unstyled/login_buttons_single.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<template name="loginButtonsLoggedOutSingleLoginButton">
|
||||||
|
<div class="login-button {{#unless configured}}configure-button{{/unless}}"
|
||||||
|
id="login-buttons-{{name}}">
|
||||||
|
<div class="login-image" id="login-buttons-image-{{name}}"></div>
|
||||||
|
{{#if configured}}
|
||||||
|
<span class="sign-in-text-{{name}}">Sign in with {{capitalizedName}}</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="configure-text-{{name}}">Configure {{capitalizedName}} Login</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
40
packages/accounts-ui-unstyled/login_buttons_single.js
Normal file
40
packages/accounts-ui-unstyled/login_buttons_single.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
(function () {
|
||||||
|
// for convenience
|
||||||
|
var loginButtonsSession = Accounts._loginButtonsSession;
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutSingleLoginButton.events({
|
||||||
|
'click .login-button': function () {
|
||||||
|
var serviceName = this.name;
|
||||||
|
loginButtonsSession.resetMessages();
|
||||||
|
Meteor["loginWith" + capitalize(serviceName)](function (err) {
|
||||||
|
if (!err) {
|
||||||
|
loginButtonsSession.closeDropdown();
|
||||||
|
} else if (err instanceof Accounts.LoginCancelledError) {
|
||||||
|
// do nothing
|
||||||
|
} else if (err instanceof Accounts.ConfigError) {
|
||||||
|
loginButtonsSession.configureService(serviceName);
|
||||||
|
} else {
|
||||||
|
loginButtonsSession.set('errorMessage', err.reason || "Unknown error");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutSingleLoginButton.configured = function () {
|
||||||
|
return !!Accounts.configuration.findOne({service: this.name});
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.loginButtonsLoggedOutSingleLoginButton.capitalizedName = function () {
|
||||||
|
if (this.name === 'github')
|
||||||
|
// XXX we should allow service packages to set their capitalized name
|
||||||
|
return 'GitHub';
|
||||||
|
else
|
||||||
|
return capitalize(this.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
// XXX from http://epeli.github.com/underscore.string/lib/underscore.string.js
|
||||||
|
var capitalize = function(str){
|
||||||
|
str = str == null ? '' : String(str);
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
};
|
||||||
|
}) ();
|
||||||
@@ -8,5 +8,14 @@ Package.on_use(function (api) {
|
|||||||
api.add_files([
|
api.add_files([
|
||||||
'login_buttons_images.css',
|
'login_buttons_images.css',
|
||||||
'login_buttons.html',
|
'login_buttons.html',
|
||||||
'login_buttons.js'], 'client');
|
'login_buttons_single.html',
|
||||||
|
'login_buttons_dropdown.html',
|
||||||
|
'login_buttons_dialogs.html',
|
||||||
|
|
||||||
|
'login_buttons_session.js',
|
||||||
|
|
||||||
|
'login_buttons.js',
|
||||||
|
'login_buttons_single.js',
|
||||||
|
'login_buttons_dropdown.js',
|
||||||
|
'login_buttons_dialogs.js'], 'client');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -152,11 +152,16 @@
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#login-buttons .message {
|
.accounts-dialog .message {
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#login-buttons-message-dialog .message {
|
||||||
|
/* we intentionally want it bigger on this dialog since it's the only thing displayed */
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.accounts-dialog .error-message {
|
.accounts-dialog .error-message {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
@@ -197,15 +202,15 @@
|
|||||||
margin-top: -40px; /* = approximately -height/2, though height can change */
|
margin-top: -40px; /* = approximately -height/2, though height can change */
|
||||||
}
|
}
|
||||||
|
|
||||||
@configure-login-services-dialog-width: 530px;
|
@configure-login-service-dialog-width: 530px;
|
||||||
#configure-login-services-dialog {
|
#configure-login-service-dialog {
|
||||||
width: @configure-login-services-dialog-width;
|
width: @configure-login-service-dialog-width;
|
||||||
margin-left: -(@configure-login-services-dialog-width
|
margin-left: -(@configure-login-service-dialog-width
|
||||||
+ @login-buttons-accounts-dialog-padding-left) / 2;
|
+ @login-buttons-accounts-dialog-padding-left) / 2;
|
||||||
margin-top: -180px; /* = approximately -height/2, though height can change */
|
margin-top: -180px; /* = approximately -height/2, though height can change */
|
||||||
}
|
}
|
||||||
|
|
||||||
#configure-login-services-dialog .login-button-configure {
|
#configure-login-service-dialog .login-button-configure {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,37 +232,37 @@
|
|||||||
background-color: rgba(0, 0, 0, 0.7);
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
#configure-login-services-dialog table {
|
#configure-login-service-dialog table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configure-login-services-dialog .configuration_labels {
|
#configure-login-service-dialog .configuration_labels {
|
||||||
width: 30%;
|
width: 30%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configure-login-services-dialog .configuration_inputs {
|
#configure-login-service-dialog .configuration_inputs {
|
||||||
width: 70%;
|
width: 70%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configure-login-services-dialog input {
|
#configure-login-service-dialog input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: "Courier New", Courier, monospace;
|
font-family: "Courier New", Courier, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configure-login-services-dialog ol {
|
#configure-login-service-dialog ol {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configure-login-services-dialog .new-section {
|
#configure-login-service-dialog .new-section {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configure-login-services-dialog ol li {
|
#configure-login-service-dialog ol li {
|
||||||
margin-left: 30px;
|
margin-left: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configure-login-services-dialog .url {
|
#configure-login-service-dialog .url {
|
||||||
font-family: "Courier New", Courier, monospace;
|
font-family: "Courier New", Courier, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template name="configureLoginServicesDialogForWeibo">
|
<template name="configureLoginServiceDialogForWeibo">
|
||||||
<p>
|
<p>
|
||||||
First, you'll need to register your app on Weibo. Follow these steps:
|
First, you'll need to register your app on Weibo. Follow these steps:
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
Template.configureLoginServicesDialogForWeibo.siteUrl = function () {
|
Template.configureLoginServiceDialogForWeibo.siteUrl = function () {
|
||||||
// Weibo doesn't recognize localhost as a domain
|
// Weibo doesn't recognize localhost as a domain
|
||||||
return Meteor.absoluteUrl({replaceLocalhost: true});
|
return Meteor.absoluteUrl({replaceLocalhost: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.configureLoginServicesDialogForWeibo.fields = function () {
|
Template.configureLoginServiceDialogForWeibo.fields = function () {
|
||||||
return [
|
return [
|
||||||
{property: 'clientId', label: 'App Key'},
|
{property: 'clientId', label: 'App Key'},
|
||||||
{property: 'secret', label: 'App Secret'}
|
{property: 'secret', label: 'App Secret'}
|
||||||
|
|||||||
Reference in New Issue
Block a user