建立您的初创企业:请求安排变更

最终产品图片
您将要创造的

本教程是Envato Tuts + 使用PHP建立启动系列的一部分 在本系列文章中,我 将以 我的 Meeting Planner 应用程序作为真实示例 ,指导您完成从概念到现实的启动 在此过程的每一步中,我都会将Meeting Planner代码作为开放源代码示例发布,您可以从中学习。 我还将解决与启动相关的业务问题。

更改会议计划

会议计划程序 alpha测试阶段开始时,最明显的功能缺陷是在安排会议后无法更改会议。 这不是一个容易的问题。 可以在没有参加者许可的情况下更改会议吗? 还是你应该问? 还是根据您在组织会议中所扮演的角色来进行选择? 如果您只是想问问15分钟后可以开会吗?那应该很容易,对吧?

解决所有这些问题需要对调整会议的社会方面进行一些反思。

随着时间的流逝,我意识到在安排会议之后轻松调整会议的能力可能会成败会议计划者品牌。

上一期有关“高级计划”的文章中 ,我实现了“更改” ,该更改使具有组织权限的组织者或参与者实质上可以重新安排会议的时间而无需征求许可。 在今天的教程中,我将引导您构建“ 请求更改”基础结构。 它要求与会人员请求更改,然后其他人可以接受或拒绝更改,从而影响最终会议日历的详细信息。

在阅读时,希望您可以在现场站点上试用新的“请求更改”功能,并在下面的评论中分享您的想法和反馈。 我确实参与了讨论,但是您也可以在Twitter上与我联系@reifman 。 我总是乐于接受Meeting Planner的新功能建议以及有关未来系列剧集的建议。

提醒一下,Meeting Planner的所有代码都是开源提供的,并使用PHP的Yii2框架编写。 如果您想了解有关Yii2的更多信息,请查看我的平行系列“ 使用Yii2编程”

让我们开始吧。

建立要求变更

一座高耸的山

除了会议视图和日程安排功能外,“请求更改”比该项目上的任何其他功能需要更多的时间和新的代码。

正如我在上一集中所提到的,使用基本安全性对所有内容进行编码要比快速原型制作花费更长的时间,但是设计和构建此功能也涉及到许多其他平台领域:

  • 使用请求和进行时间表更改的社会工程进行设计。
  • 使更改请求的UX保持简单,可以帮助人们在不使界面混乱的情况下请求和响应更改请求。
  • 处理1:1会议的请求很容易,但是为即将到来的多个参与者功能进行编码将需要更加复杂的基础架构。
  • 与多个参与者一起处理对请求的响应。
  • 通过电子邮件发送有关新请求和撤回请求,已接受和已拒绝响应的通知。
  • 当接受的请求影响日程时,更新会议确认和日历详细信息。

因此,虽然这并不是仅针对此功能所做的更改的完美描述,但以下是最终生产服务器代码提取的屏幕截图。

这是对现有代码的更改:

建立您的启动请求安排更改-Git拉文件更改

这是新文件:

建立您的启动请求计划更改-Git拉新文件

此功能涉及许多新代码。

表格及其迁移

最终,我决定围绕两个表构建一个体系结构。 首先是Requests

$this->createTable('{{%request}}', [
    'id' => Schema::TYPE_PK,
    'meeting_id' => Schema::TYPE_INTEGER.' NOT NULL DEFAULT 0',
    'requestor_id' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
    'completed_by' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
    'time_adjustment' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
    'alternate_time' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
    'meeting_time_id' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
    'place_adjustment' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
    'meeting_place_id' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
    'note' => Schema::TYPE_TEXT.' NOT NULL DEFAULT ""',
    'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
    'created_at' => Schema::TYPE_INTEGER . ' NOT NULL',
    'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL',
], $tableOptions);
$this->addForeignKey('fk_request_meeting', '{{%request}}', 'meeting_id', '{{%meeting}}', 'id', 'CASCADE', 'CASCADE');
$this->addForeignKey('fk_request_user', '{{%request}}', 'requestor_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');

以下是进一步解释模型的常量:

const STATUS_OPEN = 0;
  const STATUS_ACCEPTED = 10;
  const STATUS_REJECTED = 20;
  const STATUS_WITHDRAWN = 30;

  const TIME_ADJUST_NONE = 50;
  const TIME_ADJUST_ABIT = 60;
  const TIME_ADJUST_OTHER = 70;

  const PLACE_ADJUST_NONE = 80;
  const PLACE_ADJUST_OTHER = 90;

有两种调整时间的方法: TIME_ADJUST_ABIT (即比所选时间早或晚数分钟或数小时的间隔),或TIME_ADJUST_OTHER (共计不同的会议时间)。

第二个表是RequestResponses

$this->createTable('{{%request_response}}', [
    'id' => Schema::TYPE_PK,
    'request_id' => Schema::TYPE_INTEGER.' NOT NULL DEFAULT 0',
    'responder_id' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
    'note' => Schema::TYPE_TEXT.' NOT NULL DEFAULT ""',
    'response' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
    'created_at' => Schema::TYPE_INTEGER . ' NOT NULL',
    'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL',
], $tableOptions);

基本上,谁要求更改,谁做出了响应以及响应是什么:接受还是拒绝。

多参与者环境需要第二张表。

要求变更

会议组织者和与会人员可以通过我们在上一集中构建的下拉选项菜单访问“ 请求更改 ”:

建立启动请求计划更改-选项菜单请求更改

申请变更表

actionCreate()actionCreate()加载用户请求更改的表单:

建立您的启动请求安排变更-申请变更表

这就是复杂性开始的地方。 参与者可以要求哪些变更?

  • 您想早晚开会吗?
  • 您想在完全不同的时间见面吗?
  • 您想在其他地方见面吗?

注意: 我尚未实现添加新地点和时间的功能-当前,您可以从计划过程中提供的日期和地点中选择其他日期和地点。

早晚的下拉列表

创建下拉列表的代码错综复杂。 我做到了,因此您可以选择两个半小时或更早的时间,以接近原始时间的15分钟为增量,然后以30分钟为增量:

for ($i=1;$i<12;$i++) {
  // later times
  if ($i<4 || $i%2 == 0) {
    $altTimesList[$chosenTime->start+($i*15*60)]=Meeting::friendlyDateFromTimestamp($chosenTime->start+($i*15*60),$timezone,false);
  }
  // earlier times
  $earlierIndex = ((12-$i)*-15);
  if ($i%2 == 0 || $i>=9) {
    $altTimesList[$chosenTime->start+($earlierIndex*60)]=Meeting::friendlyDateFromTimestamp($chosenTime->start+($earlierIndex*60),$timezone,false);
  }
}
$altTimesList[$chosenTime->start]='────────────────────';
$altTimesList[-1000]=Yii::t('frontend','Select an alternate time below');
ksort($altTimesList);

我填写了$altTimesList 使用每个可能时间的按键,并根据用户所在的时区调整友好时间的值。 然后,我使用ksort()对下拉列表进行了排序,因此较早的时间出现在以后。

Meeting Planner的一位顾问(此刻我只有一位)建议显示当前选择的会议时间,我在下面做了。 我还在下拉列表中添加了带有禁用选项的分隔符。 它将较早的时间与较晚的时间分开,但不能选择:

建立您的启动请求计划变更-带有分隔符的增强型请求表

这是下拉代码,显示了如何基于$currentStart索引键禁用分隔符:

<?php
echo $form->field($model, 'alternate_time')->label(Yii::t('frontend','Choose a time slightly earlier or later than {currentStartStr}',['currentStartStr'=>$currentStartStr]))
  ->dropDownList(
      $altTimesList,
      ['options' => [$currentStart => ['disabled' => true]]]
  );
  ?>

而且,当参与者希望选择其他时间之一时,可以使用JQuery更改下拉菜单,这是构建表单的另一种复杂性:

<?php ActiveForm::end();
    $this->registerJsFile(MiscHelpers::buildUrl().'/js/request.js',['depends' => [\yii\web\JqueryAsset::className()]]);
    ?>
</div>

这是/frontend/web/js/request.js:

$("#adjust_how" ).change(function() {
    if ($("#adjust_how" ).val()==50) {
      $("#choose_earlier").addClass('hidden');
      $("#choose_another").addClass('hidden');
    } else if ($("#adjust_how" ).val()==60) {
      $("#choose_earlier").removeClass('hidden');
      $("#choose_another").addClass('hidden');
    } else {
      $("#choose_earlier").addClass('hidden');
      $("#choose_another").removeClass('hidden');
    }
  });

这是隐藏备用时间的表单的样子:

建立您的启动请求安排更改-选择其他位置

只是将不同的地点集成到地点下拉列表中(如顶部的特色图片所示)。

处理请求

提出请求后,我们会通知请求者将通知其他会议参与者。 而且,只要有会议的活跃请求,就有指向View Them的链接:

建立您的启动请求安排更改-会议页面查看请求

我认为这将是人们访问请求的一种简单,整洁的方法。

会议要求清单

以下是会议请求列表,通常只有一个:

建立您的启动请求计划更改-会议请求列表

Request::buildSubject()根据请求的内容(即时间和/或地点更改Request::buildSubject()创建上述字符串:

public static function buildSubject($request_id,$include_requestor = true) {
  $r = Request::findOne($request_id);
  $requestor = MiscHelpers::getDisplayName($r->requestor_id);
  $timezone = MiscHelpers::fetchUserTimezone(Yii::$app->user->getId());
  $rtime ='';
  $place = '';
  switch ($r->time_adjustment) {
    case Request::TIME_ADJUST_NONE:
    break;
    case Request::TIME_ADJUST_ABIT:
      $rtime = Meeting::friendlyDateFromTimestamp($r->alternate_time,$timezone);
    break;
    case Request::TIME_ADJUST_OTHER:
      $t = MeetingTime::findOne($r->meeting_time_id);
      if (!is_null($t)) {
          $rtime = Meeting::friendlyDateFromTimestamp($t->start,$timezone);;
      }
    break;
  }
  if ($r->place_adjustment == Request::PLACE_ADJUST_NONE || $r->place_adjustment == 0 && $r->meeting_place_id ==0 ) {
    // do nothing
  } else {
    // get place name
    $place = MeetingPlace::findOne($r->meeting_place_id)->place->name;
  }
  $result = $requestor.Yii::t('frontend',' asked to meet at ');
  if ($rtime=='' && $place =='') {
    $result.=Yii::t('frontend','oops...no changes were requested.');
  } else if ($rtime<>'') {
    $result.=$rtime;
    if ($place<>'') {
      $result.=Yii::t('frontend',' and ');
    }
  }
  if ($place<>'') {
    $result.=$place;
  }
  return $result;
}

此功能在电子邮件通知中也反复使用。

RequestController.php中还存在一些限制,这些限制会阻止用户每次一次会议提出多个请求:

public function actionCreate($meeting_id)
    {
        // verify is attendee
        if (!Meeting::isAttendee($meeting_id,Yii::$app->user->getId())) {
          $this->redirect(['site/authfailure']);
        }
        if (Request::countRequestorOpen($meeting_id,Yii::$app->user->getId())>0) {
            $r = Request::find()
              ->where(['meeting_id'=>$meeting_id])
              ->andWhere(['requestor_id'=>Yii::$app->user->getId()])
              ->andWhere(['status'=>Request::STATUS_OPEN])
              ->one();
            Yii::$app->getSession()->setFlash('info', Yii::t('frontend','You already have an existing request below.'));
              return $this->redirect(['view','id'=>$r->id]);
        }

这是显示限制的视图请求页面:

建立您的启动请求计划更改-查看请求

如果是您自己的请求,则可以撤回您的请求

如您所见,为此构建了许多不同的UX功能。 当请求者以外的其他人响应时,我没有显示给您。

请求和响应通知电子邮件

在构建这些功能的过程中,我决定创建generic_htmlgeneric_text电子邮件模板,以及可重用的Request::notify()函数,以简化围绕会议策划器的各种Request::notify()的传递。

这是准备电子邮件的Request::create()方法:

public function create() {
  $user_id = $this->requestor_id;
  $meeting_id = $this->meeting_id;
  $subject = Request::buildSubject($this->id);
  $content=[
    'subject' => Yii::t('frontend','Change Requested to Your Meeting'),
    'heading' => Yii::t('frontend','Requested Change to Your Meeting'),
    'p1' => $subject,
    'p2' => $this->note,
    'plain_text' => $subject.' '.$this->note.'...'.Yii::t('frontend','Respond to the request by visiting this link: '),
  ];
  $button= [
    'text' => Yii::t('frontend','Respond to Request'),
    'command' => Meeting::COMMAND_VIEW_REQUEST,
    'obj_id' => $this->id,
  ];
  $this->notify($user_id,$meeting_id, $content,$button);
  // add to log
  MeetingLog::add($meeting_id,MeetingLog::ACTION_REQUEST_SENT,$user_id,0);
}

$content 数组用于电子邮件主题,消息标题和段落,而$button数组用于任何命令按钮,例如“ 响应请求”或“ 查看会议”

这是notify()方法,类似于之前发送电子邮件的send()finalize()操作:

public static function notify($user_id,$meeting_id,$content,$button = false) {
  // sends a generic message based on arguments
  $mtg = Meeting::findOne($meeting_id);
  // build an attendees array for all participants without contact information
  $cnt =0;
  $attendees = array();
  foreach ($mtg->participants as $p) {
      $auth_key=\common\models\User::find()->where(['id'=>$p->participant_id])->one()->auth_key;
      $attendees[$cnt]=['user_id'=>$p->participant_id,'auth_key'=>$auth_key,
      'email'=>$p->participant->email,
      'username'=>$p->participant->username];
      $cnt+=1;
  }
  // add organizer
  $auth_key=\common\models\User::find()->where(['id'=>$mtg->owner_id])->one()->auth_key;
  $attendees[$cnt]=['user_id'=>$mtg->owner_id,'auth_key'=>$auth_key,
    'email'=>$mtg->owner->email,
    'username'=>$mtg->owner->username];
// use this code to send
foreach ($attendees as $cnt=>$a) {
  // check if email is okay and okay from this sender_id
  if ($user_id != $a['user_id'] && User::checkEmailDelivery($a['user_id'],$user_id)) {
    Yii::$app->timeZone = $timezone = MiscHelpers::fetchUserTimezone($a['user_id']);
      // Build the absolute links to the meeting and commands
      $links=[
        'home'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_HOME,0,$a['user_id'],$a['auth_key']),
        'view'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_VIEW,0,$a['user_id'],$a['auth_key']),
        'footer_email'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_EMAIL,0,$a['user_id'],$a['auth_key']),
        'footer_block'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_BLOCK,$user_id,$a['user_id'],$a['auth_key']),
        'footer_block_all'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_BLOCK_ALL,0,$a['user_id'],$a['auth_key']),
      ];
      if ($button!==false) {
        $links['button_url']=MiscHelpers::buildCommand($mtg->id,$button['command'],$button['obj_id'],$a['user_id'],$a['auth_key']);
        $content['button_text']=$button['text'];
      }
      // send the message
      $message = Yii::$app->mailer->compose([
        'html' => 'generic-html',
        'text' => 'generic-text'
      ],
      [
        'meeting_id' => $mtg->id,
        'sender_id'=> $user_id,
        'user_id' => $a['user_id'],
        'auth_key' => $a['auth_key'],
        'links' => $links,
        'content'=>$content,
        'meetingSettings' => $mtg->meetingSettings,
    ]);
      // to do - add full name
    $message->setFrom(array('support@meetingplanner.com'=>$mtg->owner->email));
    $message->setReplyTo('mp_'.$mtg->id.'@meetingplanner.io');
    $message->setTo($a['email'])
        ->setSubject($content['subject'])
        ->send();
    }
  }
}

本质上,generic_html.php布局基于我在我们的电子邮件模板教程中讨论的简单文本更新模板。 它提供了一种格式合理的方式,可以通过电子邮件添加一些段落来更新参与者。

这是整合$content$button数据的generic_html.php视图文件。 它检查第二段和第三段,例如$p2$p3  $button数据:

<tr>
  <td style="color:#777; font-family:Helvetica, Arial, sans-serif; font-size:14px; line-height:21px; text-align:center; border-collapse:collapse; padding:10px 60px 0; width:100%" align="center" width="100%">
    <p>Hi <?php echo Html::encode(MiscHelpers::getDisplayName($user_id)); ?>,</p>
    <p><?= Html::encode($content['p1']) ?></p>
    <?php if ($content['p2']<>'') {
    ?>
    <p><?= Html::encode($content['p2']); ?></p>
    <?php
      }
    ?>
    <?php if (isset($content['p3']) && $content['p3']<>'') {
    ?>
    <p><?= Html::encode($content['p3']); ?></p>
    <?php
      }
    ?>
  </td>
</tr>
<?php if ($links['button_url']!='') {
 ?>
<tr>
  <td style="color:#777; font-family:Helvetica, Arial, sans-serif; font-size:14px; line-height:21px; text-align:center; border-collapse:collapse; padding:30px 0 30px 0" align="center">
    <div>
<!--[if mso]>
      <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="http://" style="height:45px;v-text-anchor:middle;width:155px;" arcsize="15%" strokecolor="#ffffff" fillcolor="#ff6f6f">
        <w:anchorlock/>
        <center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>
      </v:roundrect>
    <![endif]--><a href="<?php echo $links['button_url'] ?>" style='color:#fff; text-decoration:none; -webkit-text-size-adjust:none; background-color:#ff6f6f; border-radius:5px; display:inline-block; font-family:"Cabin", Helvetica, Arial, sans-serif; font-size:14px; font-weight:regular; line-height:45px; mso-hide:all; text-align:center; width:155px' bgcolor="#ff6f6f" align="center" width="155"><?= Html::encode($content['button_text']); ?></a>
  </div>
  </td>
</tr>
<?php } ?>

这是一个通知示例, Rob Smith要求我更改会议时间和地点(根据上面的代码生成):

建立您的启动请求计划更改-请求更改的电子邮件通知

回应要求

单击“ 响应请求”时 ,将进入RequestResponse控制器的actionCreate()方法:

建立您的启动请求安排更改-响应请求表-接受或拒绝

在整个请求UX中,我合并了人们编写注释的能力,从而为请求和响应提供了上下文。

这种形式的挑战是确定如何根据单击的提交按钮将响应定向到不同的控制器方法。 换句话说,区分不同的提交POST按钮单击。

public function actionCreate($id)
{
  $request = Request::findOne($id);
  if (!Meeting::isAttendee($request->meeting_id,Yii::$app->user->getId())) {
    $this->redirect(['site/authfailure']);
  }
    // has this user already responded
    $check = RequestResponse::find()
      ->where(['request_id'=>$id])
      ->andWhere(['responder_id'=>Yii::$app->user->getId()])
      ->count();
    if ($check>0) {
      Yii::$app->getSession()->setFlash('error', Yii::t('frontend','Sorry, you already responded to this request.'));
      return $this->redirect(['meeting/view', 'id' => $request->meeting_id]);
    }
    if ($request->requestor_id == Yii::$app->user->getId()) {
      Yii::$app->getSession()->setFlash('error', Yii::t('frontend','Sorry, can not respond to your own request.'));
      return $this->redirect(['meeting/view', 'id' => $request->meeting_id]);
    }
    $subject = Request::buildSubject($id);
    $model = new RequestResponse();
    $model->request_id = $id;
    $model->responder_id = Yii::$app->user->getId();
    if ($model->load(Yii::$app->request->post()) ) {
      $posted = Yii::$app->request->post();
      if (isset($posted['accept'])) {
        // accept
        $model->response = RequestResponse::RESPONSE_ACCEPT;
        $model->save();
        $request->accept($model);
        Yii::$app->getSession()->setFlash('success', Yii::t('frontend','Request accepted. We will update the meeting details and inform other participants.'));
      } else {
        // reject
        $model->response = RequestResponse::RESPONSE_REJECT;
        $model->save();
        $request->reject($model);
        Yii::$app->getSession()->setFlash('success', Yii::t('frontend','Your decline has been recorded. We will let other participants know.'));
      }
      return $this->redirect(['/meeting/view', 'id' => $request->meeting_id]);
    } else {
        return $this->render('create', [
            'model' => $model,
            'subject' => $subject,
            'meeting_id' => $request->meeting_id,
        ]);
    }
}

这是/frontend/views/request-response/_form.php:

<div class="request-response-form">
    <?php $form = ActiveForm::begin(); ?>
      <?= BaseHtml::activeHiddenInput($model, 'responder_id'); ?>
        <?= BaseHtml::activeHiddenInput($model, 'request_id'); ?>
    <?= $form->field($model, 'note')->label(Yii::t('frontend','Include a note'))->textarea(['rows' => 6])->hint(Yii::t('frontend','optional')) ?>

<div class="form-group">
  <?= Html::submitButton(Yii::t('frontend', 'Accept Request'), 
        ['class' => 'btn btn-success','name'=>'accept',]) ?>
  <?= Html::submitButton(Yii::t('frontend', 'Decline Request'),
        ['class' => 'btn btn-danger','name'=>'reject',
      'data' => [
          'confirm' => Yii::t('frontend', 'Are you sure you want to decline this request?'),
          'method' => 'post',
      ],]) ?>

本质上,我只是向每个按钮添加了'accept''reject' name值。 然后,将其作为过帐值交付,如下所示:

if ($model->load(Yii::$app->request->post()) ) {
      $posted = Yii::$app->request->post();
      if (isset($posted['accept'])) {
        // accept
        $model->response = RequestResponse::RESPONSE_ACCEPT;
        $model->save();
        $request->accept($model);
        Yii::$app->getSession()->setFlash('success', Yii::t('frontend','Request accepted. We will update the meeting details and inform other participants.'));
      } else {
        // reject
        $model->response = RequestResponse::RESPONSE_REJECT;
        $model->save();
        $request->reject($model);
        Yii::$app->getSession()->setFlash('success', Yii::t('frontend','Your decline has been recorded. We will let other participants know.'));
      }
      return $this->redirect(['/meeting/view', 'id' => $request->meeting_id]);
    }

当响应者接受或拒绝请求时,将向他们显示一条简短消息并发送电子邮件。 此外,会议不再有任何活动请求显示:

建立您的启动请求计划更改-接受请求后的会议页面

这是“ 请求更改接受”通知电子邮件:

建立启动请求计划更改-接受请求更改的电子邮件通知

下面的Request::accept()发生了很多事情:

public function accept($request_response) {
  // to do - this will need to change when there are multiple participants
  $this->status = Request::STATUS_ACCEPTED;
  $this->update();
  $m = Meeting::findOne($this->meeting_id);
  // is there a new time
  switch ($this->time_adjustment) {
    case Request::TIME_ADJUST_ABIT:
      // create a new meeting time with alternate_time
      $this->meeting_time_id = MeetingTime::addFromRequest($this->id);
      $this->update();
      // mark as selected
      MeetingTime::setChoice($this->meeting_id,$this->meeting_time_id,$request_response->responder_id);
    break;
    case Request::TIME_ADJUST_OTHER:
     // mark as selected
      MeetingTime::setChoice($this->meeting_id,$this->meeting_time_id,$request_response->responder_id);
    break;
  }
  // is there a different place
  if ($this->place_adjustment == Request::PLACE_ADJUST_OTHER || $this->meeting_place_id !=0 ) {
    MeetingPlace::setChoice($this->meeting_id,$this->meeting_place_id,$request_response->responder_id);
  }
  if ($m->isOwner($request_response->responder_id)) {
    // they are an organizer
    $this->completed_by =$request_response->responder_id;
    $this->update();
    MeetingLog::add($this->meeting_id,MeetingLog::ACTION_REQUEST_ORGANIZER_ACCEPT,$request_response->responder_id,$this->id);
  } else {
    // they are a participant
    MeetingLog::add($this->meeting_id,MeetingLog::ACTION_REQUEST_ACCEPT,$request_response->responder_id,$this->id);
  }
  $user_id = $request_response->responder_id;
  $subject = Request::buildSubject($this->id, true);
  $p1 = MiscHelpers::getDisplayName($user_id).Yii::t('frontend',' accepted the request: ').$subject;
  $p2 = $request_response->note;
  $p3 = Yii::t('frontend','You will receive an updated meeting confirmation reflecting these change(s). It will also include an updated attachment for your Calendar.');
  $content=[
    'subject' => Yii::t('frontend','Accepted Requested Change to Meeting'),
    'heading' => Yii::t('frontend','Requested Change Accepted'),
    'p1' => $p1,
    'p2' => $p2,
    'p3' => $p3,
    'plain_text' => $p1.' '.$p2.' '.$p3.'...'.Yii::t('frontend','View the meeting here: '),
  ];
  $button= [
    'text' => Yii::t('frontend','View the Meeting'),
    'command' => Meeting::COMMAND_VIEW,
    'obj_id' => 0,
  ];
  $this->notify($user_id,$this->meeting_id, $content,$button);
  // Make changes to the Meeting
  $m->increaseSequence();
  // resend the finalization - which also needs to be done for resend invitation
  $m->finalize($m->owner_id);
}

在发送电子邮件之前,会议时间表将进行更新以反映任何新的日期/时间和/或新的地点。 发送电子邮件后,会议结束。 这会将包含更新的日历文件的新会议更新发送给所有参与者:

建立您的启动请求安排更改-更新会议通知

下一步是什么?

希望您喜欢本教程。 构建此功能花费的时间比我希望的要长,但是效果很好。 我认为这为会议计划程序的安排增加了一个维度,这是其他服务无法比拟的。

如果尚未开始,请与Meeting Planner安排您的第一次会议尽管分心 (编码很难), 我仍继续朝着Beta版本取得不可思议的进步:

我还打算编写有关众筹的教程,因此请考虑关注我们的WeFunder Meeting Planner页面

请在下面分享你的评论。 我总是乐于接受新功能的想法和主题建议,以供将来的教程使用。 您也可以与我联系@reifman

请阅读“ 用PHP构建您的启动”系列,继续关注所有这些以及以后的教程。 并一定要查看我们的Yii2系列编程(Envato Tuts +)

相关链接

翻译自: https://code.tutsplus.com/tutorials/building-your-startup-requesting-scheduling-changes--cms-27076

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值