dstroot / skeleton

Node Express MongoDB Bootstrap Passport... all rolled up into juicy goodness
skeleton-app.jit.su
MIT License
370 stars 47 forks source link

Adding a new page to the app #24

Closed cmpsoares91 closed 10 years ago

cmpsoares91 commented 10 years ago

Hey Dan,

I came across something strange:

I'm trying to add a user connection system to the app but keep getting the same 500 error message: Error: Failed to lookup view "/workspace/friends" in views directory "/home/oneprovider/QuantBull/views"

But compared to the other controller/jade files it looks fine. Mind giving it a look, I need some more experienced eyes?

Sorry for the long post but I know the code will look familiar because I based it on your admin.js/accoun.jade combination.

The files are:

The controller - workspace.js:

'use strict';

/**
 * Module Dependencies
 */

//var config        = require('../config/config');
//var _             = require('underscore');
var validator     = require('validator');        // https://www.npmjs.org/package/validator
var nodemailer    = require('nodemailer');
var User          = require('../models/User');
var config        = require('../config/config');
var passportConf  = require('../config/passport');

/**
 * workspace Controller
 */

module.exports.controller = function (app) {

  /**
   * GET /*
   * *ALL* workspace routes must be authenticated first
   */

  app.all('/workspace*', passportConf.isAuthenticated);

  /**
   * GET /workspace/projects
   * Renders page that shows every project
   */

  app.get(
    '/workspace/projects', function (req, res) {
      res.render(
        '/workspace/projects', {
          url: req.url
        }
      );
    }
  );

  /**
   * GET /workspace/reports
   */

  app.get(
    '/workspace/reports', function (req, res) {
      res.render(
        '/workspace/reports', {
          url: req.url
        }
      );
    }
  );

  /**
   * GET /workspace/friends
   */

  app.get('/workspace/friends', function (req, res) {
    res.render('/workspace/friends', {
      url: '/workspace' // to set navbar active state
      //token: res.locals.token
    });
  });

  /**
   * GET /workspace/projects/:projectId
   * Renders the page showing the project's files, reports and collaborators
   */

  app.get(
    '/workspace/projects/:projectId', function (req, res) {
      //TODO: Finish this whe project page is finished
      res.render(
        '/workspace/reports', {
          url: req.url
        }
      );
    }
  );

  /**
   * GET /workspace/fileEditor/:fileId
   * Renders the fileEditor for a predefined file
   */

  app.get(
    '/workspace/fileEditor/:fileId', function (req, res) {
      //TODO: Finish this when fileEditor page is finished
      res.render(
        '/workspace/reports', {
          url: req.url
        }
      );
    }
  );

  /**
   * GET /workspace/report/:reportId
   * Renders the individual private and public report web page (Authenticated mode)
   */

  app.get(
    '/workspace/reports/:reportId', function (req, res) {
      //TODO: Finish this whe report page is finished
      res.render(
        '/workspace/reports', {
          url: req.url
        }
      );
    }
  );

  /**
   * All Restful JSON API's:
   */

  /**
   * GET /workspace/friends/list
   * JSON friend list API
   */

  app.get(
    '/workspace/friends/list', passportConf.isAuthenticated, function (req, res) {
      var userFriends = [];
      for (var i = 0; i < req.user.friends.length; i++) {
        if (req.user.friends[i].verified) {
          userFriends.push(req.user.friends[i]);
        }
      }
      res.json(userFriends);
    }
  );

  /**
   * GET /workspace/friends/:userId
   * JSON friend list API
   */

  app.get('/workspace/friends/:userId', passportConf.isAuthenticated, function (req, res) {
    User.findOne({ _id: req.param.userId}, function (err, user) {
      res.json(user);
    });
  });

  /**
   * GET /workspace/friends/sentRequests/
   * JSON friends requests list API
   */

  app.get(
    '/workspace/friends/sentRequests/', passportConf.isAuthenticated, function (req, res) {
      var userFriendRequests = [];
      for (var i = 0; i < req.user.friends.length; i++) {
        if (!req.user.friends[i].verified) {
          userFriendRequests.push(req.user.friends[i]);
        }
      }
      res.json(userFriendRequests);
    }
  );

  /**
   * GET /workspace/friends/receivedRequests/
   * JSON friends received requests list API
   */

  app.get(
    '/workspace/friends/receivedRequests/', passportConf.isAuthenticated, function (req, res) {
      //var userReceivedRequests = [];
      User.find({ $and: [{ 'friends.email': req.user.email }, { 'friends.verified': false }] }, function (err, users) {
        res.json(users);
      });
    }
  );

  /**
   * GET /workspace/friends/del/:friend
   * JSON API to delete a friend connection
   */

  app.get(
    '/workspace/friends/del/:friend', passportConf.isAuthenticated, function (req, res) {
      User.find({ $or: [{ _id: req.user._id }, { email: req.params.friend}] }, function (err, users) {
        if (err) {
          res.json(500, { error: 'Couldn\'t find user(s): ' + err });
        }
        for (var i = 0; i < users[0].friends.length; i++) {
          if (req.params.friend == users[0].friends[i].email) {
            // console.log('Found user');
            users[0].friends[i].remove();
          }
        }
        if (users.length === 2) {
          for (var j = 0; j < users[1].friends.length; j++) {
            if (req.user._id == users[1].friends[j]._id) {
              // console.log('Found user');
              users[1].friends[i].verified = false;
            }
          }
        }
        users.save(
          function (err, user, numAffected) {
            if (err) {
              res.json(500, { error: 'Couldn\'t save friend connection changes: ' + err });
            }
            else {
              res.json({ succes: true });
            }
          }
        );
      });
    }
  );

  /**
   * GET /workspace/friends/add/:friend
   * API to add a friend connection and to send the email request.
   */

  app.get(
    '/workspace/friends/add/:friend', passportConf.isAuthenticated, function (req, res) {
      // Define referral Link:
      var refLink = req.protocol + '://' + req.headers.host + '/friends/';

      // Aditional variables:
      var isFriend  = false,
        hasResquest = false,
        sendEmail   = false,
        newFriend, strType;

      //Check if req.params.friend is an email
      if (validator.isNull(req.params.friend) || !validator.isEmail(req.params.friend)) {
        req.flash('Error', { msg: 'Invalid email!' });
        res.redirect('/workspace/friends');
      }

      for (var i = 0; i < req.user.friends.length; i++) {
        if (req.params.friend == req.user.friends[i].email) {
          console.log('Request already sent once or is already a friend.');
          isFriend = true;
          res.json(false);
        }
      }
      if (!isFriend) {
        User.find({ $or: [{ _id: req.user._id }, { email: req.params.friend }]}, function (err, users) {
          if (users.length === 2) {
            //Other user exists
            for (var i = 0; i < users[1].friends.length; i++) {
              if (users[1].friends[i].email == users[0].email) {
                console.log('This user has sent request already, so we confirmed the connection.');

                //the hasRequest variable indicates that it's a request confirmation.
                hasResquest = true;

                //updating users info at friend:
                users[1].friends[i].verified = true;
                users[1].friends[i]._id = users[0]._id;
                users[1].friends[i].name = users[0].name;
                users[1].friends[i].gender = users[0].gender;

                //Updating info in the user itself:
                newFriend = {
                  friendId: users[1]._id,
                  email: req.params.friend,
                  verified: true,
                  name: users[1].profile.name,
                  gender: users[1].profile.gender
                };

                users[0].friends.push(newFriend);

                sendEmail = true;
                strType = 'requestConfirmation';
              }
            }

            //As no request exist it's necessary to create one and send email
            if (!hasResquest) {
              newFriend = {
                friendId: users[1]._id,
                email: req.params.friend,
                verified: false,
                name: users[1].profile.name,
                gender: users[1].profile.gender
              };
              users[0].friends.push(newFriend);

              sendEmail = true;
              strType = 'friendRequest';
            }
          }
          else {
            //Other user doesn't exist, send invitation
            newFriend = { email: req.params.friend };
            users[0].friends.push(newFriend);

            sendEmail = true;
            strType = 'invitation';
          }

          if (sendEmail) {
            // Define variables:
            var fName, phraseString, phrase2String;

            console.log(newFriend);

            if (validator.isEmail(newFriend.email)) {
              //Send Email:
              // Create a reusable nodemailer transport method (opens a pool of SMTP connections)
              var smtpTransport = nodemailer.createTransport(
                'SMTP', {
                  service: 'Mandrill',
                  auth: {
                    user: config.mandrill.user,
                    pass: config.mandrill.password
                  }
                }
              );

              // General declarations:
              phraseString = users[0].profile.name + ' invited you to join him at ';
              phrase2String = 'To accept this connection';
              fName = newFriend.name;

              // Type specific declarations:
              if (strType === 'requestConfirmation') {
                phraseString = users[0].profile.name + ' accepted your invitation to join your connections at ';
                phrase2String = 'To see your connection';
              }
              /* else if (strType === 'friendRequest') {} */
              else if (strType === 'invitation') {
                fName = newFriend.email;
              }

              // Render HTML to send using .jade mail template (just like rendering a page)
              res.render(
                'mail/invitation', {
                  phrase1: phraseString,
                  phrase2: phrase2String,
                  link: refLink,
                  friendName: fName,
                  name: users[0].profile.name,
                  mailtoName: config.smtp.name,
                  mailtoAddress: config.smtp.address
                }, function (err, html) {
                  if (err) {
                    return (err, null);
                  }
                  else {
                    // Now create email text (multiline string as array FTW)
                    var text = [
                      'Hello ' + fName + '!',
                      phraseString + config.smtp.name + '.',
                      phrase2String + 'click here: ' + refLink,
                      'If you have any questions, or suggestions, feel free to email us at ' + config.smtp.address + '.',
                      'Thank you for your time!',
                      '  - The ' + config.smtp.name + ' team'
                    ].join('\n\n');

                    // Create email
                    var mailOptions = {
                      to: fName + ' <' + newFriend.email + '>',
                      from: users[0].profile.name + ' <' + users[0].email + '>',
                      subject: app.locals.application + ' invitation',
                      text: text,
                      html: html
                    };

                    // Send email
                    smtpTransport.sendMail(
                      mailOptions, function (err) {
                        if (err) {
                          req.flash('error', { msg: err.message });
                        }
                        // shut down the connection pool, no more messages
                        smtpTransport.close();
                      }
                    );
                  }
                }
              );
            }
            else {
              console.error('Email is incorrect...');
            }
          }

          //Save changes to the users:
          users[0].save(function (err, user, numAffected) {
            if (err) {
              req.flash('error', { msg: err.message });
              res.json(500, { error: 'Couldn\'t save friend connection changes: ' + err });
            }
            else {
              // Send user on their merry way
              // console.log(user);
              //console.log(numAffected);
              req.flash('success', { msg: 'The friend request has been sent successfully!' });
              res.json(200, 'Update complete');
            }
          });

          if (users.length === 2) {
            users[1].save(function (err, users, numAffected) {
              if (err) {
                res.json(500, { error: 'Couldn\'t save friend connection changes: ' + err });
              }
              else {
                // Send user on their merry way
                res.json(200, 'Update complete');
              }
            });
          }
        });
      }
    }
  );
};

//TODO: API's for Projects and Reports.

And the .jade file - friends.jade:

extends ../layouts/layout
//- http://cwbuecheler.com/web/tutorials/2014/restful-web-app-node-express-mongodb/

block head
  title #{application} &middot; Friends
  link(rel='stylesheet', href='/lib/filament-dialog/src/dialog.css')
  link(rel='stylesheet', href='/lib/tablesaw-script/tablesaw.css')

  script(src='/lib/jquery/dist/jquery.js')
  script(src='/lib/filament-dialog/src/dialog.js')
  script(src='/lib/filament-dialog/src/dialog-init.js')
  script(src='/lib/tablesaw-script/tablesaw.js')

  script.
    //- grunticon Stylesheet Loader | https://github.com/filamentgroup/grunticon | (c) 2012 Scott Jehl, Filament Group, Inc. | MIT license.
    window.grunticon = function(e) {
      if (e && 3 === e.length) {
        var t = window,
        n =! (!t.document.createElementNS || !t.document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect || !document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1") || window.opera && -1 === navigator.userAgent.indexOf("Chrome")),
        o = function(o) {
          var r = t.document.createElement("link"),
          a = t.document.getElementsByTagName("script")[0];
          r.rel = "stylesheet",
          r.href = e[o&&n?0:o?1:2],
          a.parentNode.insertBefore(r,a)
        },
        r = new t.Image;
        r.onerror = function() {
          o(!1)
        },
        r.onload = function() {
          o(1 === r.width && 1 === r.height)
        },
        r.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="
      }
    };
    grunticon(["/lib/tablesaw-script/icons/icons.data.svg.css", "/lib/tablesaw-script/icons/icons.data.png.css", "/lib/tablesaw-script/icons/icons.fallback.css"]);

  //- noscript tag hasn't been added to Jade yet...
   noscript
     link(href='/lib/tablesaw-script/icons/icons.fallback.css', rel='stylesheet')

block content
  .container
    .row
      .col-md-4.col-md-push-8
        .page-header
          h2
            i.fa.fa-user
            | &nbsp;Friend Details
        //- USER INFO
        #userInfo.well
          p
            img(id='userPicture', src='', width='100px')
          p
            strong Name:
            |  <span id='userInfoName'></span>
          p.hidden-xs
            strong Email:
            | &nbsp;
            a(href='', id='userInfoEmail')
          p.hidden-xs
            strong Location:
            |  <span id='userInfoLocation'></span>
          p.hidden-xs
            strong Gender:
            |  <span id='userInfoGender'></span>
          p.hidden-xs
            strong Website:
            | &nbsp;
            a(href='', id='userInfoWebsite')
          p
            strong Date Established:
            |  <span id='userInfoEstablished'></span>
          p
            strong Last Logon:
            |  <span id='userInfoLogon'></span>
          p.hidden-xs
            strong Last Update:
            |  <span id='userInfoUpdate'></span>
      //- /USER INFO
      .col-md-8.col-md-pull-4
        .page-header
          h2
            i.fa.fa-users
            | &nbsp;#{application} Friends
        //- Wrapper
        #wrapper

          //- USER LIST
          #userList
            table.tablesaw(data-mode='swipe', data-sortable='data-sortable', data-sortable-switch='data-sortable-switch', data-minimap='data-minimap', data-mode-switch='data-mode-switch')
                thead
                  th(data-priority='persist', data-sortable-col='data-sortable-col')
                    abbr(title='Name') Name
                  th(data-priority='1', data-sortable-col='data-sortable-col')
                    abbr(title='Email') Email
                  th(data-priority='4', data-sortable-col='data-sortable-col', data-sortable-default-col='data-sortable-default-col')
                    abbr(title='User Since') User Since
                  th(data-priority='3', data-sortable-col='data-sortable-col')
                    abbr(title='Gender') Gender
                  th(data-priority='2') Remove?
                tbody
          //- /USER LIST

        //- /WRAPPER

  #myModal.modal.fade(tabindex='-1', role='dialog', aria-labelledby='myModalLabel', aria-hidden='true')
      .modal-dialog.modal-sm
        .modal-content
          .modal-header
            button.close(type='button', data-dismiss='modal', aria-hidden='true') ×
            h4#myModalLabel.modal-title FriendShip Removal confirmation
          .modal-body
            h4 Are you sure?
          .modal-footer
            button.btn.btn-default(type='button', data-dismiss='modal') No
            button#yes.btn.btn-primary(type='button') Yes!

  //- script(src='lib/moment/min/moment.min.js')
  //- script(src='lib/bootstrap/js/modal.js')
block scripts
  script.
    // Userlist data array for filling in info box
    var userListData = [];

    // DOM Ready =============================================================
    $(document).ready(function() {

      // Load Bootstrap Modal
      $.getScript('lib/bootstrap/js/modal.js');

      // Load moment.js
      $.getScript('lib/moment/min/moment.min.js')
      .done(function(script, textStatus) {
        // Now populate the user table
        populateTable();
      });

      // Friend's Username link click
      $('#userList table tbody').on('click', 'td a.linkshowuser', showUserInfo);

      // Delete friend connection link click
      $('#userList table tbody').on('click', 'td a.linkdeleteuser', deleteUser);

    });

    // Functions =============================================================

    // Fill table with data
    function populateTable() {

      // Empty content string
      var tableContent = '';

      // jQuery AJAX call for JSON
      $.getJSON( '/workspace/friends/list', function( data ) {
        // For each item in our JSON, add a table row and cells to the content string
        $.each(data, function(){
          //Loading each user data into global variable.
          $.getJSON( '/workspace/friends/' + this.friendId, function( user ) {
            userListData.push(user);
          });

          tableContent += '<tr>';
          tableContent += '<td><a href="#" class="linkshowuser btn btn-info btn-xs"" rel="' + this.friendId + '" title="Show Details">' + this.name + '</td>';
          tableContent += '<td><a href="mailto:' + this.email + '">' + this.email + '</a></td>';
          tableContent += '<td>' + moment(useListData[-1].activity.date_established).format('MMMM Do YYYY') + '</td>';
          tableContent += '<td>' + this.gender + '</td>';
          tableContent += '<td><a href="#" class="linkdeleteuser btn btn-danger btn-xs" rel="' + this.email + '">Danger!</a></td>';
        });

        //console.log(userListData);

        // Inject the whole content string into our existing HTML table
        $('#userList table tbody').html(tableContent);
      });

    };

    // Show User Info
    function showUserInfo(event) {

      // Prevent Link from Firing
      event.preventDefault();

      // Retrieve username from link rel attribute
      var thisUserName = $(this).attr('rel');

      // Get Index of object based on id value
      var arrayPosition = userListData.map(function(arrayItem) { return arrayItem._id; }).indexOf(thisUserName);

      // Get our User Object
      var thisUserObject = userListData[arrayPosition];

      //Populate Info Box  .attr("src", src)
      $('#userPicture').attr("src", (thisUserObject.profile.picture));
      $('#userInfoName').text(thisUserObject.profile.name);
      $('#userInfoEmail').attr('href', 'mailto:' + thisUserObject.email).text(thisUserObject.email);
      $('#userInfoLocation').text(thisUserObject.profile.location);
      $('#userInfoGender').text(thisUserObject.profile.gender);
      if (thisUserObject.profile.website) {
        $('#userInfoWebsite').attr('href', thisUserObject.profile.website).text('Website Link');
      }
      $('#userInfoEstablished').text(moment(thisUserObject.activity.date_established).format('MMMM Do YYYY'));
      $('#userInfoLogon').text(moment(thisUserObject.activity.last_logon).fromNow());
      $('#userInfoUpdate').text(moment(thisUserObject.activity.last_update).fromNow());
    };

    // Delete User
    function deleteUser(event) {

      // Prevent Link from Firing
      event.preventDefault();

      // Pop up a confirmation dialog
      // Convoluted because we have to
      // pass the user's id through
      function showmodal(userid) {
        // show modal
        $('#myModal').modal({keyboard:true,backdrop:'static'});
        // if "yes" clicked
        $('#yes').on('click', function () {
          // do our delete
          $.getJSON( '/workspace/friends/del/' + userid, function( response ) {
            if (response.succes) {
            }
            else {
              alert('Error: ' + response.msg);
            }
            // Update the table
            populateTable();
          });
          // close the modal
          $('#myModal').modal('hide');
        });
      }

      // call modal function with the user's account id
      showmodal($(this).attr('rel'));

    };

Many Thanks!

Carlos

cmpsoares91 commented 10 years ago

BTW: is "validateLineBreaks" in .jscsrc important for this matter because I commented it because I work on both linux and windows when developing.

dstroot commented 10 years ago

This is where you code is failing so it is looking for /home/oneprovider/QuantBull/views/workspace/friends.jade. Does that template exist?

image

cmpsoares91 commented 10 years ago

Well I've Created a .jade file there with that name, is anything else necessary?

cmpsoares91 commented 10 years ago

It was the /, I had to write res.render('workspace/frriends, function ... instead.

Thank you!

cmpsoares91 commented 9 years ago

Hi,

I'd like to add you to my professional network on LinkedIn.

Accept: http://www.linkedin.com/blink?simpleRedirect=3oNdzANejAVdjoUejoTdPoPejkZh4BKrSBQonhFtCVF9B5muktSuD9DfnBBiShBsC5EsOpQsSlRpRZBt6BSrCAZqSkConhzbmlQqnpKqiRQsSlRpORIrmkZpSVFqSdxsDgCpnhFtCV9pSlipn9Mfm4CojoJqD1zdT8UcCAJqC4UpDtEbjRBfP9SbSkLrmZzbCVFp6lHrCBIbDtTtOYLeDdMt7hE&msgID=I8308246908_1&markAsRead=

You received an invitation to connect. LinkedIn will use your email address to make suggestions to our members in features like People You May Know. Unsubscribe here: http://www.linkedin.com/blink?simpleRedirect=64SbmFMoPtOe39FbmFxe6pTq2QZp6BB9ABjl55zlnh9iCFnj6hDk3B9lmFfcQRii4JikSVakzp7i35ciQt2jmhIrBlijnsPu3heuAB7dmx1qBFdkz9VblARoB5fcANVkP14pllpcDFmp7BVsCJGtP5HdAh4iR5Fl7Bpp3pHqQR1h4N7ej9Qq4Fps71vsRlKrAtErR1hql9qcDtUczlirjpQdksVgRBOjSRlgk51pTcVijlbgn1RiCt5kk4Zp6BLr2pQsSlRpRZBt6BSrCAZqSkCkjoPp4l7q5p6sCR6kk4ZrClHrRhAqmQCrDlIfngCojoJqD1zdT8UcCAJqC4UpDtEbjRBfP9SbSkLrmZzbCVFp6lHrCBIbDtTtOYLeDdMt7hE&amp;msgID=I8308246908_1&amp;markAsRead= Learn why we included this at the following link: http://www.linkedin.com/blink?simpleRedirect=e3wTd3RAimlIoSBQsC4Ct7dBtmtvpnhFtCVFfmJB9CNOlmlzqnpOpldOpmRLt7dRoPRx9C4SbmFMoPtOe39FbmFxe6pTq2QZpjYOtyZBbSRLoOVKqmhBqSVFr2VTtTsLbPFMt7hE&msgID=I8308246908_1&markAsRead= © 2014, LinkedIn Corporation. 2029 Stierlin Ct. Mountain View, CA 94043, USA

cmpsoares91 commented 9 years ago

Carlos Soares would like to connect on LinkedIn. How would you like to respond?

Accept: http://www.linkedin.com/blink?simpleRedirect=3oNdzANejAVdjoUejoTdPoPejkZh4BKrSBQonhFtCVF9B5muktSuD9DfnBBiShBsC5EsOoVclZMu6lvtCVFfmJB9D9Bp6VFrmlObnhMpmdzoiRybmtSrCBvrmRLoORIrmkZpSVFqSdxsDgCpnhFtCV9pSlipn9Mfm4Cuj8JtDBNr31AcCAJqC4UpDtEbjRBfP9SbSkLrmZzbCVFp6lHrCBIbDtTtOYLeDdMt7hE&msgID=I8308246908_1&markAsRead=

View Carlos Soares's profile: http://www.linkedin.com/blink?simpleRedirect=ej5vs7xBnTpKqjRHpipOpmhKqmRBsyRQs6lzoS4JoyRDtCVFnSRJrScJr6RBfmtKqmJzon9Q9DpMrzRQ9DAObnpVsmMMp39FbmFxe6pTq2QZpjYOtyZBbSRLoOVKqmhBqSVFr2VTtTsLbPFMt7hE&msgID=I8308246908_1&markAsRead=

You are receiving Reminder emails for pending invitations. Unsubscribe here: http://www.linkedin.com/blink?simpleRedirect=rmZzhj8BoDlEt6BDhj8BumNMpn8Md2kNcj8QcjAQbjcNoCcPom9yejsQcjcPdPgSomhBemlzpCoNojcVdzAPcmgUd6oTcm8JcP4TcP8Oe3cJqk8O9nBIs6lOfmNFomRB9z0Sc30OfmhF9zoNdzANejAVdjoUejoTdPoPejkZp6BD9zANnT1UplZSrCAZqSkCoDlPrDkJpyRzoClJnSRJrScJr6RBfmtKqmJzon9Q9CZLpPRQ9DAObnpVsmMMp39FbmFxe6pTq2QZpjYOtyZBbSRLoOVKqmhBqSVFr2VTtTsLbPFMt7hE&msgID=I8308246908_1&markAsRead=

You received an invitation to connect. LinkedIn will use your email address to make suggestions to our members in features like People You May Know. Unsubscribe here: http://www.linkedin.com/blink?simpleRedirect=uj8JtDBNr31AcCAJqC4UpDtEbjRAqmkCqThjgPpcjlYOtkRbh75vm79LlSkJinpWrCpalmEUrPAOgldFtlwPoQNTc4sQkjlkumQPgAlCjAxWqAdPlAVxkAl7p5x5jCZMhQ58lA5ac4thgTxJlSROtmRmrDlpqm9CtjsQrORboARUi4V9qQ8Vuz9xtAVJcQMOlkpOplt1p691sBl4i6JzcPdNlSRykAdBrll1gk51kBt1hkARcQARq4phgjRAqmZI9zANnT1UplZSrCAZqSkCkjoPp4l7q5p6sCR6kk4ZrClHrRhAqmQCrDlIfngCuj8JtDBNr31AcCAJqC4UpDtEbjRBfP9SbSkLrmZzbCVFp6lHrCBIbDtTtOYLeDdMt7hE&amp;msgID=I8308246908_1&amp;markAsRead= Learn why we included this at the following link: http://www.linkedin.com/blink?simpleRedirect=0Ue3sQfmh9pmNzqnhOoioVclZMu6lvtCVFfmJB9CNOlmlzqnpOpldOpmRLt7dRoPRx9DAObnpVsmMMp39FbmFxe6pTq2QZpjYOtyZBbSRLoOVKqmhBqSVFr2VTtTsLbPFMt7hE&msgID=I8308246908_1&markAsRead= © 2014, LinkedIn Corporation. 2029 Stierlin Ct. Mountain View, CA 94043, USA

cmpsoares91 commented 9 years ago

Carlos Soares would like to connect on LinkedIn. How would you like to respond?

Accept: http://www.linkedin.com/blink?simpleRedirect=3oNdzANejAVdjoUejoTdPoPejkZh4BKrSBQonhFtCVF9B5muktSuD9DfnBBiShBsC5EsOoVclZMu6lvtCVFfmJB9D9Bp6VFrmlObnhMpmdzoiRybmtSrCBvrmRLoORIrmkZpSVFqSdxsDgCpnhFtCV9pSlipn9Mfm4CpP4Jq6kPpn1IcCAJqC4UpDtEbjRBfP9SbSkLrmZzbCVFp6lHrCBIbDtTtOYLeDdMt7hE&msgID=I8308246908_1&markAsRead=

View Carlos Soares's profile: http://www.linkedin.com/blink?simpleRedirect=ej5vs7xBnTpKqjRHpipOpmhKqmRBsyRQs6lzoS4JoyRDtCVFnSRJrScJr6RBfmtKqmJzon9Q9DpMrzRQ9CsNbmxBcSlMr39FbmFxe6pTq2QZpjYOtyZBbSRLoOVKqmhBqSVFr2VTtTsLbPFMt7hE&msgID=I8308246908_1&markAsRead=

You are receiving Reminder emails for pending invitations. Unsubscribe here: http://www.linkedin.com/blink?simpleRedirect=rmZzhj8BoDlEt6BDhj8BumNMpn8Md2kNcj8QcjAQbjcNoCcPom9yejsQcjcPdPgSomhBemlzpCoNojcVdzAPcmgUd6oTcm8JcP4TcP8Oe3cJqk8O9nBIs6lOfmNFomRB9z0Sc30OfmhF9zoNdzANejAVdjoUejoTdPoPejkZp6BD9zANnT1UplZSrCAZqSkCoDlPrDkJpyRzoClJnSRJrScJr6RBfmtKqmJzon9Q9CZLpPRQ9CsNbmxBcSlMr39FbmFxe6pTq2QZpjYOtyZBbSRLoOVKqmhBqSVFr2VTtTsLbPFMt7hE&msgID=I8308246908_1&markAsRead=

You received an invitation to connect. LinkedIn will use your email address to make suggestions to our members in features like People You May Know. Unsubscribe here: http://www.linkedin.com/blink?simpleRedirect=pP4Jq6kPpn1IcCAJqC4UpDtEbjRAqmkCe6JAqAhnkldThQRShStRrPtHkRZ7rP95bnpjcjBggjpld7xRcRZDgSRDtzt8d6FAgPlUe4t1k4FKu6VculFIl5YRlkZvnP4OhAB2clFfgz9AjP5fsP91iCBkkB9VpkdcgkZ2i6VCpOQVnSFDu4lOqj1xej8Ri2RgrRFUoRZpkQp4jBlCi4FMuDhDdkNhk5sVrll1gk51tCJWtC8Rr4pOmkxhgjRAqmZI9zANnT1UplZSrCAZqSkCkjoPp4l7q5p6sCR6kk4ZrClHrRhAqmQCrDlIfngCpP4Jq6kPpn1IcCAJqC4UpDtEbjRBfP9SbSkLrmZzbCVFp6lHrCBIbDtTtOYLeDdMt7hE&amp;msgID=I8308246908_1&amp;markAsRead= Learn why we included this at the following link: http://www.linkedin.com/blink?simpleRedirect=0Ue3sQfmh9pmNzqnhOoioVclZMu6lvtCVFfmJB9CNOlmlzqnpOpldOpmRLt7dRoPRx9CsNbmxBcSlMr39FbmFxe6pTq2QZpjYOtyZBbSRLoOVKqmhBqSVFr2VTtTsLbPFMt7hE&msgID=I8308246908_1&markAsRead= © 2014, LinkedIn Corporation. 2029 Stierlin Ct. Mountain View, CA 94043, USA