",
diff --git a/seed/challenges/intermediate-bonfires.json b/seed/challenges/intermediate-bonfires.json
index 5ae11fc7a4..f9c9be22d8 100644
--- a/seed/challenges/intermediate-bonfires.json
+++ b/seed/challenges/intermediate-bonfires.json
@@ -103,7 +103,7 @@
"difficulty": "2.02",
"description": [
"Convert the given number into a roman numeral.",
- "All
roman numerals answers should be provided in upper-case.",
+ "All
roman numerals answers should be provided in upper-case.",
"Remember to use
RSAP if you get stuck. Try to pair program. Write your own code."
],
"challengeSeed": [
@@ -356,7 +356,7 @@
" return arr1;",
"}",
"",
- "unite([1, 2, 3], [5, 2, 1, 4], [2, 1]);"
+ "unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);"
],
"tests": [
"assert.deepEqual(unite([1, 3, 2], [5, 2, 1, 4], [2, 1]), [1, 3, 2, 5, 4], 'should return the union of the given arrays');",
@@ -675,7 +675,8 @@
"tests": [
"assert.deepEqual(steamroller([[['a']], [['b']]]), ['a', 'b'], 'should flatten nested arrays');",
"assert.deepEqual(steamroller([1, [2], [3, [[4]]]]), [1, 2, 3, 4], 'should flatten nested arrays');",
- "assert.deepEqual(steamroller([1, [], [3, [[4]]]]), [1, 3, 4], 'should work with empty arrays');"
+ "assert.deepEqual(steamroller([1, [], [3, [[4]]]]), [1, 3, 4], 'should work with empty arrays');",
+ "assert.deepEqual(steamroller([1, {}, [3, [[4]]]]), [1, {}, 3, 4], 'should work with actual objects');"
],
"MDNlinks": [
"Array.isArray()"
diff --git a/seed/challenges/jquery-ajax-and-json.json b/seed/challenges/jquery-ajax-and-json.json
index ed9c2c64c9..dad707f590 100644
--- a/seed/challenges/jquery-ajax-and-json.json
+++ b/seed/challenges/jquery-ajax-and-json.json
@@ -257,9 +257,9 @@
"dashedName": "waypoint-target-the-same-element-with-multiple-jQuery-Selectors",
"difficulty": 3.06,
"description": [
- "Now you know three ways of targeting elements: by type (
$('button')
), by class (
($('.btn')
), and by id (
($'#target1')
).",
+ "Now you know three ways of targeting elements: by type
$('button')
, by class
$('.btn')
), and by id
$('#target1')
).",
"Use each of these jQuery selectors to target your
button
element with the class \"btn\" and the id \"target1\".",
- "Use the
addClass()
jQuery function to give the element one new class for each selector: \"animated\", \"shake\", and \"button-primary\"."
+ "Use the
addClass()
jQuery function to give the element one new class for each selector: \"animated\", \"shake\", and \"btn-primary\"."
],
"tests": [
"assert(editor.match(/\\$\\(\\'button\\'\\)/g), 'Use the
$\\(\\'button\\'\\)
selector.')",
diff --git a/seed/loopbackMigration.js b/seed/loopbackMigration.js
index 333629438a..d167ccec8b 100644
--- a/seed/loopbackMigration.js
+++ b/seed/loopbackMigration.js
@@ -92,6 +92,8 @@ var users = dbObservable
user.username = 'fcc' + uuid.v4().slice(0, 8);
if (user.github) {
user.isGithubCool = true;
+ } else {
+ user.isMigrationGrandfathered = true;
}
return user;
})
diff --git a/server/boot/challenge.js b/server/boot/challenge.js
index 5e0cc37f0d..4228a3ff15 100644
--- a/server/boot/challenge.js
+++ b/server/boot/challenge.js
@@ -1,35 +1,3 @@
-/**
- * Created by nathanleniz on 5/15/15.
- * Copyright (c) 2015, Free Code Camp
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- 3. Neither the name of the copyright holder nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
var R = require('ramda'),
Rx = require('rx'),
assign = require('object.assign'),
diff --git a/server/boot/user.js b/server/boot/user.js
index 7278e0a07d..b1d3c6d8d3 100644
--- a/server/boot/user.js
+++ b/server/boot/user.js
@@ -122,113 +122,123 @@ module.exports = function(app) {
*/
function returnUser(req, res, next) {
+ const username = req.params.username.toLowerCase();
+ const { path } = req;
User.findOne(
- { where: { 'username': req.params.username.toLowerCase() } },
+ { where: { username } },
function(err, user) {
if (err) {
return next(err);
}
- if (user) {
- user.progressTimestamps =
- user.progressTimestamps.sort(function(a, b) {
- return a - b;
- });
-
- var timeObject = Object.create(null);
- R.forEach(function(time) {
- timeObject[moment(time).format('YYYY-MM-DD')] = time;
- }, user.progressTimestamps);
-
- var tmpLongest = 1;
- var timeKeys = R.keys(timeObject);
-
- user.longestStreak = 0;
- for (var i = 1; i <= timeKeys.length; i++) {
- if (moment(timeKeys[i - 1]).add(1, 'd').toString()
- === moment(timeKeys[i]).toString()) {
- tmpLongest++;
- if (tmpLongest > user.longestStreak) {
- user.longestStreak = tmpLongest;
- }
- } else {
- tmpLongest = 1;
- }
- }
-
- timeKeys = timeKeys.reverse();
- tmpLongest = 1;
-
- user.currentStreak = 1;
- var today = moment(Date.now()).format('YYYY-MM-DD');
-
- const yesterday = moment(today).subtract(1, 'd').toString();
- const yesteryesterday = moment(today).subtract(2, 'd').toString();
-
- if (
- moment(today).toString() === moment(timeKeys[0]).toString() ||
- yesterday === moment(timeKeys[0]).toString() ||
- yesteryesterday === moment(timeKeys[0]).toString()
- ) {
- for (var _i = 1; _i <= timeKeys.length; _i++) {
-
- if (
- moment(timeKeys[_i - 1]).subtract(1, 'd').toString() ===
- moment(timeKeys[_i]).toString()
- ) {
-
- tmpLongest++;
-
- if (tmpLongest > user.currentStreak) {
- user.currentStreak = tmpLongest;
- }
- } else {
- break;
- }
- }
- } else {
- user.currentStreak = 1;
- }
-
- var data = {};
- var progressTimestamps = user.progressTimestamps;
- progressTimestamps.forEach(function(timeStamp) {
- data[(timeStamp / 1000)] = 1;
- });
-
- user.currentStreak = user.currentStreak || 1;
- user.longestStreak = user.longestStreak || 1;
- var challenges = user.completedChallenges.filter(function( obj ) {
- return obj.challengeType === 3 || obj.challengeType === 4;
- });
-
- res.render('account/show', {
- title: 'Camper ' + user.username + '\'s portfolio',
- username: user.username,
- name: user.name,
- location: user.location,
- githubProfile: user.githubProfile,
- linkedinProfile: user.linkedinProfile,
- codepenProfile: user.codepenProfile,
- facebookProfile: user.facebookProfile,
- twitterHandle: user.twitterHandle,
- bio: user.bio,
- picture: user.picture,
- progressTimestamps: user.progressTimestamps,
- challenges: challenges,
- calender: data,
- moment: moment,
- longestStreak: user.longestStreak +
- (user.longestStreak === 1 ? ' day' : ' days'),
- currentStreak: user.currentStreak +
- (user.currentStreak === 1 ? ' day' : ' days')
- });
- } else {
+ if (!user) {
req.flash('errors', {
- msg: "404: We couldn't find a page with that url. " +
- 'Please double check the link.'
+ msg: `404: We couldn't find path ${ path }`
});
return res.redirect('/');
}
+ if (!user.isGithubCool && !user.isMigrationGrandfathered) {
+ req.flash('errors', {
+ msg: `
+ user ${ username } has not completed account signup
+ `
+ });
+ return res.redirect('/');
+ }
+ user.progressTimestamps =
+ user.progressTimestamps.sort(function(a, b) {
+ return a - b;
+ });
+
+ var timeObject = Object.create(null);
+ R.forEach(function(time) {
+ timeObject[moment(time).format('YYYY-MM-DD')] = time;
+ }, user.progressTimestamps);
+
+ var tmpLongest = 1;
+ var timeKeys = R.keys(timeObject);
+
+ user.longestStreak = 0;
+ for (var i = 1; i <= timeKeys.length; i++) {
+ if (moment(timeKeys[i - 1]).add(1, 'd').toString()
+ === moment(timeKeys[i]).toString()) {
+ tmpLongest++;
+ if (tmpLongest > user.longestStreak) {
+ user.longestStreak = tmpLongest;
+ }
+ } else {
+ tmpLongest = 1;
+ }
+ }
+
+ timeKeys = timeKeys.reverse();
+ tmpLongest = 1;
+
+ user.currentStreak = 1;
+ var today = moment(Date.now()).format('YYYY-MM-DD');
+
+ const yesterday = moment(today).subtract(1, 'd').toString();
+ const yesteryesterday = moment(today).subtract(2, 'd').toString();
+
+ if (
+ moment(today).toString() === moment(timeKeys[0]).toString() ||
+ yesterday === moment(timeKeys[0]).toString() ||
+ yesteryesterday === moment(timeKeys[0]).toString()
+ ) {
+ for (var _i = 1; _i <= timeKeys.length; _i++) {
+
+ if (
+ moment(timeKeys[_i - 1]).subtract(1, 'd').toString() ===
+ moment(timeKeys[_i]).toString()
+ ) {
+
+ tmpLongest++;
+
+ if (tmpLongest > user.currentStreak) {
+ user.currentStreak = tmpLongest;
+ }
+ } else {
+ break;
+ }
+ }
+ } else {
+ user.currentStreak = 1;
+ }
+
+ var data = {};
+ var progressTimestamps = user.progressTimestamps;
+ progressTimestamps.forEach(function(timeStamp) {
+ data[(timeStamp / 1000)] = 1;
+ });
+ var challenges = user.completedChallenges.filter(function( obj ) {
+ return obj.challengeType === 3 || obj.challengeType === 4;
+ });
+
+ user.currentStreak = user.currentStreak || 1;
+ user.longestStreak = user.longestStreak || 1;
+
+ res.render('account/show', {
+ title: 'Camper ' + user.username + '\'s portfolio',
+ username: user.username,
+ name: user.name,
+ isMigrationGrandfathered: user.isMigrationGrandfathered,
+ isGithubCool: user.isGithubCool,
+ location: user.location,
+ githubProfile: user.githubProfile,
+ linkedinProfile: user.linkedinProfile,
+ codepenProfile: user.codepenProfile,
+ facebookProfile: user.facebookProfile,
+ twitterHandle: user.twitterHandle,
+ bio: user.bio,
+ picture: user.picture,
+ progressTimestamps: user.progressTimestamps,
+ calender: data,
+ challenges: challenges,
+ moment: moment,
+ longestStreak: user.longestStreak +
+ (user.longestStreak === 1 ? ' day' : ' days'),
+ currentStreak: user.currentStreak +
+ (user.currentStreak === 1 ? ' day' : ' days')
+ });
}
);
}
diff --git a/server/server.js b/server/server.js
index 93b0a1a322..8a75517964 100755
--- a/server/server.js
+++ b/server/server.js
@@ -2,12 +2,13 @@ require('dotenv').load();
var pmx = require('pmx');
pmx.init();
-var assign = require('lodash').assign,
- loopback = require('loopback'),
- boot = require('loopback-boot'),
- expressState = require('express-state'),
- path = require('path'),
- passportProviders = require('./passport-providers');
+var uuid = require('node-uuid'),
+ assign = require('lodash').assign,
+ loopback = require('loopback'),
+ boot = require('loopback-boot'),
+ expressState = require('express-state'),
+ path = require('path'),
+ passportProviders = require('./passport-providers');
var generateKey =
require('loopback-component-passport/lib/models/utils').generateKey;
@@ -43,6 +44,39 @@ passportConfigurator.setupModels({
userCredentialModel: app.models.userCredential
});
+// using es6 argument destructing
+function setProfileFromGithub(
+ user,
+ {
+ profileUrl: githubURL,
+ username
+ },
+ {
+ location,
+ email: githubEmail,
+ id: githubId,
+ 'created_at': joinedGithubOn,
+ blog: website,
+ name
+ }
+) {
+ return assign(
+ user,
+ { isGithubCool: true, isMigrationGrandfathered: false },
+ {
+ name,
+ username: username.toLowerCase(),
+ location,
+ joinedGithubOn,
+ website,
+ githubId,
+ githubURL,
+ githubEmail,
+ githubProfile: githubURL
+ }
+ );
+}
+
var passportOptions = {
emailOptional: true,
profileToUser: function(provider, profile) {
@@ -53,9 +87,9 @@ var passportOptions = {
emails[0].value :
null;
- var username = (profile.username || profile.id);
- username = typeof username === 'string' ? username.toLowerCase() :
- username;
+ // create random username
+ // username will be assigned when camper signups for Github
+ var username = 'fcc' + uuid.v4().slice(0, 8);
var password = generateKey('password');
var userObj = {
username: username,
@@ -65,8 +99,9 @@ var passportOptions = {
if (email) {
userObj.email = email;
}
+
if (provider === 'github-login') {
- userObj.isGithubCool = true;
+ setProfileFromGithub(userObj, profile, profile._json);
}
return userObj;
}
diff --git a/server/views/account/account.jade b/server/views/account/account.jade
index b1204b264e..3e95b67dc6 100644
--- a/server/views/account/account.jade
+++ b/server/views/account/account.jade
@@ -5,67 +5,74 @@ block content
.panel.panel-info(ng-controller="profileValidationController")
.panel-heading.text-center Update your portfolio here:
.panel-body
- if (!user.github)
+ if (!user.isGithubCool)
+ .row
.col-xs-12
- a.btn.btn-lg.btn-block.btn-github.btn-link-social(href='/auth/github')
- i.fa.fa-github
- | Link GitHub with my account
- .col-xs-12
- form.form-horizontal(action='/account/profile', method='POST', novalidate='novalidate', name='profileForm' ng-show="asyncComplete")
- input(type='hidden', name='_csrf', value=_csrf)
- .form-group
- label.col-sm-3.col-sm-offset-1.control-label(for='bio') Bio (140 characters)
- .col-sm-4
- input.form-control(type='text', name='bio', autocomplete="off", ng-model='user.bio', ng-maxlength='140', id='bio')
- .col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.bio.$error.maxlength && !profileForm.bio.$pristine')
- alert(type='danger')
- span.ion-close-circled
- | Your bio must be fewer than 140 characters.
- .form-group
- label.col-sm-3.col-sm-offset-1.control-label(for='email') Twitter
- .col-sm-4
- .input-group.twitter-input
- span.input-group-addon @
- input.form-control(type='text', name='twitterHandle', autocomplete="off", id='twitterHandle', ng-model='user.twitterHandle', ng-maxlength='15', ng-pattern="/^[A-z0-9_]+$/")
- .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.twitterHandle.$error.pattern")
- alert(type='danger')
+ .text-left.btn-info.btn.btn-block.btn-link-social(href='http://www.freecodecamp.com/challenges/waypoint-join-our-chat-room')
+ span Link your account to GitHub to update your portfolio page. Click here if you don’t have a GitHub account yet.
+ .row
+ .col-xs-12
+ a.btn.btn-lg.btn-block.btn-github.btn-link-social(href='/auth/github')
+ i.fa.fa-github
+ | Link GitHub with my account
+
+ .row
+ .col-xs-12
+ form.form-horizontal(action='/account/profile', method='POST', novalidate='novalidate', name='profileForm' ng-show="asyncComplete")
+ input(type='hidden', name='_csrf', value=_csrf)
+ .form-group
+ label.col-sm-3.col-sm-offset-1.control-label(for='bio') Bio (140 characters)
+ .col-sm-4
+ input.form-control(type='text', name='bio', autocomplete="off", ng-model='user.bio', ng-maxlength='140', id='bio')
+ .col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.bio.$error.maxlength && !profileForm.bio.$pristine')
+ alert(type='danger')
span.ion-close-circled
- | Your Twitter handle should only contain letters, numbers and underscores (az10_).
- .col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.twitterHandle.$error.maxlength && !profileForm.twitterHandle.$pristine')
- alert(type='danger')
+ | Your bio must be fewer than 140 characters.
+ .form-group
+ label.col-sm-3.col-sm-offset-1.control-label(for='email') Twitter
+ .col-sm-4
+ .input-group.twitter-input
+ span.input-group-addon @
+ input.form-control(type='text', name='twitterHandle', autocomplete="off", id='twitterHandle', ng-model='user.twitterHandle', ng-maxlength='15', ng-pattern="/^[A-z0-9_]+$/")
+ .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.twitterHandle.$error.pattern")
+ alert(type='danger')
+ span.ion-close-circled
+ | Your Twitter handle should only contain letters, numbers and underscores (az10_).
+ .col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.twitterHandle.$error.maxlength && !profileForm.twitterHandle.$pristine')
+ alert(type='danger')
+ span.ion-close-circled
+ | Your name must be fewer than 15 characters.
+
+ .form-group
+ label.col-sm-3.col-sm-offset-1.control-label(for='email') CodePen
+ .col-sm-4
+ input.form-control(type='url', name='codepenProfile', id='codepenProfile', autocomplete="off", ng-model='user.codepenProfile', placeholder='http://')
+ .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.codepenProfile.$error.url && !profileForm.codepenProfile.$pristine")
+ alert(type='danger')
span.ion-close-circled
- | Your name must be fewer than 15 characters.
+ | Please enter a valid URL format (http://www.example.com).
- .form-group
- label.col-sm-3.col-sm-offset-1.control-label(for='email') CodePen
- .col-sm-4
- input.form-control(type='url', name='codepenProfile', id='codepenProfile', autocomplete="off", ng-model='user.codepenProfile', placeholder='http://')
- .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.codepenProfile.$error.url && !profileForm.codepenProfile.$pristine")
- alert(type='danger')
- span.ion-close-circled
- | Please enter a valid URL format (http://www.example.com).
+ .form-group
+ label.col-sm-3.col-sm-offset-1.control-label(for='email') LinkedIn
+ .col-sm-4
+ input.form-control(type='url', name='linkedinProfile', id='linkedinProfile', autocomplete="off", ng-model='user.linkedinProfile', placeholder='http://')
+ .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.linkedinProfile.$error.url && !profileForm.linkedinProfile.$pristine")
+ alert(type='danger')
+ span.ion-close-circled
+ | Please enter a valid URL format (http://www.example.com).
- .form-group
- label.col-sm-3.col-sm-offset-1.control-label(for='email') LinkedIn
- .col-sm-4
- input.form-control(type='url', name='linkedinProfile', id='linkedinProfile', autocomplete="off", ng-model='user.linkedinProfile', placeholder='http://')
- .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.linkedinProfile.$error.url && !profileForm.linkedinProfile.$pristine")
- alert(type='danger')
- span.ion-close-circled
- | Please enter a valid URL format (http://www.example.com).
+ .form-group
+ label.col-sm-3.col-sm-offset-1.control-label(for='email') Facebook
+ .col-sm-4
+ input.form-control(type='url', name='facebookProfile', id='facebookProfile', autocomplete="off", ng-model='user.facebookProfile', placeholder='http://')
+ .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.facebookProfile.$error.url && !profileForm.facebookProfile.$pristine")
+ alert(type='danger')
+ span.ion-close-circled
+ | Please enter a valid URL format (http://www.example.com).
- .form-group
- label.col-sm-3.col-sm-offset-1.control-label(for='email') Facebook
- .col-sm-4
- input.form-control(type='url', name='facebookProfile', id='facebookProfile', autocomplete="off", ng-model='user.facebookProfile', placeholder='http://')
- .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.facebookProfile.$error.url && !profileForm.facebookProfile.$pristine")
- alert(type='danger')
- span.ion-close-circled
- | Please enter a valid URL format (http://www.example.com).
-
- button.btn.btn-lg.btn-block.btn-primary.btn-link-social(type='submit', ng-disabled='profileForm.$invalid')
- span.ion-edit
- | Update my info
+ button.btn.btn-lg.btn-block.btn-primary.btn-link-social(type='submit', ng-disabled='!user.isGithubCool || profileForm.$invalid')
+ span.ion-edit
+ | Update my info
.panel.panel-info
.panel-heading.text-center Manage your account here:
diff --git a/server/views/account/email-signup.jade b/server/views/account/email-signup.jade
index c270b2470c..965eca605e 100644
--- a/server/views/account/email-signup.jade
+++ b/server/views/account/email-signup.jade
@@ -19,25 +19,6 @@ block content
alert(type='danger')
span.ion-close-circled
| That email address is already in use.
- .form-group
- .col-sm-6.col-sm-offset-3
- input.form-control(type='text', name='username', ng-keypress='', autocomplete="off", id='username', placeholder='username', ng-model='username', unique-username='', required, ng-minlength=5, ng-maxlength=20, ng-pattern="/^[A-z0-9_]+$/")
- .col-sm-6.col-sm-offset-3(ng-cloak, ng-show="profileForm.username.$error.pattern && !signupForm.username.$pristine")
- alert(type='danger')
- span.ion-close-circled
- | Your username should only contain letters, numbers and underscores (az10_).
- .col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.username.$error.unique && !signupForm.username.$pristine")
- alert(type='danger')
- span.ion-close-circled
- | This username is taken.
- .col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.username.$error.minlength && !signupForm.username.$pristine")
- alert(type='danger')
- span.ion-close-circled
- | Your username must be at least 5 characters long.
- .col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.username.$error.maxlength && !signupForm.username.$pristine")
- alert(type='danger')
- span.ion-close-circled
- | Your usernames must be 20 characters or fewer.
.form-group
.col-sm-6.col-sm-offset-3
input.form-control(type='password', ng-model='password', name='password', id='password', placeholder='password', required, ng-minlength=8)
diff --git a/server/views/partials/navbar.jade b/server/views/partials/navbar.jade
index 8d310d8846..a306151639 100644
--- a/server/views/partials/navbar.jade
+++ b/server/views/partials/navbar.jade
@@ -21,15 +21,7 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height
a.btn.signup-btn.signup-btn-nav(href='/login') Sign in
else
li
- if (user.username)
- a(href='/' + user.username) [ #{user.progressTimestamps.length} ]
-
- else
- a(href='/account') [ #{user.progressTimestamps.length} ]
+ a(href='/account') [ #{user.progressTimestamps.length} ]
.hidden-xs.hidden-sm
- if (user.username)
- a(href='/' + user.username)
- img.profile-picture.float-right(src='#{user.picture}')
- else
- a(href='/account')
- img.profile-picture.float-right(src='#{user.picture}')
+ a(href='/account')
+ img.profile-picture.float-right(src='#{user.picture}')