Программирование, веб-кодинг

Yii2: Генерация файла ics на сайте для добавления в iCalendar

Создаем кнопку на сайте для добавления события ical в календарь (.ics)

Если вы публикуете на сайте анонсы мероприятий и всяких встреч, то не лишним будет добавить кнопку "Добавить в календарь ics". Многие iphone-о-владельцы будут рады парой кликов добавить в свой iCalendar событие, которое хотят посетить или не пропустить.

Yii2: Генерация файла ics на сайте для добавления в iCalendar

В работе можно посмотреть на naukapobedit.ru, кнопка "Добавить в календарь".

Итак, в представление добавляем кнопку и ссылку на метод в контроллере, который сгенерирует и выдаст в браузер файл с расширением .ics на скачивание. Я использую виджет GridView, который работает в режиме Pjax. Добавляю кнопку в колонку.

<?= GridView::widget([
            ...
            'columns' => [
                ...
                [
                    'attribute'=>'addtocalendar',
                    'label' => '',
                    'headerOptions' => ['width' => '220'],
                    'format'=>'raw',
                    'value'=>function ($model, $index, $widget){
                        if(($model->when_end) == NULL){
                            return 
                                '<div class="dropdown">
                                    <button class=" btn btn-default dropdown-toggle btn-block" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                        <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span> Добавить в календарь
                                    </button>
                                    <ul class="dropdown-menu dropdown-menu-right"><li>'.
                                        Html::a(
                                           'Добавить в календарь (.ics)',
                                           ['event/output', 'id'=>$model->id],
                                           [                       
                                             'title'=>'Подробнее',
                                             'target'=>'_blank',
                                             'data-pjax'=>0
                                           ]
                                        ).'
                                    </li></ul>
                                </div>';
                        } else {return "";}
                    }
                ],
                ...

Здесь я добавил кнопку с выпадающим списком, чтобы потом добавить ещё вариант Добавить в Google Calendar. Сейчас говорим только про ical. При нажатии на ссылку из выпадающего списка вызывается метод 'output' в контроллере 'event'. Очень важный параметр 'data-pjax'=>0 - он отменяет режим pjax, иначе браузер отображает содержимое файла .ics, а должен отдавать файл для сохранения пользователем.



Переходим к контроллеру. Там метод output вызывает метод input, и ещё некоторые "махинации" с форматом даты есть. Кто хочет, сделайте код по изящнее.

const DATETIME_FORMAT = 'Ymd\THis';//'Ymd\THis\Z';

public function actionOutput($id)
    {
        $model = $this->findModel($id);
        
        $ch = curl_init();                  //инициализируем сессию и возвращаем ее дескриптор
        $_csrf = http_build_query(['_csrf'=>$_COOKIE['_csrf']]);
        $token = http_build_query([
            'when_date'=>$model->when_date,
            'when_time'=>$model->when_time,
            'who'=>$model->who,
            'what'=>$model->what,
            'clubname'=>$model->clubName,
            'where'=>$model->where,
            'link'=>$model->link,
             '_csrf'=>Yii::$app->request->getCsrfToken()]);
        curl_setopt($ch, CURLOPT_URL, 'http://naukapobedit.ru/event/input');    //Указываем на какой урл будем передавать данные
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_COOKIE, $_csrf);           //Указываем способ передачи данных
        curl_setopt($ch, CURLOPT_POSTFIELDS, $token );   //Указываем данные, которые будем передавать на сервер
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //Сохранять полученный ответ от сервера будем в переменную
        //curl_setopt($ch, CURLOPT_HEADER, 1); 

        $output = curl_exec($ch);
        curl_close($ch);
        
        $headers = Yii::$app->response->headers;
        $headers->set('Content-Disposition', 'attachment;filename="«'.\yii\helpers\Html::encode($model->who.'__'.$model->what).'».ics"');
        $headers->set('Content-Type', 'text/calendar;charset=utf-8');
        $headers->set('Content-Description: File Transfer');
        $headers->set('Cache-Control: public');
        header('HTTP/1.0 200 OK', true, 200);
        
        Yii::$app->response->sendContentAsFile($output, "«".$model->who."__".$model->what."»".'.ics');
        Yii::$app->response->send();
    } 
    
    public function actionInput()
    {  
        $when_date = $_POST['when_date'];
        $when_time = $_POST['when_time'];
        $who = $_POST['who'];
        $what = $_POST['what'];
        $clubname = $_POST['clubname'];
        $location = $_POST['where'];
        $url = $_POST['link'];
        
        $dt = new \DateTime($when_date." ".$when_time, new \DateTimeZone("UTC"));
        $dt2 = new \DateTime($when_date." ".$when_time, new \DateTimeZone("UTC"));
        $dtend = $dt2->add(new \DateInterval('PT2H'))->format(self::DATETIME_FORMAT);
        
        $dt->createFromFormat('Y-m-d H:i:s', $when_date." ".$when_time);
        $dtstart = $dt->format(self::DATETIME_FORMAT);
        
        $now = new \DateTime();
        $dtstamp = $now->format(self::DATETIME_FORMAT);
        
return $ical = "BEGIN:VCALENDAR
PRODID:-//Naukapobedit//NONSGML Naukapobedit //RU
VERSION:2.0
METHOD:PUBLISH
BEGIN:VEVENT
UID:" . md5(uniqid(mt_rand(), true)). "
DTSTART:".$dtstart."
DTEND:".$dtend."
DTSTAMP:".$dtstamp."
SUMMARY:".$who.": ".$what."
DESCRIPTION:".$clubname."
LOCATION:".$location."
URL:".$url."
END:VEVENT
END:VCALENDAR
";
    }