Instructor Lead On-Demand Learning Courses - August Big $10 Sitewide Extravaganza All Pro Web Designs and Udemy are happy to offer this special to you, good only on dates: 08/21-08/31 Act Now!

Building Your Startup: Completing Group Scheduling

Final product image
What You’ll Be Creating’;
$m->notify($m->id,$p->participant_id);
}

Any filtering is done deeper within the event log textualization.

Enhanced Notifications: “Everyone Is Available!”

I also created a new notification for alerting organizers when everyone is agreeable to at least one specific place and time, MeetingLog::ACTION_SEND_EVERYONE_AVAILABLE:

// check if meeting has place and time for everyone now
if (count($m->participants)>1 && !MeetingLog::hasEventOccurred($m->id,MeetingLog::ACTION_SEND_EVERYONE_AVAILABLE) && Meeting::isEveryoneAvailable($m->id)) {
  Meeting::notifyOrganizers($m->id,MeetingLog::ACTION_SEND_EVERYONE_AVAILABLE);
  MeetingLog::add($m->id,MeetingLog::ACTION_SEND_EVERYONE_AVAILABLE,0);
}

This notifies organizers when the meeting is ready to finalize/confirm.

Here’s the code that looks at all the meeting places and meeting times to see if everyone is agreeable to at least one place:

public static function isEveryoneAvailable($meeting_id) {
  // check that one place works for everyone attending
  $m = Meeting::findOne($meeting_id);
  $cntAll = $m->countAttendingParticipants(true);
  // count organizer + attending participants
  $mpExists=false;
  $mtExists=true;
  $mps = frontendmodelsMeetingPlace::find()->where(['meeting_id'=>$meeting_id])->all();
  foreach ($mps as $mp) {
    $cnt=0;
    foreach ($mp->meetingPlaceChoices as $mpc) {
      if ($m->getParticipantStatus($mpc->user_id)!=Participant::STATUS_DEFAULT) {
        // skip withdrawn, declined, removed participants
        continue;
      }
      if ($mpc->status == frontendmodelsMeetingPlaceChoice::STATUS_YES) {
        $cnt+=1;
      }
    }
    if ($cnt >=$cntAll) {
      $mpExists = true;
    }
  }
  $mts = frontendmodelsMeetingTime::find()->where(['meeting_id'=>$meeting_id])->all();
  foreach ($mts as $mt) {
    $cnt=0;
    foreach ($mt->meetingTimeChoices as $mtc) {
      if ($m->getParticipantStatus($mtc->user_id)!=Participant::STATUS_DEFAULT) {
        // skip withdrawn, declined, removed participants
        continue;
      }
      if ($mtc->status == frontendmodelsMeetingTimeChoice::STATUS_YES) {
        $cnt+=1;
      }
    }
    if ($cnt >=$cntAll) {
      $mtExists = true;
    }
  }
  // at least one time and one place works for everyone attending
  if ($mpExists && $mtExists) {
    return true;
  } else {
    return false;
  }
}

Similarly, I built a function to flash a notice to the organizer that none of the date times and places are acceptable to anyone, Meeting::isSomeoneAvailable():

if ($model->status < = Meeting::STATUS_SENT) {
  if ($model->isOrganizer()
    && ($model->status == Meeting::STATUS_SENT) 
    && !$model->isSomeoneAvailable()) {
    Yii::$app->getSession()->setFlash('danger', 
    Yii::t('frontend',
    'None of the participants are available for the meeting's current options.'));
  }

This indicates that they should suggest additional date times and/or places.

Updating Meeting Reminders

Everything about meeting reminders worked well for multiple participants, but I did need to turn off reminders if a participant had declined or withdrawn from a meeting or been removed:

$cnt =1;
foreach ($mtg->participants as $p) {
if ($p->status ==Participant::STATUS_DEFAULT) {
 $attendees[$cnt]=$p->participant_id;
 $cnt+=1;
 }

STATUS_DEFAULT indicates an attendee that should be added to the array of users to email reminders too. 

Reviewing Calendar Files

I also reviewed the work of generating calendar files for invitations to ensure that all the attendees are included. In Meeting::prepareDownloadIcs(), I put together an attendees array with the owner and participants actively attending:

$attendees = array();
foreach ($m->participants as $p) {
  if ($p->status ==Participant::STATUS_DEFAULT) {
    $auth_key=commonmodelsUser::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;
    // reciprocate friendship to organizer
    frontendmodelsFriend::add($p->participant_id,$p->invited_by);
    // to do - reciprocate friendship in multi participant meetings
  }
}
$auth_key=commonmodelsUser::find()->where(['id'=>$m->owner_id])->one()->auth_key;
$attendees[$cnt]=['user_id'=>$m->owner_id,
  'auth_key'=>$auth_key,
  'email'=>$m->owner->email,
  'username'=>$m->owner->username];
foreach ($attendees as $cnt=>$a) {
  if ($a['user_id']==$actor_id) {
    $icsPath = Meeting::buildCalendar($m->id,$chosenPlace,$chosenTime,$a,$attendees);

During this time, I also learned how to indicate that a calendar file of a canceled meeting should trigger removal of the event from someone’s calendar. The ics standard is powerful though not easily learned.

Updating Request Changes for Groups

As I wrote about recently, the Requesting Meeting Changes feature required a lot of work and new UX.

Startup Series Group Scheduling - Request a Change to Your Meeting

For multiple participant meetings, the social engineering needed to change again. For example, organizers can accept or reject requests and change the meeting schedule. However, participants can only express Like, Dislike or Don’t Care about change requests. And organizers should see all the participants’ responses to aid them in their decision-making.

Here’s what the participant sees after they submit their change request:

Startup Series Group Scheduling - Confirming Your Request

New change requests needed to be sent to all participants. This is handled transparently by the activity log notifications. When a request is made, this event is created in RequestController::actionCreate() submit:

MeetingLog::add($model->meeting_id,MeetingLog::ACTION_REQUEST_CREATE,Yii::$app->user->getId(),$model->id);

Here’s what the requested change notification looks like to other participants:

Startup Series Group Scheduling - Email Notification of Requested Changes to Participants

Everyone is asked to respond. Clicking Respond to Request jumps right to the request. Or you can find it through the list of requests from the flash alert link on the meeting shown above.

Startup Series Group Scheduling - List of Requests

New UX for Participants Responding to Requests

Here’s the form that participants see when they respond to a request:

Startup Series Group Scheduling - Participants View of Responding to a Request

If there are other participant responses already, they’ll see them:

Startup Series Group Scheduling - Startup Series Group Scheduling - Participants View of Responding to a Request with Others Responses

Here’s the top part of that form in /frontend/views/request-response/_form.php:

< ?= $subject ?>

< ?= GridView::widget([ 'dataProvider' => $responseProvider, 'columns' => [ [ 'label'=>'Responses from Other Participants', 'attribute' => 'responder_id', 'format' => 'raw', 'value' => function ($model) { $note=''; if (!empty($model->note)) { $note = ' said, "'.$model->note.'"'; } return '
'.MiscHelpers::getDisplayName($model->responder_id).' '.$model->lookupOpinion().$note.'
'; }, ], ], ]); ?>
< ?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')) ?>

The Gridview lists the existing responses, e.g. Like, Dislike, Neutral, and any personal note attached.

Then, here’s the code for the lower half of the form, which will display Like, Dislike, Don’t Care to participants but Accept and Make Changes or Decline Request to owners.

< ?php
  if (!$isOwner && $isOrganizer) {
?>
  

< ?= Yii::t('frontend','Since you are an organizer, you can accept the request and make the changes or reject it.');?>

< ?php } ?> < ?php if ($isOrganizer) { ?>
< ?= Html::submitButton(Yii::t('frontend', 'Accept and Make Changes'), ['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', ],]) ?>
< ?php } ?> < ?php if (!$isOwner && $isOrganizer) { ?>

< ?= Yii::t('frontend','Or, you can just express your opinion and defer to other organizers.');?>

< ?php } ?> < ?php if (!$isOwner) { ?> < ?php if (!$isOrganizer) { ?>

< ?= Yii::t('frontend','Please share your opinion of this request for the organizers to consider.');?>

< ?php } ?>
< ?= Html::submitButton(Yii::t('frontend', 'Like'), ['class' => 'btn btn-success','name'=>'like',]) ?> < ?= Html::submitButton(Yii::t('frontend', 'Don't Care'), ['class' => 'btn btn-info','name'=>'neutral',]) ?> < ?= Html::submitButton(Yii::t('frontend', 'Dislike'),['class' => 'btn btn-danger','name'=>'dislike',]) ?>
< ?php } ?> < ?php ActiveForm::end(); ?>
Startup Series Group Scheduling - Organizers View of Responding to a Request

Participants that are anointed organizers are shown both sets of buttons and can either express their opinion or make or decline the change.

Here’s the email notification that a change has been accepted:

Startup Series Group Scheduling - Email Notification that Change was Accepted

Of course, an updated invitation and calendar files will be sent out to everyone if a change is made.

Always More Improvements to Make

I hope you’ve enjoyed these two episodes (today’s and Building Your Startup: Meetings With Multiple Participants). In startup mode with a huge new feature, there’s always a focused, streamlined launch effort, which leaves many loose ends undone and defects unpolished.

A few examples of this include:

  • Rejoining meetings you’ve declined or withdrawn from.
  • Improved presentation of the participant list in invitations.
  • Options to keep the participant list and/or their individual statuses private from other participants.
  • Improved handling and presentation of group contact information and virtual conference details, e.g. a conference line and participation code.
  • Secure URL for sharing meeting invitations. This would allow organizers to share a URL on Facebook or via email to invite new participants.

Despite these shortcomings, I’ve worked super hard to reach this level of function and usability for Meeting Planner. I’m super excited about its progress and hearing good feedback about it from friends and colleagues. 

I’m turning in these two tutorials today and taking a few days offline in the forest for some downtime. Rest is important. Being in touch with nature helps remind you of what’s important in life—startups aren’t always, actually. They may be creative pursuits, important to our income and careers, they may in some cases help people live more efficiently and productively—but they are often distant from the earth, from friendships, and from helping others less fortunate. These are all things I think about every day and will do again while I’m away.

I’ll ask myself repeatedly am I doing everything I want to be doing with my time—especially in light of my brain surgery.

I’ll also take heart from the fact that I’m proud of Meeting Planner, especially the work to date and its growing usefulness. Overall, the service’s beta is nearing completion. 

Multiple participant meetings had been the most daunting, complex remaining work item. Looking ahead, features and tasks are more moderate, smaller, and more easily manageable. I’m excited about its future prospects!

If you haven’t yet, go schedule your first meeting with Meeting Planner now!

A tutorial on crowdfunding is also in the works, so please follow our WeFunder Meeting Planner page.

You can also reach out to me @reifman. I’m always open to new feature ideas and topic suggestions for future tutorials.

Stay tuned for all of this and more upcoming tutorials by checking out the Building Your Startup With PHP series

Related Links

See more about Building Your Startup: Completing Group Scheduling

Instructor Lead On-Demand Learning Courses - August Big $10 Sitewide Extravaganza All Pro Web Designs and Udemy are happy to offer this special to you, good only on dates: 08/21-08/31 Act Now!

Leave a Reply