diff --git a/app.js b/app.js index fe7abd4586..5ee26997a2 100644 --- a/app.js +++ b/app.js @@ -150,6 +150,7 @@ app.use(helmet.contentSecurityPolicy({ 'graph.facebook.com', '*.githubusercontent.com', '*.googleusercontent.com', + '*' ].concat(trusted), fontSrc: ['*.googleapis.com'].concat(trusted), mediaSrc: [ @@ -248,7 +249,7 @@ app.get( challengesController.returnChallenge ); app.get( - '/users/:username', + '/campers/:username', userController.returnUser ); app.all('/account', passportConf.isAuthenticated); diff --git a/controllers/user.js b/controllers/user.js index a69c7b64e0..3504679509 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -168,7 +168,7 @@ exports.getAccount = function(req, res) { /** - * GET /users/:username + * GET /campers/:username * Public Profile page. */ @@ -189,16 +189,16 @@ exports.returnUser = function(req, res, next) { codepenProfile: user.profile.codepenProfile, twitterHandle: user.profile.twitterHandle, bio: user.profile.bio, - website1: user.portfolio.website1Link, + picture: user.profile.picture, + website1Link: user.portfolio.website1Link, website1Title: user.portfolio.website1Title, website1Image: user.portfolio.website1Image, - website2: user.portfolio.website2Link, + website2Link: user.portfolio.website2Link, website2Title: user.portfolio.website2Title, website2Image: user.portfolio.website2Image, - website3: user.portfolio.website3Link, + website3Link: user.portfolio.website3Link, website3Title: user.portfolio.website3Title, website3Image: user.portfolio.website3Image, - picture: user.profile.picture, challenges: c, ch: user.challengesHash, moment: moment @@ -283,6 +283,7 @@ exports.postUpdateProfile = function(req, res, next) { user.profile.codepenProfile = req.body.codepenProfile || ''; user.profile.twitterHandle = req.body.twitterHandle || ''; user.profile.bio = req.body.bio || ''; + user.profile.picture = req.body.picture || ''; user.portfolio.website1Title = req.body.website1Title || ''; user.portfolio.website1Link = req.body.website1Link || ''; user.portfolio.website1Image = req.body.website1Image || ''; diff --git a/public/css/main.less b/public/css/main.less index b07a205529..d2bf63fb25 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -11,7 +11,7 @@ html,body,div,span,a,li,td,th { font-weight: 300; } -li { +li, .wrappable { white-space: pre; /* CSS 2.0 */ white-space: pre-wrap; /* CSS 2.1 */ white-space: pre-line; /* CSS 3.0 */ @@ -403,4 +403,16 @@ thead { marign: 0 auto; width: 80%; } +} + +.min650 { + min-height: 630px; +} + +.portfolio-image { + max-height: 164px; +} + +.flat-top { + margin-top: -5px; } \ No newline at end of file diff --git a/views/account/profile.jade b/views/account/profile.jade index 0fb61c6926..0a248af00f 100644 --- a/views/account/profile.jade +++ b/views/account/profile.jade @@ -62,6 +62,15 @@ block content .col-sm-4 input.form-control(type='text', name='location', id='location', ng-model='user.profile.location') + .form-group + label.col-sm-3.col-sm-offset-2.control-label(for='email') Link to Profile Photo + .col-sm-4 + input.form-control(type='url', name='picture', id='picture', ng-model='user.profile.picture', placeholder='http://') + .col-sm-4.col-sm-offset-5(ng-show="profileForm.picture.$error.url && !profileForm.picture.$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-2.control-label(for='bio') Bio (140 characters) .col-sm-4 @@ -202,19 +211,18 @@ block content span.ion-edit | Update my Portfolio br - .panel - .big-break - .container.text-center - a.btn.btn-cta.signup-btn(href='/') Take me to my current challenge - .big-break - .container.text-center - a.btn.btn-default.btn-big(href='/logout') Sign out - .big-break - + .panel.panel-primary + .panel-heading.text-center Actions + .panel-body + .col-xs-12 + a.btn.btn-lg.btn-block.btn-info.btn-link-social(href='/campers/#{user.profile.username}') Check out my Public Profile + a.btn.btn-lg.btn-block.btn-primary.btn-link-social(href='/') Take me to my current challenge + a.btn.btn-lg.btn-block.btn-warning.btn-link-social(href='/logout') Sign out + br - if (!user.google || !user.facebook || !user.github || !user.linkedin || !user.twitter) - .panel - .container.text-center - h1 Link other services to your account: + .panel.panel-primary + .panel-heading.text-center Link other services to your account: + .panel-body - if (!user.google) .col-xs-12 a.btn.btn-lg.btn-block.btn-google-plus.btn-link-social(href='/auth/google') @@ -242,9 +250,9 @@ block content | Link Twitter with your account br - if (ch[0] > 0) - .panel - .container - h1 Completed Challenges + .panel.panel-primary + .panel-heading.text-center Completed Challenges: + .panel-body .col-xs-12 table.table.table-striped thead @@ -257,38 +265,39 @@ block content td= challenges[challenge.challengeNumber].name td= moment(ch[challenge.challengeNumber], 'X').format("MMM DD, YYYY") br - .panel - .container - h3 Danger Zone - button.btn.btn-danger.confirm-deletion - span.ion-trash-b - | I want to delete my account - br - br - script. - $('.confirm-deletion').on("click", function() { - $('#modal-dialog').modal('show'); - }); - #modal-dialog.modal.animated.wobble - .modal-dialog - .modal-content - .modal-header - a.close(href='#', data-dismiss='modal', aria-hidden='true') × - h3 Are you really leaving us? - .modal-body - p Pro Tip: If you tweet feedback to  - a(href="https://twitter.com/intent/tweet?text=Hey%20@freecodecamp") @FreeCodeCamp - | , we'll act quickly on it! - .modal-footer - a.btn.btn-success.btn-block(href='#', data-dismiss='modal', aria-hidden='true') - span.ion-happy - | Nevermind, I'll stick around - br - form(action='/account/delete', method='POST') - input(type='hidden', name='_csrf', value=_csrf) - button.btn.btn-danger.btn-block(type='submit') - span.ion-trash-b - | Yes, Delete my account + .panel.panel-danger + .panel-heading.text-center Danger Zone: + .panel-body + .col-xs-12.text-center + button.btn.btn-danger.btn-small.confirm-deletion + span.ion-trash-b + | I want to delete my account + br + br + script. + $('.confirm-deletion').on("click", function() { + $('#modal-dialog').modal('show'); + }); + #modal-dialog.modal.animated.wobble + .modal-dialog + .modal-content + .modal-header + a.close(href='#', data-dismiss='modal', aria-hidden='true') × + h3 Are you really leaving us? + .modal-body + p Pro Tip: If you tweet feedback to  + a(href="https://twitter.com/intent/tweet?text=Hey%20@freecodecamp") @FreeCodeCamp + | , we'll act quickly on it! + .modal-footer + a.btn.btn-success.btn-block(href='#', data-dismiss='modal', aria-hidden='true') + span.ion-happy + | Nevermind, I'll stick around + br + form(action='/account/delete', method='POST') + input(type='hidden', name='_csrf', value=_csrf) + button.btn.btn-danger.btn-block(type='submit') + span.ion-trash-b + | Yes, Delete my account diff --git a/views/account/show.jade b/views/account/show.jade index 068f98586e..5a5f853faf 100644 --- a/views/account/show.jade +++ b/views/account/show.jade @@ -2,39 +2,101 @@ extends ../layout block content .row .col-sm-12.col-md-8.col-xs-12 - .panel.panel-primary + .panel.panel-primary.min650 .panel-heading.text-center @#{username}'s Portfolio: .panel-body + if (website1Title && website1Link && website1Image) + .row + .col-xs-12.col-md-4 + img.img-responsive.portfolio-image.img-center(src=website1Image, alt="@#{username}'s #{website1Title") + .col-xs-12.col-md-8 + h3.text-center.wrappable.flat-top= website1Title + a.btn.btn-lg.btn-block.btn-info(href=website1Link, target='_blank') + i.fa.icon-beaker + | Try it out + br + if (website1Title && website1Link && !website1Image) + .col-xs-12.col-md-12 + h3.text-center.wrappable.flat-top= website1Title + a.btn.btn-lg.btn-block.btn-info(href=website1Link, target='_blank') + i.fa.icon-beaker + | Try it out + br + if (website2Title && website2Link && website2Image) + .row + .col-xs-12.col-md-4 + img.img-responsive.portfolio-image.img-center(src=website2Image, alt="@#{username}'s #{website2Title") + .col-xs-12.col-md-8 + h3.text-center.wrappable.flat-top= website2Title + a.btn.btn-lg.btn-block.btn-info(href=website2Link, target='_blank') + i.fa.icon-beaker + | Try it out + br + if (website2Title && website2Link && !website2Image) + .col-xs-12.col-md-12 + h3.text-center.wrappable.flat-top= website2Title + a.btn.btn-lg.btn-block.btn-info(href=website2Link, target='_blank') + i.fa.icon-beaker + | Try it out + br + if (website3Title && website3Link && website3Image) + .row + .col-xs-12.col-md-4 + img.img-responsive.portfolio-image.img-center(src=website3Image, alt="@#{username}'s #{website1Title") + .col-xs-12.col-md-8 + h3.text-center.wrappable.flat-top= website3Title + a.btn.btn-lg.btn-block.btn-info(href=website3Link, target='_blank') + i.fa.icon-beaker + | Try it out + if (website3Title && website3Link && !website3Image) + .col-xs-12.col-md-12 + h3.text-center.wrappable.flat-top= website3Title + a.btn.btn-lg.btn-block.btn-info(href=website3Link, target='_blank') + i.fa.icon-beaker + | Try it out .col-sm-12.col-md-4.col-xs-12 - .panel.panel-primary + .panel.panel-primary.min650 .panel-heading.text-center About @#{username}: .panel-body .col-xs-12 - img.img-center.img-responsive.public-profile-img(src=picture) + if picture + img.img-center.img-responsive.public-profile-img(src=picture) + else + img.img-center.img-responsive.public-profile-img(src='#{user.gravatar(200)}') .row - .col-xs-4 - h1.text-center + .col-xs-4.text-right + h1 = "[ " + user.points + " ]" - .col-xs-8 + .col-xs-8.text-left h1 - if (twitterHandle) a.ion-social-twitter.text-primary(title="@#{username}'s Twitter Profile", href="http://twitter.com/#{twitterHandle}") - a.ion-social-github.text-primary(title="@#{username}'s GitHub Profile", href=githubProfile) - a.ion-social-linkedin.text-primary(title="@#{username}'s LinkedIn Profile", href=linkedinProfile) - a.ion-social-codepen.text-primary(title="@#{username}'s CodePen Profile", href=codepenProfile) - a.ion-social-javascript.text-primary(title="@#{username}'s CoderByte Profile", href=coderbyteProfile) - .col-xs-12 - h2= name - h3= location - - if (ch[0] > 0) - .container - h1 Completed Challenges - .col-xs-12 - table.table.table-striped - thead - tr - th Challenge - th Date Finished + - if (githubProfile) + a.ion-social-github.text-primary(title="@#{username}'s GitHub Profile", href=githubProfile) + - if (linkedinProfile) + a.ion-social-linkedin.text-primary(title="@#{username}'s LinkedIn Profile", href=linkedinProfile) + - if (codepenProfile) + a.ion-social-codepen.text-primary(title="@#{username}'s CodePen Profile", href=codepenProfile) + - if (coderbyteProfile) + a.ion-social-javascript.text-primary(title="@#{username}'s CoderByte Profile", href=coderbyteProfile) + .col-xs-12.text-center + h1.flat-top.wrappable= name + h3.flat-top.bolded.wrappable= location + h4.flat-top.wrappable= bio + br + + - if (ch[0] > 0) + .row + .col-sm-12 + .panel.panel-primary + .panel-heading.text-center @#{username}'s Completed Challenges: + .panel-body + .col-sm-12 + table.table.table-striped + thead + tr + th Challenge + th Date Finished for challenge in challenges if ch[challenge.challengeNumber] > 0 tr