Создание вашего автозагрузки: Пригласить пользователей по URL-адресу

11 января 2018

Этот учебник является частью построения вашего запуска с серией PHP на Envato Tuts +. В этой серии я проведу вас, запуская запуск из концепции в реальность, используя приложение «Планировщик встреч» в качестве примера в реальной жизни. Каждый шаг по пути, я выпущу код планировщика собраний как примеры с открытым исходным кодом, из которых вы можете узнать. Я также рассмотрю связанные с запуском бизнес-вопросы по мере их возникновения.

Расписание собрания вашей группы с помощью ярлыка URL

Добро пожаловать! Недавно я вернулся из своего любимого места в мире, о котором я упомянул в конце последнего эпизода. После завершения нескольких встреч участников, я сделал перерыв в природе.

Сегодня я добавлю возможность приглашать участников собрания, поделившись безопасным URL-адресом, связанным с вашей встречей. Это будет особенно полезно для планирования групповых встреч. Например, если вы хотите пригласить 30 человек, иногда бывает проще отправить электронное письмо всем с URL-адресом приглашения.

Если вы еще не хотите, пожалуйста, постарайтесь запланировать встречу своей группы сегодня! Пригласите нескольких друзей встретиться с вами для kombucha, kava или кофе. Поделитесь своими мыслями и отзывами о каждом опыте в комментариях ниже. Я участвую в обсуждениях, но вы также можете связаться со мной @reifman в Twitter. Я всегда открываю новые идеи для планировщика собраний, а также предложения для будущих серийных эпизодов.

Напоминаем, что весь код Планировщика собраний предоставляется с открытым исходным кодом и написан в Yii2 Framework для PHP. Если вы хотите узнать больше о Yii2, ознакомьтесь со своей параллельной серией Программирование с Yii2.

Прежде чем я погружусь в эту функцию, я хочу привести некоторые примеры некоторых ошибок (или пропусков) в день, которые я обнаружил при создании службы. (Если вы просто хотите прочитать о безопасных совместно используемых URL-адресах, не стесняйтесь пропустить этот раздел.)

Интерполяция ошибок при запуске

Поскольку все больше и больше людей начинают пытаться планировать встречу, появляются ошибки. И, часто во время разработки, Я замечаю их сам. Вот несколько последних, просто чтобы дать вам представление о стартовой жизни.

Трудно сосредоточиться на развитии бизнеса и маркетинге, кодировании новых функций и определении и исправлении ошибок. Запуск одного человека растет с трудом по мере роста возможностей вашего сайта.

Как я уже писал ранее, в настоящее время я использую Asana для планирования функций, но также для отслеживания ошибок.

Ошибка присваивания

Я уверен, что если бы у меня было больше опыта работы в качестве разработчика, работы с коллегами или у меня было больше времени, чтобы не кодировать Планировщик собраний, я точно знаю, какие расширения для Atom Editor охотятся за ними. Если вы знаете, разместите его в комментариях.

По-видимому, в важной функции, чтобы проверить, действительно ли зритель был участником собрания, я использовал назначение для проверки. Другими словами, я не спрашивал, является ли владелец зрителем - я временно делал это.

public static function isAttendee($meeting_id,$user_id) {
  $m = Meeting::findOne($meeting_id);
  // are they the organizer?
  // EEEK!
  if ($m->owner_id = $user_id) {
    return true;
  }

Вы помните, что два равенства - это сравнение, одно равное - назначение. Точно так же, как и периоды для конкатенаций, и плюс знаки для добавления, за исключением JavaScript, где им бесконечно сложно найти ошибки (что также является причиной того, что Ajax - ад в PHP).

Запросы базы данных, которые не работают со временем

По мере того как мое собственное использование Планировщика собраний увеличилось, в моих вкладках было все больше и больше встреч. И затем я заметил, что иногда появятся дубликаты. Это было трудно обнаружить раньше, когда было меньше данных.

Мои запросы на конкретную встречу (например, планирование собрания, подтвержденные собрания, прошлые собрания и т. Д.) Не изолировали уникальные записи:

$plaingProvider = new ActiveDataProvider([
      'query' => Meeting::find()->joinWith('participants')
      ->where(['owner_id'=>Yii::$app->user->getId()])
      ->orWhere(['participant_id'=>Yii::$app->user->getId()])
      ->andWhere(['meeting.status'=>[Meeting::STATUS_PLANNING,Meeting::STATUS_SENT]])
      /* NEEDED TO ADD THIS */
      ->distinct(),
      'sort'=> ['defaultOrder' => ['created_at'=>SORT_DESC]],
      'pagination' => [
          'pageSize' => 7,
          'params' => array_merge($_GET, ['tab' => 'plaing']),
        ],
  ]);

Добавив -> distinct () в запрос, исправил его.

Yii2 разбиение на страницы на грид-представления на вкладке

Еще одна ошибка, с которой я столкнулся с большим количеством данных, заключалась в том, что ссылки на разбиение на страницы Yii2 всегда возвращали меня на первую вкладку.

Я добавил параметр запроса для текущей вкладки, которую теперь ищет MeetingController.php actionIndex:

public function actionIndex()
    {
      if (Meeting::countUserMeetings(Yii::$app->user->getId())==0) {
        $this->redirect(['create']);
      }
      $tab ='plaing';
      if (isset(Yii::$app->request->queryParams['tab'])) {
        $tab =Yii::$app->request->queryParams['tab'];
      }
      $plaingProvider = new ActiveDataProvider([
            'query' => Meeting::find()->joinWith('participants')->where(['owner_id'=>Yii::$app->user->getId()])->orWhere(['participant_id'=>Yii::$app->user->getId()])->andWhere(['meeting.status'=>[Meeting::STATUS_PLANNING,Meeting::STATUS_SENT]])->distinct(),
            'sort'=> ['defaultOrder' => ['created_at'=>SORT_DESC]],
            'pagination' => [
                'pageSize' => 7,
                'params' => array_merge($_GET, ['tab' => 'plaing']),
              ],
        ]);

Кроме того, я проинструктировал параметры страницы для слияния текущей настройки табуляции. Когда пользователи нажимают на другую ссылку на страницу, текущая вкладка теперь включена.

Наконец, я также сделал представление frontend, на котором встречается index.php об этом, установив активную вкладку из параметра запроса:

<!-- Tab panes -->
<div class="tab-content">
  <div class="tab-pane <?= ($tab=='plaing'?'active':'') ?>" id="plaing">
    <div class="meeting-index">
      <?php
      ?>
      <?= $this->render('_grid', [
          'mode'=>'plaing',
          'dataProvider' => $plaingProvider,
          'timezone'=>$timezone,
      ]) ?>
    </div> <!-- end of plaing meetings tab -->
  </div>
  <div class="tab-pane <?= ($tab=='upcoming'?'active':'') ?>" id="upcoming">
    <div class="meeting-index">
      <?= $this->render('_grid', [
          'mode'=>'upcoming',
          'dataProvider' => $upcomingProvider,
          'timezone'=>$timezone,
      ]) ?>
      </div> <!-- end of upcoming meetings tab -->
  </div>
  <div class="tab-pane <?= ($tab=='past'?'active':'') ?>" id="past">
    <?= $this->render('_grid', [
        'mode'=>'past',
        'dataProvider' => $pastProvider,
        'timezone'=>$timezone,
    ]) ?>
  </div> <!-- end of past meetings tab -->

Это всего лишь несколько хороших примеров повседневных ошибок, с которыми я сталкиваюсь в создании пусковой площадки Планировщика собраний.

Теперь давайте погрузиться в создание приглашений с помощью ярлыков, как было обещано.

Создание безопасных общих URL-адресов ярлыков

Думая о безопасности для URL-адресов

Чтобы затруднить случайный взлом URL-адресов, чтобы пробиться в приглашение на собрание кого-то, мне нужно было иметь довольно уникальный ключ в сочетании с неопознанным код.

Я решил использовать имя пользователя в качестве ключа. У каждого пользователя будет большое количество почти неописуемых кодов встреч.

Так, например, URL встречи может быть https: meetingplaer.io presidenthillary X1Y2Z3A7C9.

Для кода я решил использовать восемь буквенно-цифровых символов, чувствительных к регистру. Другими словами, каждый символ будет a-z, A-Z или 0-9, по существу, 62 возможности для каждого символа.

Общее количество возможностей для каждого пользователя составляет 218 340,105,584,896, что составляет более 218 трлн. О, и вам нужно знать имя своей цели, чтобы начать! Было бы намного проще взломать учетную запись участника.

Добавление кода безопасности для каждого собрания

Чтобы добавить код безопасности ко всем существующим встречам, я создал миграцию, m160902_174350_extend_meeting_for_identifier.php:

class m160902_174350_extend_meeting_for_identifier extends Migratio
{
  public function up()
  {
    $tableOptions = null;
    if ($this->db->driverName === 'mysql') {
        $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=IoDB';
    }
    $this->addColumn('{{%meeting}}','identifier',Schema::TYPE_STRING.' NOT NULL');
    $all = Meeting::find()
      ->where(['identifier'=>''])
      ->all();
    foreach ($all as $m) {
      $m->identifier = Yii::$app->security->generateRandomString(8);
      $m->update();
    }
  }

Вы заметите, что в этой миграции я действительно использую код для создания случайных строк для каждой существующей встречи, т. Е. Yii:: $ app-> security-> generateRandomString (8);.

Не часто бывает, что я буду писать код в процессе миграции для обновления существующих областей базы данных. В этом случае он работает плавно. В других случаях я использовал модель Fix.php для моделей frontend.

Кроме того, в Meeting:: beforeSave () я добавил автоматический код для генерации идентификатора для всех будущих встреч:

public function beforeSave($insert)
      {
          if (parent::beforeSave($insert)) {
            if ($insert) {
              $this->identifier = Yii::$app->security->generateRandomString(8);
            }
          }
          return true;
      }

Расширение Yii Routing

Хотя было бы проще включить префикс контроллера, такой как идентификационный код пользователя, я хотел бы, чтобы ссылки были простыми, без дополнительного префикса. Это потребовало расширения маршрута Yii.

Если я сохраню это в своей собственной модели для префикса и имени пользователя, я мог бы использовать то, что я написал в YII2 Sluggable Behaviors и Building Your Startup: Geolocation и Google Places.

Вместо этого я добавил '' => 'meeting identity ', который сопоставляет любое имя пользователя с идентификационной строкой с методом MeetingController actionIdentity ().

'urlManager' => [
            'class' => 'yiiwebUrlManager',
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            //'enableStrictParsing' => false,
            'rules' => [
              'place' => 'place',
              'place/yours' => 'place/yours',
              'place/create' => 'place/create',
              'place/create_geo' => 'place/create_geo',
              'place/create_place_google' => 'place/create_place_google',
              'place/view/<id:d+>' => 'place/view',
              'place/update/<id:d+>' => 'place/update',
              'place/<slug>' => 'place/slug',
              '<controller:w+>/<id:d+>' => '<controller>/view',
              '<controller:w+>/<action:w+>/<id:d+>' => '<controller>/<action>',
              'daemon/<action>' => 'daemon/<action>', // incl eight char actio
              'site/<action>' => 'site/<action>', // incl eight char actio
              'features' => 'site/features',
              'about' => 'site/about',
              '<username>/<identity:[A-Za-z0-9_-]{8}>' => 'meeting/identity',
              // note - currently actions with 8 letters and no params will fail
              '<controller:w+>/<action:w+>' => '<controller>/<action>',
            ],
        ],

С этим я столкнулся с несколькими проблемами. Мне пришлось переупорядочить правила и поставить статические маршруты для любых восьмисимвольных действий, которые могли бы выглядеть как имя пользователя (вместо контроллера) и метод (вместо ключа идентификации).

Например, функции https: meetingplaer.io, сопоставленные с сайтом с именем пользователя, имеющим безопасный идентификатор встречи 'features ', вместо таблицы новых функций Meeting Plaer.

Но, как только я ориентировался на проблемы, все работало нормально.

Метод идентификации контроллера собрания

Затем я создал actionIdentity () в MeetingController:

public function actionIdentity()
  {
    // fetch path
    list($username,$identifier) = explode("/",Yii::$app->request->getPathInfo());
    // verify the meeting identifier
    $m = Meeting::find()
      ->where(['identifier'=>$identifier])
      ->one();
    if (is_null($m) || ($m->owner->username != $username)) {
      // access failure
      return $this->redirect(['site/authfailure']);
    }
    // identifier is authentic
    if (Yii::$app->user->isGuest) {
      // redir to Participant join form
      return $this->redirect(['/participant/join','meeting_id'=>$m->id,'identifier'=>$identifier]);
    } else {
      $user_id = Yii::$app->user->getId();
      if (!Meeting::isAttendee($m->id,$user_id)) {
          // if not an attendee -- add them as a participant
          Participant::add($m->id,$user_id,$m->owner_id);
      }
      return $this->actionView($m->id);
    }

Во-первых, он проверяет, что имя пользователя и идентификатор соответствуют существующему пользователю и существующей встрече. Если нет, мы отправляем их на authfailure.

Если пользователь уже вошел в систему, мы автоматически добавляем их в качестве участника на эту страницу и перенаправляем их на страницу просмотра собрания.

Если это не так, мы отправляем их в контроллер участника. Присоединиться к адресу для входа в систему или регистрации.

Участник, желающий присоединиться к собранию

Например, предположим, что я получаю следующее приглашение от друга по электронной почте:

https: meetingplaer.io tomeMcFarline JzRq1a42. Мне будет показана эта страница:

Если пользователь хочет зарегистрироваться в Планировщике встреч через социальную сеть, мы сможем подтвердить их адрес электронной почты.

Итак, я установил URL-адрес Yii (страница, на которую пользователь перенаправляется после успешной регистрации или входа в систему), который вернет их на страницу просмотра собрания после аутентификации.

// set return Url
Yii::$app->user->setReturnUrl($m->getSharingUrl());

По большей части, социальная аутентификация, логин и / или регистрация управляются кодом, описанным в разделе «Создание вашего автозагрузки: упрощение Onramp с помощью OAuth».

Если участник является новым, он будет предоставлять свое имя и фамилию и адрес электронной почты. Мы добавим их на собрание как непроверенный участник, аналогичный тому, что мы делаем, когда пользователь приглашает кого-то, добавив новый адрес электронной почты на собрание.

$model = new Participant;
$model->meeting_id = $meeting_id;
...
$model->invited_by = $m->owner_id;
$model->status = Participant::STATUS_DEFAULT;
if (!$validationError && $model->validate()) {
  $model->participant_id = User::addUserFromEmail($model->email);
  $model->save();
  // look up email to see if they exist
  Meeting::displayNotificationHint($meeting_id);
  $user = User::findOne($model->participant_id);
  Yii::$app->user->login($user);
  return $this->redirect(['/meeting/view', 'id' => $meeting_id]);
}

Вы сейчас задаетесь вопросом, эй, Джефф, что с... сегодня? Это просто, не так ли? Мы просто добавляем нового пользователя на встречу.

При кодировании этого я понял, что создаю огромную дыру в конфиденциальности.

Пример забавной защитной дыры

Пусть, как утверждают, у Тома МакФарлина нечего делать в один прекрасный день и решает возиться со мной (и с Богом). Он создаст новую встречу и, зная, что Далай-лама является обычным пользователем Планировщика собраний (из-за всех его духовных встреч), Макфарлин добавит его на свою встречу, используя электронную почту dalailama@gmail.com.

Затем он схватит свой причудливый безопасный ярлык. За мной?

Затем Макфарлин откроет другой браузер и откроет свой безопасный URL-адрес ярлыка и притворится, что он Далай-лама, который только что получил другое приглашение по электронной почте от Тома, то есть он попытается присоединиться к его собственной встрече, как если бы он был Далай-лама. то есть Далай, Лама, dalailama@gmail.com.

Первоначально я предполагал, что нет вероятности, что кто-то когда-либо догадался бы о безопасном URL-адресе, поэтому, если это произойдет, я просто позволю человеку войти в систему таким образом.

Но это дало бы опасный доступ McFarlin к учетной записи Далай-ламы (отчасти потому, что я еще не создал режим ограниченного доступа для пользователей, входящих через URL-адреса, чтобы иметь возможность видеть только одно собрание, пока они авторизоваться).

Да, мой исходный код работал таким образом. И затем я позвонил с небес и указал на это.

Что, если Макфарлин предложил Салли и Салли переслать безопасный URL Биллу Гейтсу? Ручным добавлением Билла Гейтса на встречу сначала, Макфарлин мог получить доступ ко всем встречам Гейтса с этим трюком.

Новый код требует участия участника с использованием защищенного URL-адреса, который уже случайно был добавлен в собрание для входа в систему вручную. Вот код...:

if ($model->load(Yii::$app->request->post())) {
  // asking does the person joining already exist in User table
  // might have been added to invitation by organizer or might already be a registered user
  $person = User::find()->where(['email'=>$model->email])->one();
  if (!is_null($person)) {
      // user email already exists
      // improve their profile
      $postedVars = Yii::$app->request->post();
      if (!empty($postedVars['Participant']['firstname'])) {
          $model->firstname = $postedVars['Participant']['firstname'];
      }
      if (!empty($postedVars['Participant']['lastname'])) {
          $model->lastname = $postedVars['Participant']['lastname'];
      }
      UserProfile::improve($person->id,$model->firstname,$model->lastname);
      // are they an attendee
    if (Meeting::isAttendee($model->meeting_id,$person->id)) {
      /*
        // to do note - this has to be changed to restricted access mode or removed
        $identity = $person->findIdentity($person->id);
        Yii::$app->user->login($identity);
        // to do - update user profile with first and last name
        $this->redirect(['meeting/view','id'=>$model->meeting_id]);
      } else {
      */
        // caution - don't turn off this requirement
        // a person could add a celebrity to a meeting by using their email with any meeting code and login with their account
        Yii::$app->getSession()->setFlash('warning', Yii::t('frontend','Since you have an account already, please login below.'));
        return $this->redirect(['/site/login']);
      }
    }

Я буду исправлять это снова, как только создаю единый режим ограниченного доступа.

Я, возможно, и не подумал об этом, если бы не знал, насколько хитрый Макфарлин. Уф. Еще одна королева спасла от отравления.

Наверное, мои долгие выходные в природе заставили меня больше общаться с небесами.

Что в Трубе?

Надеюсь, вам понравился этот эпизод по созданию безопасных URL-адресов, чтобы приглашать людей на собрания. Я предполагаю, что он очень многоразовый для других сценариев в ваших собственных сервисах.

Вы также, вероятно, можете сказать, что я либо наслаждаюсь потенциальным планировщиком собраний, похоже, на этом этапе, либо я слишком долго работал.

В конечном счете, создание защищенных URL-адресов также открывает возможность предлагать пользователям страницу общего планирования. Например, я могу поделиться своим публичным URL-адресом планировщика собраний с друзьями и, скажем, просто запланировать меня по адресу https: meetingplaer.io.

В будущем я мог бы даже расширить Планировщик собраний, чтобы предлагать функции подписки для профессионалов, чтобы назначать встречи на своей открытой странице Планировщика собраний. Однако у меня есть другие интересные идеи. Эта территория хорошо зарекомендовала себя другими компаниями, ориентированными на бизнес.

Поездка на волну домой

Если вы еще не приехали, пойдите в расписание своей первой встречи с Планировщиком собраний прямо сейчас! Попробуйте поделиться своим ярлыком на ярлыке своей встречи и сохраните его в тайне от нашего редактора-редактора Tom McFarlin.

Вы также можете связаться со мной @reifman. Я всегда открываю новые идеи и предложения для будущих руководств. Или попробуйте нашу службу поддержки и откройте отчет об ошибке или билет с запросом на функцию.

Учебное пособие по crowdfunding также находится в работе, поэтому, пожалуйста, зайдите на страницу WeFunder Meeting Plaer.

Оставайтесь с нами в курсе всех этих и других предстоящих учебников, проверив «Построение вашего запуска с помощью серии PHP».

Ссылки по теме

Примечание планирования планировщика После планирования планировщика встреч Программирование с помощью серии Yii2 (Envato Tuts +)