YII2: авторизация через Active Directory в шаблоне basic

Почти наверняка это делается легко и не принужденно, каким то другим способом, более стандартным, но: Мы не ищем легких путей (с). Поэтому велосипед.

Для начала в файле params.php добавим настройки необходимые для соединения с AD:

return [
    "ldap_server"=>"wrfwerfwer.ru",    
];

Далее изменим модель User.php следующим образом, добавив в него следующие функции:

 /**
     * Проверяем логин-пароль используя AD. Если в БД такого пользователя нет, до добавляем
     * @param type $username
     * @param type $password
     * @return null/int если авторизация удалась возвращаем id из БД.
     */
    public static function AuthByAD($username,$password){
        $params = require(__DIR__ . '/../config/web.php');
        $value = \yii\helpers\ArrayHelper::getValue($params, 'params');
        
        $ldap_con = ldap_connect($value["ldap_server"]) or die("Could not connect to LDAP server.");
    
        // Устанавливаем флажки протокола
        ldap_set_option($ldap_con, LDAP_OPT_PROTOCOL_VERSION, 3);  // AD Windows 2003 и выше
        ldap_set_option($ldap_con, LDAP_OPT_REFERRALS, 0);         //Определяет, следовать ли автоматически рефералам, возвращенным сервером LDAP. Зачем нужно - не понятно

        // авторизуемся в AD
        $ldapbind = @ldap_bind($ldap_con, $username."@".$value["ldap_server"], $password);                        
        if ($ldapbind==false) return null;

        $user_id=SELF::GetIdByLoginFromDB($username);      
        if ($user_id!=null) return $user_id;
              
        //если такого логина нет в БД, то добавляем..
        $res=(new \yii\db\Query())->createCommand()->insert('users', ['login' =>$username])->execute();
        if ($res==false){
            die("Не удалось вставить запись с новым пользователем..Увы и ах..");
        };        
        $user_id=$this->GetIdByLoginFromDB($username);
        if ($user_id!=null) return $user_id;
        
        return null;
        
    }
         
    /**
     * Получаем id пользователя по логину из БД
     * @param type $login
     * @return type null/id null если пользователь не найден
     */
    public static function GetIdByLoginFromDB($login){
       $rows = (new \yii\db\Query())->select(['id'])->from('users')->where(['login' => $login])->limit(1)->all();
       if (count($rows)>0) {
           return $rows[0]["id"];
       } else {
         return null;  
       };
    }
    
    /**
     * Получить расширенную информацию о пользователе БД по его ID
     * @param type $id
     * @return string
     */
    public static function GetExtendInfoByIdFromDB($id){
       $rows = (new \yii\db\Query())->select(['id',"login","name","photo","email","last_updated"])->from('users')->where(['id' => $id])->limit(1)->all();
       if (count($rows)>0) {
           $rows[0]["username"]=$rows[0]["login"];
           $rows[0]["password"]="";
           $rows[0]["authKey"]=$rows[0]["id"]."key";
           $rows[0]["accessToken"]=$rows[0]["id"]."-token";
           return $rows[0];
       } else {
         return null;  
       };
        
    }

И там-же заменим функцию:

 public static function findIdentity($id)
    {
        $arr=self::GetExtendInfoByIdFromDB($id);        
        $res=new User();
        $res->id=$arr["id"];
        $res->username=$arr["username"];
        $res->password=$arr["password"];
        $res->authKey=$arr["authKey"];
        $res->accessToken=$arr["accessToken"];        
        return $res;      
    }

В модели LoginFrom.php изменим функцию login():

 public function login()
    {
        $user_id=User::AuthByAD(Yii::$app->request->post()["LoginForm"]["username"],Yii::$app->request->post()["LoginForm"]["password"]);        
        if ($user_id==null) {
            $this->addError("password", 'Не верный пароль или логин');
            return false;
        };
        $extended_info=User::GetExtendInfoByIdFromDB($user_id);
        $tuser=new User();
        $tuser->id=$extended_info["id"];
        $tuser->username=$extended_info["username"];
        $tuser->password=$extended_info["password"];
        $tuser->authKey=$extended_info["authKey"];
        $tuser->accessToken=$extended_info["accessToken"];

        return Yii::$app->user->login($tuser, $this->rememberMe ? 3600*24*30 : 0);
        
    }

В результате получим собственно возможность авторизации через AD, с сохранением пользователя AD если такового в БД нет, в базе данных.

Windows: Ошибка при установке YII через composer

Ошибка вида:

In CurlDownloader.php line 372:

  curl error 60 while downloading https://asset-packagist.org/packages.json: SSL certificate problem: self-signed certificate in certificate chain

Решение:

скачать сертификат curl.haxx.se/ca/cacert.pem и подлогжить его в php.ini:

[curl]
curl.cainfo ="e:\cacert.pem"

1С: POST запросы

POST запрос к какому то ресурсу в 1С можно организовать например примерно так:

&НаСервере
Функция СтруктураВСтрокуJSON(Данные)экспорт
	
		ЗаписьJSON = Новый ЗаписьJSON();
		ЗаписьJSON.УстановитьСтроку();
		ЗаписатьJSON(ЗаписьJSON, Данные);
		ответ=ЗаписьJSON.Закрыть();
		// заменим -999999999 на null раз уж 1С это не умеет
		ответ=стрЗаменить(ответ,"-999999999","null");
		возврат ответ;
	
КонецФункции
&НаСервере
Функция API_SendSMS(УРЛ,Логин,Пароль,Отправитель,Телефон,ТекстСмс,ВремяЖизни=360)экспорт
	Соединение=ПолучитьПараметрыСоединения(УРЛ,Логин,Пароль);
	answer=API_getSessionID(Соединение,Логин,Пароль);
	если answer.error=true тогда возврат answer;конецесли;	
	
	ПОСТ_Структура=Новый Структура("sessionId,sourceAddress,data,destinationAddress,validity");
	ПОСТ_Структура.sessionId=answer.result; 		 //идсессии,
	ПОСТ_Структура.sourceAddress=Отправитель;  		 //имяотправителя
	ПОСТ_Структура.destinationAddress=Телефон;		 //получатель(номер)
	ПОСТ_Структура.data=ТекстСмс;	 				 //текст сообщения
	ПОСТ_Структура.validity=ВремяЖизни; 			 //время жизни в минутах

	Заголовки = Новый Соответствие;
	Заголовки["Content-Type"] = "application/json";	
	Заголовки.Вставить("Connection", "keep-alive");	
	Заголовки.Вставить("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)");

	HTTPЗапрос = Новый HTTPЗапрос("/rest/Sms/Send",Заголовки);
	HTTPЗапрос.УстановитьТелоИзСтроки(СтруктураВСтрокуJSON(ПОСТ_Структура), КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
	
	Результат = Соединение.ВызватьHTTPМетод("POST",HTTPЗапрос);
	РезЗапроса=JSONВСтруктуру(Результат.ПолучитьТелоКакСтроку());
	если ТипЗнч(РезЗапроса)=Тип("Строка") тогда
		answer.error=true;
		answer.result=РезЗапроса;
		возврат answer;
	конецесли;	
	если ТипЗнч(РезЗапроса)=Тип("Структура") тогда
		answer.error=true;
		answer.result=РезЗапроса.Desc;
		возврат answer;		
	конецесли;	
	answer.error=false;
	answer.result=РезЗапроса[0];
	возврат answer;
	
КонецФункции

Синхронный запрос через fetch

Да, знаю, плохо, отвратительно и всё такое. Но иногда нужно. Например мне понадобилось при написании Service Worker, для возможности подмены запрашиваемого для загрузки url.. Реализовал так:

self.addEventListener('fetch', (event) => {    
    console.log('Запрашиваем URL: '+event.request.url);    
    (async () => {
        res=await postData("woodpecker.php", { "url": event.request.url});
    })();
    console.log("--получили ответ");
    if (res.error===false){
        console.log("Отдаём URL");
        if ( event.request.url.endsWith( '.png' ) ) {
           return event.respondWith(
             fetch( 'm.jpg' )
           );
         }        
    } else {
        return event;
    };
            
});

async function postData(url = "", data = {}) {
   console.log("-отправляем в "+url);
  // Default options are marked with *
  const response = await fetch(url, {
    method: "POST", 
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded', 
    },
    body: "data="+JSON.stringify(data), 
  });
  return await response.json();
}

Map api yandex: иконка кластера в виде круговой диаграммы

Задача: отобразить на карте иконки кластеров в виде диаграммы построенной на основании сумм неких значений

Решение:

Используя библиотеку PieChartGD.php создадим файл вывода картинки (PHP):

<?php
include_once '../../SamChristy/PieChart/PieChartGD.php';

use SamChristy\PieChart\PieChartGD;

$chart = new PieChartGD(300, 300);

$chart->setLegend(false);

$sausages_6=$_GET["sausages_6"];
$sausages_6_12=$_GET["sausages_6_12"];
$sausages_more_12=$_GET["sausages_more_12"];

$max=$sausages_6+$sausages_12+$sausages_more_12;
$pers_6=round($sausages_6*100/$max);
$pers_6_12=round($sausages_6_12*100/$max);
$pers_more_12=round($sausages_more_12*100/$max);

$chart->addSlice('sausages_6', $pers_6, '#0a9b0e');
$chart->addSlice('sausages_6_12',    $pers_6_12, '#f3a61f');
$chart->addSlice('sausages_more_12',$pers_more_12, '#f31f1f');

$chart->draw();
$chart->outputPNG();

Далее создадим функцию отображения кластера (JavaScript):

function SetClusterProp(ob){
    console.log("-настраиваю иконки кластера");

   ob.clusters.options.set({
        gridSize: 800,
        groupByCoordinates: true,
        clusterDisableClickZoom: true,
        clusterIcons: [{
            href: 'images/m1.png',
            size: [50, 50],            
            offset: [-25, -25]
        }]
    });
    ob.clusters.events.add('click', function (e) {
          console.log("Кликнули по кластеру");
          click_cluster = e.get('target');//.state.get('activeObject')
    });
    ob.clusters.events.add('add', function (e) { 
       var cluster = ob.clusters.getById(e.get('objectId')),
        objects = cluster.properties.geoObjects;
        if (objects.length >= 20 && objects.length < 100) {       
            dz=0;sausages=0;sausages_6=0;sausages_6_12=0;sausages_more_12=0;
            for (var i = 0; i < objects.length; i++) {
              dz=dz+objects[i].properties.dz;
              sausages=sausages+objects[i].properties.sausages;
              sausages_6=sausages_6+objects[i].properties.sausages_6;
              sausages_6_12=sausages_6_12+objects[i].properties.sausages_6_12;
              sausages_more_12=sausages_more_12+objects[i].properties.sausages_more_12;
            };            
            ob.clusters.setClusterOptions(cluster.id, {
                clusterIcons: [{
                    href: 'images/pie.php?sausages='+sausages+"&sausages_6="+sausages_6+"&sausages_6_12="+sausages_6_12+"&sausages_more_12="+sausages_more_12,
                    size: [45, 45],            
                    offset: [-22.5, -22.5]
                }]
            });
        }
        if (objects.length >= 100 && objects.length < 1000) {       
            dz=0;sausages=0;sausages_6=0;sausages_6_12=0;sausages_more_12=0;
            for (var i = 0; i < objects.length; i++) {
              dz=dz+objects[i].properties.dz;
              sausages=sausages+objects[i].properties.sausages;
              sausages_6=sausages_6+objects[i].properties.sausages_6;
              sausages_6_12=sausages_6_12+objects[i].properties.sausages_6_12;
              sausages_more_12=sausages_more_12+objects[i].properties.sausages_more_12;
            };
            ob.clusters.setClusterOptions(cluster.id, {
                clusterIcons: [{
                    href: 'images/pie.php?sausages='+sausages+"&sausages_6="+sausages_6+"&sausages_6_12="+sausages_6_12+"&sausages_more_12="+sausages_more_12,
                    size: [55, 55],            
                    offset: [-22.5, -22.5]
                }]
            });
        }
        if (objects.length >= 1000) {       
            dz=0;sausages=0;sausages_6=0;sausages_6_12=0;sausages_more_12=0;
            for (var i = 0; i < objects.length; i++) {
              dz=dz+objects[i].properties.dz;
              sausages=sausages+objects[i].properties.sausages;
              sausages_6=sausages_6+objects[i].properties.sausages_6;
              sausages_6_12=sausages_6_12+objects[i].properties.sausages_6_12;
              sausages_more_12=sausages_more_12+objects[i].properties.sausages_more_12;
            };                   
            ob.clusters.setClusterOptions(cluster.id, {
                clusterIcons: [{
                    href: 'images/pie.php?sausages='+sausages+"&sausages_6="+sausages_6+"&sausages_6_12="+sausages_6_12+"&sausages_more_12="+sausages_more_12,
                    size: [65, 65],            
                    offset: [-22.5, -22.5]
                }]
            });
        }

    });        
  return ob;
} 

И перед началом отрисовки меток, настроим кластеризацию:

 // настройки кластеризакции
    objectManager = new ymaps.ObjectManager({
        gridSize: 120,
        clusterDisableClickZoom: true,
        geoObjectOpenBalloonOnClick: true,
        minClusterSize: 2,
        showInAlphabeticalOrder: false,
        viewportMargin: 128,
        zoomMargin: 0,         
        clusterize: true
    }); //кластеризуем
          
    objectManager=SetClusterProp(objectManager);        
    myMap.geoObjects.add(objectManager);        
...
objectManager.add(массив_объектов);    

В результате получим что-то вроде:

1 40 41 42 43 44 310