• Почему ответы одинаковые?

    copist
    @copist
    Empower people to give
    Вот это .php?_=1548878205977 - надо генерить динамически
    Например так

    const url = "https://tproger.ru/wp-content/plugins/citation-widget/get-quote.php"
    
    let query = () =>{
      return new Promise((resolve,reject)=>{
        https.get(url + '?_=' + Date.now(), .... 
        // или так
        https.get(url + '?_=' + Math.random(), ....
    };


    Иначе может быть закешированный ответ
    Ответ написан
    Комментировать
  • Как реализовать отзывы на лендинге без бэкенда?

    copist
    @copist
    Empower people to give
    Попробуй backendless архитектуру - храни отзывы в специальном сервисе, сохраняй и получай через AJAX
    https://habr.com/ru/company/backendless/blog/180367/

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

    Кстати, форма у тебя не соотвествует блокам. В блоках есть картинка (отлично подготовленная дизайнером), заголовок, текст, имя, профессия. У тебя в форме имя имейл и текст отзыва.

    Вдруг они вместо отзыва вопрос зададут? Как написать им в ответ?
    Вдруг отзыв будет жалобой? Как потом пометить отзыв как "проблема решена"?

    Более чем уверен, понадобится админка для всех этих отзывов. Не такая уж и сложная.
    Ответ написан
    4 комментария
  • Почему Incorrect string value при strtolower?

    copist
    @copist
    Empower people to give
    mb_strtolower
    Ответ написан
    Комментировать
  • Есть ли русские сервисы по поиску менторов/наставников?

    copist
    @copist
    Empower people to give
    Лучшую реализацию сервиса наставников я нашёл на codementor.io - а русских реализаций аналогичных или более качественных нет, и пожалуй кодментор самый раскрученный, поскольку в самом начале развития получил несколько $M инвестиций как стартап и знатно вложились в функционал и раскрутку.

    Плюсы:
    * Можно разово консультации получить
    * Можно запланировать longterm на несколько месяцев вперёд
    * У них есь краудсорсный сервис codereview
    * Можно захантить спеца
    * Можно нанять на фриланс
    * Много статей интересных - по ним можно определить уровень специалистов-менторов
    * Менторы могут устроить аукцион
    * Менторы даже могут дать подсказку (текстом в чате) ещё до начала митинга

    Минусы:
    * английский в основном, но есть менторы из России и Украины - с ними можно на русском общаться
    * стоимость больше 1000 р в час, но единица тарификации - 15 минут, иногда первые 15 минут бесплатные (зависит от ментора)
    * мобильное приложение глючное

    P.S. Я работал там и как бы со стороны ученика скажу - очень ненадёжно ориентироваться на одного ментора. Ментор то занят, то устал. Редкий ученик ждёт часы или сутки, чтобы встретиться. Чем сложнее тема, чем больше она приближена к реальным производственным задачам, тем нетерпеливей ученик. И выбор - это лучшее, что можно придумать.
    Ответ написан
    2 комментария
  • Всем привет,может кто знает как менять цены на сайте в зависимости от гео?

    copist
    @copist
    Empower people to give
    Есть два способа: 1. взять из браузера, если пользователь разрешит 2. определить по IP адресу
    Оба способа не 100% надёжны, могут показать совершено другую страну, например если посетитель пользуется прокси-сервером

    По втором способу (через IP адрес)
    1. На веб-сервере подключить модуль geo
    В Nginx https://docs.nginx.com/nginx/admin-guide/dynamic-m...
    В Apache https://dev.maxmind.com/geoip/legacy/mod_geoip2/
    В заголовках HTTP запроса будут дополнительные заголовки "страна", "город", "координаты" и другое

    2. В PHP подключить модуль GeoIP
    Тогда станут доступны методы типа "определить город", "определить страну"
    php.net/manual/ru/book.geoip.php

    Или использовать TabGeo https://github.com/dostelon/TabGeo - проект умер

    3. Воспользоваться серверными методами (межсерверный API) - ты ему IP адрес клиента, он тебе - геоинформацию
    https://dev.maxmind.com/geoip/legacy/web-services/
    или ip-api.com https://ipinfo.io geoip-db.com

    4. Аналогично можно использовать клиентскими (из браузера).

    В итоге у тебя либо на сервере (в PHP), либо в клиенте (JavaScript) код страны и по этому коду переключить блоки текстов и цены.
    Ответ написан
  • Как защитить идею?

    copist
    @copist
    Empower people to give
    1. Проведите исследование, оцените жизнеспособность идеи. Наймите временно помощников, *заплатите*. Наймите маркетолога, чтобы он сделал оценку рентабельности. *заплатите*.

    2. Наймите аналитика, технического писателя, дизайнера. Подпишите с ними договор о неразглашении комерческой информации. Раскажите идею. Сделайте грубый карандашный набросок. Заключите контракт на выполнение работы по проектированию. Оцените результат, скорректируйте при необходимости. *Оплатите работу.*

    3. Реализуйте минимальную рабочую версию. На программную реализацию можно получить патент. Не забудьте *заплатить*

    4. Наймите юриста, проанализируйте рынок. Возможно вы сунулись в область, где уже есть патент. *Заплатите за работу*. Если возможно и есть желание, запатентуйте реализацию и зарегистрируйте товарный знак. *Заплатите*

    Иногда *заплатите* можно пропустить, если Вы самостоятельно можете эту работу выполнить. Бывают основатели-универсалы, способные на все перечисленные работы (маркетинг, проектирование, реализация, правоведение). Если нет - попробовать в команду подобрать второго-третьего сооснователя для компенсации недостающих навыков.

    Да, для старта любого дела нужны деньги. Как минимум п1. и п2. п.3 надо выполнить, чтобы можно было попробовать найти инвестора. Инвесторы не вкладываются в проекты без перспектив, макета и команды с нужными навыками.
    Ответ написан
    Комментировать
  • Что значит :id в запросе "SELECT * FROM db_name WHERE id = :id"?

    copist
    @copist
    Empower people to give
    Артем ответил правильно, но в указанном фрагменте кода https://github.com/igorsimdyanov/php7/blob/fd6d91e... нет биндинга. Это комментарии с заготовками кода. Кто-то написал на будущее. Под id подразумевался скорее всего slug страницы, чтобы из базы извлекать мета-теги. Его, к примеру, можно вычислить из URL текущего запроса или через какую-нибудь компоненту/класс/модуль, отвечающий за отображение текущей страницы.
    Ответ написан
    Комментировать
  • Как вы поддерживаете фикстуры актуальными?

    copist
    @copist
    Empower people to give
    Писать в массивы файлами (как рекомендует Yii) - пробывали - сложно держать актуальными - у нас в неделю по 5-10 миграций, структура довольно активно меняется + сами объекты сложные, некоторые агрегаты раскиданы по 10и таблицам.

    Конечно сложно. А откуда появляется метрика "Код с тестами писать в три раза медленнее"
    Код поддерживать, тесты поддерживать, фикстуры поддерживать.
    Ответ написан
    3 комментария
  • Как можно привлечь студента к стартапу?

    copist
    @copist
    Empower people to give
    Вам не получится создать продукт только с джуниором-программистом, это я вам 200% гарантию дам. Мне из 500 джунов только 4 встречалось, которые безжалостно ежедневно бьются головой в стену и упорно продолжают работу, несмотря на то, что они НИФИГА не понимают. Джунам нужен наставник, даже если они говорят, что сами разберутся. Без этого продукт будет визуально некрасив, полон труднообъяснимых багов, код будет невозможно поддерживать, продукт будет небезопасен.

    Ищите техлида, который и вас сможет научить, и команду собрать.
    Ответ написан
  • Где найти сформированную команду для "выстрелившего" стартапа?

    copist
    @copist
    Empower people to give
    Год назад был такой же вопрос - "как нанять команду стартапа, если founder не может оценить уровень кандидатов". Я ответил на тот вопрос и нечаянно оказался в довольно интересном проекте GetDayOffer - подбор ресторана по личным предпочте.... Сначала как рекрутер, затем как архитектор.

    Команду в штат целиком нанять нельзя. Я встречался с целой командой из шести человек, которая ушла в стартап. Но это единичный случай. Обычно приходится каждого подбирать по одиночке. Это долго и дорого.

    Первым делом нужен будет Team Lead с навыками найма. Или же ведущий разработчик + специалист по кадрам. Задача - создать объявления о ваканиях, провести собеседования, набрать нужный штат, распределить работу по команде или командам.

    Можно взять стороннюю студию на ауторс. Такие есть, я встречал много. Придётся чуть переплатить, но есть и экономия - если разработка закончилась или приостановилась, то оплачивать работу команды не придётся. Это полезно когда MVP создан и нужно заняться маркетингом.
    Ответ написан
    Комментировать
  • Как создать поддомены для страниц?

    copist
    @copist
    Empower people to give
    В DNS создаётся запись вида
    * IN CNAME domain.com.

    В конфиге веб-сервера указывается, что ему нужно обрабатывать несколько доменов
    # nginx
    http {
        server {
            listen       80;
            # слушать несколько доменов
            server_name  *.domain.com  domain.com;
    
            # ... остальная часть конфига
        }
    }


    В коде приложения PHP анализируется $_SERVER['SERVER_NAME']

    Пример зависимости базы данных и шаблона от домена описан тут Как проектировать систему для отображения общего контента на разных субдоменах / доменах?
    Ответ написан
    Комментировать
  • Как сделать шаблонизатор на php?

    copist
    @copist
    Empower people to give
    Сначала - зачем нужен шаблонизатор. Тут подмена понятий. Скорее вопрос такой: как отделить вывод от заголовков. Если не отделять, то может быть такая ошибка

    <html>
    <body>
    <?php
    // начать сессию
    session_start(); // отправить куку PHPSESSID через заголовки HTTP
    // но она не может отправиться, потому что уже начался вывод HTML в строке "<html ..."
    
    // если не авторизован, то отправить на страницу логина
    if (empty($_SESSION['username']))
        header('Location: /login.php'); // но заголовок HTTP тоже не может отправиться, как и кука
    ?>
        <h1>Hello, <?php echo $_SESSION['username'] ?></h1>
    </body>
    </html>


    Проблема решается, если вывод HTML делать после вывода заголовков. Например, использовать буфер
    <?php ob_start(); // открыть буфер ?>
    <html>
    <body>
    <?php
    session_start(); //  кука PHPSESSID отправится, потому что HTML ещё в буфере
    if (empty($_SESSION['username']))
        header('Location: /login.php'); // заголовок HTTP отправится, потому что HTML ещё в буфере
    ?>
        <h1>Hello, <?php echo $_SESSION['username'] ?></h1>
    </body>
    </html>
    <?php ob_end_flush(); // выбросить содержимое буфера наружу и закрыть его ?>


    Однако так придётся писать в каждом месте, где формируется HTML. Можно ли сократить?

    Простейшее представление через буфер

    <?php
    function render($viewPath)
    {
        if (!is_file($viewPath))
            return 'View "'. $viewPath . '" not exists';
        ob_start();
        include($viewPath);
        return ob_get_clean();
    }
    
    session_start();
    if (empty($_SESSION['username']))
        header('Location: /login.php');
    
    $viewsPath = __DIR__.'/views/'; // где лежат представления
    render($viewsPath . 'page.php'); // нарисовать страницу HTML


    <?php
    # page page.php
    ?>
    <html>
    <body>
        <h1>Hello, <?php echo $_SESSION['username'] ?></h1>
    </body>
    </html>


    А дополнительно ещё решают проблему отделения логики от формирования интерфейса. Работа с внешними данными в одном месте, а отображение их - в другом. Для этого в представление передают всё, что нужно показать. И точка. Лишних данных там не надо. В некоторых фреймворках стоит Exception если представление начнёт работать с базой данных, читает данные из входного запроса или пытается отправить заголовки.

    <?php
    function render($viewPath, $vars)
    {
        if (!is_file($viewPath))
            return 'View "'. $viewPath . '" not exists';
        extract($vars); // extract делает из массива набор переменных в локальной области видимости
        ob_start();
        include($viewPath); // эти переменные будут видны внутри подключаемого файла
        return ob_get_clean();
    }
    
    session_start();
    if (empty($_SESSION['username']))
        header('Location: /login.php');
    
    $viewsPath = __DIR__.'/views/'; // где лежат представления
    render($viewsPath . 'page.php', array( // отображаемые данные передаются массивом
        'username' => $_SESSION['username'],
    ));


    # page.php
    <?php
    /**
     * Подсказки для IDE, чтобы не подсвечивал переменные как неопределённые
     * @var string $username
     */
    <html>
    <body>
        <h1>Hello, <?php echo $sername ?></h1>
    </body>
    </html>


    Не правда ли, с представлениями код становится значительно изящнее. А если логика для реализации представлений хранится в отдельном файле-библиотеке, то код становится короче и понятнее.

    В фреймворках вместо функции render может использоваться объектная реализация

    <?php
    class ViewException extends Exception {}
    
    class View
    {
        public $viewsPath = __DIR__.'/views/';
        
        public function __construct($viewsPath = null)
        {
             // настройка представлений
            // например, можно перепределить место хранения представлений
            if (!is_null($viewsPath))
                $this->viewsPath = $viewsPath;
        }
    
        public function render($viewPath, $vars)
        {
            if (!is_file($this->viewsPath . $viewPath))
                throw new ViewException('View "'. $viewPath . '" in folder "'. $this->viewsPath . '" not exists');
            extract($vars);
            ob_start();
            include($this->viewsPath . $viewPath);
            return ob_get_clean();
        }
    }
    
    $view = new View();
    $view->render('page.php', array(
        'username' => $_SESSION['username']
    ));


    Как таблицы шаблонизаирова если в одной 3 столбца, в другой 5?

    Никто в представлениях не запрещает использовать языковые конструкции. Это могут быть конструкции языка PHP или какой-нибудь другой язык, специально написанный для шаблонизатора. Например, в Smarty, Blade, Pug свои языки. Передай в представление количество колонок и сделай цикл :)

    у первой первый столбец должен быть 70% а у второй последний?

    В представлениях можно подключать стили CSS, через которые меняется отображение (колонка 70%).
    У каждого представления может быть свои стили.
    Если нужно, чтобы были некоторые общие стили и дополнительные, нужные только для этой страницы, используют что-то типа буферизирования вывода блока стилей.

    <?php
    class Assets
    {
        public static $cssLinks = array();
        public static $css = array();
    
        // добавить ссылку на файл стилей
        public function addCssLink($link)
        {
            self::$cssLinks[$link] = $link;
        }
    
        // добавить блок стилей
        public function addCss($name, $css) // $name - это чтобы не дублировались блоки стилей, на всякий случай
        {
            self::$css[$name] = $css;
        }
    
        public function renderCss()
        {
            foreach(self::$cssLinks as $url)
                echo '<link href="'.$url.'" rel="stylesheet" type="text/css" />';
            echo '<style type="text/css">';
            foreach(self::$css as $css)
                echo $css;
            echo '</style>';
        }
    }


    # index.php
    
    $assets = new Assets();
    // общие стили
    $assets->addCssLink('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css');
    
    // ...
    
    $view = new View();
    $view->render('page.php', array(
        'username' => $_SESSION['username']
    ));


    # page.php
    <?php
    /**
     * @var string $username
     */
    $assets = new Assets();
    $assets->addCssLink('/path/to/my/styles.css'); // ещё один файл стилей
    $assets->addCss('h1 { color: red; }'); // или даже что-нибудь микроскопическое, только для этой страницы
    ?>
    <html>
    <head>
    <?php $assets->renderCss() ?>
    </head>
    <body>
        <h1>Hello, <?php echo $username ?></h1>
    </body>
    </html>


    И аналогично про JS.
    Ответ написан
    7 комментариев
  • Существует ли курс по обучению разработке чат ботов?

    copist
    @copist
    Empower people to give
    Гугль, поиск "Python chatbot telegram" либо статьи, либо видео.
    Ищи те, что более свежие по дате.

    текст на русском https://www.gitbook.com/book/groosha/telegram-bot-... автор groosha (собака) protonmail.com - свяжись с ним, если что-то не понятно
    видео-тьюториал на русском https://www.youtube.com/watch?v=iMBuy0INnHQ&list=P...
    стрим на английском https://www.youtube.com/watch?v=fXzbf0k4JFI
    Ответ написан
    Комментировать
  • Как получить название категории: 2 базы данных, в одной (товары) id категории, в другой (категории) id категории и ее название...?

    copist
    @copist
    Empower people to give
    Насколько я понимаю картинку, база одна, `work` называется.
    Нужно запрос на объединение данных из двух таблиц

    SELECT `product`.*, `category`.`name`
    FROM `product`
    JOIN `category` ON `category`.`id` = `product`.`category_id`

    или
    SELECT `product`.*, `category`.`name`
    FROM `product`, `category`
    WHERE `category`.`id` = `product`.`category_id`
    Ответ написан
    3 комментария
  • Нужен ли Guzzle небольшим проектам?

    copist
    @copist
    Empower people to give
    Guzzle - это монстр, конечно.

    Иногда хватает объектной обёртки над curl. Таймуты, заголовки... Я для себя написал библиотеку https://github.com/pvolyntsev/http-requests и уже лет 6 ей пользуюсь, ещё до публикации github пользовался.

    Ещё есть библиотека Requests
    Ответ написан
    Комментировать
  • Как использовать общий код приложений в микросервисах?

    copist
    @copist
    Empower people to give
    Если коротко, то архитектурный стиль микросервисов — это подход, при котором единое приложение строится как набор небольших сервисов, каждый из которых работает в собственном процессе и коммуницирует с остальными используя легковесные механизмы, как правило HTTP. Эти сервисы построены вокруг бизнес-потребностей и развертываются независимо с использованием полностью автоматизированной среды. Существует абсолютный минимум централизованного управления этими сервисами. Сами по себе эти сервисы могут быть написаны на разных языках и использовать разные технологии хранения данных.

    https://habrahabr.ru/post/249183/

    276b6976199983e0329e7a45e84bbc03.png

    В общем случае у них разные базы данных. Потому что там не-смежные данные. Если данные нужно хранить в одном месте из-за отношений - объединяй микросервисы. Если данные нужны для сложных запросов - делай промежуточный сервис агрегации (data aggregation), хранилище данных (data warehouse) или сервис консолидированных отчётов (reporting service) - в общем место, куда сливается инфа из микробаз и соединяется (https://www.quora.com/How-is-reporting-implemented...

    Реализовать можно и одним приложением (один репозиторий) с 20 точками входа и 20 приложений (20 репозиториев). От архитектора зависит. Как ему удобнее. Как команде удобнее.
    Ответ написан
    1 комментарий
  • Интернет-магазин на Falcon и VueJS?

    copist
    @copist
    Empower people to give
    Описанная тобой схема, при которой приложение разбито на две части: клиентское на JS и серверное, которые обмениваются данными через открытое API по HTTP - называется Rich Internet Application или Single Page Application. Реализуется на любом стеке. PHP/Python/NodeJS/Ruby/Go/C#/Java и др. с одной стороны и Vue/Angular/Meteor/React и др. (тыщи их) с другой стороны.

    (Упомянуя схема "микросервисная архитектура" по сути декомпозиция серверной части на незаввисимые модули с открытым API, совсем не обязательно реализовано через HTTP. Частный случай SPA/RIA.)

    Проблему назову одну. Только она не даёт покоя. Она выматывает душу, нервы и кошелёк.

    Интернет магазин должен быть открыт для индексации поисковым ботам, а HTML генерится в runtime на JavsScript. Только Google умеет выполнять JS, и то весьма посредственно. Остальные вообще JS не трогают. Есть два решения:
    для индексации сразу рисовать HTML на стороне сервера
    или снимать "отпечатки" HTML c приложения через виртуальный браузер, что сбоит

    Отрисовка HTML на стороне сервера (server side render) может быть реализована тремя способами:
    * подменять выдачу через серверный язык программирования, то есть вместо шаблонизации в Vue рисовать в Falcon - блин, две шаблонизации, две логики работы с данными (через AJAX и напрямую из базы)
    * имитировать исполнение JS на сервере (хм, это возможно опять же несколькими способами) - тут вообще танцы с бубном
    * отказаться от PHP/Python/Ruby и др. в пользу NodeJS и изоморфного фрейморка, например MeteorJS или VueJS (Nuxt)

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

    P.S. Про Google: проверено, глючит в тех местах, где клиентский JS начинает подкачку данных через HTTP - гугль обрывает рендеринг и в поисковом индексе лежат пустые HTML страницы. Толку от них никакого.
    P.P.S Снятие "отпечатков" HTML для SPA можно через специальные сервисы (prerender.io или brombone.com) или сделать самостоятельно, например через PhantomJS или Electron. В любом случае для проекта с десятком тысяч страниц это расходы на оплату сервиса, либо на мощный сервер. И электрон и фантом виснут, а HTML довольно большие и со временем забивают диск/базу. Опят же надо не забывать про то, что страницы требуют подгрузки данных через AJAX, надо чуть подождать.
    Пример: web-mastery-gauge.ru сделан на Angular, для поисковиков HTML отрисовывается через prerender.io - для проекта с 15 страницами это вообще никакой сложности не вызывает.
    P.P.P.S. SPA просто идеально для реализации той части пользовательского интерфейса, которая не индексируется поисковыми ботами. Например, то доступно только авторизованным пользователям. В этом случае не требуется server side render и 75% проблем отпадают. В том же интернет-магазине может быть админка - её можно сделать на VueJS.
    Ответ написан
    6 комментариев
  • Как составить портфолио?

    copist
    @copist
    Empower people to give
    https://copist.ru/ru/blog/2016/02/19/web-portfolio/ Подходящий опыт (Relevant experience)

    Очень важно подтвердить технические навыки. Иногда это бывает чрезвычайно сложно, например, по причине подписанного соглашения о неразглашении (NDA). В этом случае ты не сможешь вставить результаты своей работы в портфолио и, как ни удивительно, ничем не отличаешься от начинающего программиста c таким же пустым портфолио.

    Рекомендую в портфолио указать места работы и список полученных навыков (достижения и стек технологий лучше пропустить), на "pet project" сильно не заморачиваться (его детально никогда не проверяют - нужен факт владения системами контроля версий и по хорошему репа должна иметь более одного контрибутора или массу подписчиков, человек 10-20), наряду с опытом работы указать любые тестовые работы выполненные недавно (если тестовые задания тоже не под NDA, это же твои работы, твой опыт).

    Можно вместо своего проекта принять участие в Open Source, сделать около 10 изменений через pull-request или стать основным контрибутором, чтобы принять чужие pull-request.
    Вот есть запрос, который случайным образом выбирает свежайшие открытые issue в публичных репозиториях для языка Java на GitHub

    https://github.com/search?o=desc&q=type%3Aissue+is...

    (синтаксис поисковых запросов)
    Ответ написан
  • Доступ к ресурсам REST API?

    copist
    @copist
    Empower people to give
    Вы зря тип транспорта "зашили" в URL, от этого и путаница.

    Классический REST API оперирует сущностями. У вас сущности "производитель", "типы транспортных средств", "транспортные средства производителя", "типы транспортных средств производителя" и "транспортное средство"

    GET /api/transport-types список типов транспортных средств
    GET /api/transport-type/[id] один конкретный тип транспортных средств
    GET /api/manufacturers список производителей
    GET /api/manufacturer/[id] описание производителя (название, адрес)
    GET /api/manufacturer/[id]/transport-types список типов транспортных средств производителя
    GET /api/manufacturer/[id]/transport-type/[id] описание типа транспортных средств производителя
    GET /api/manufacturer/[id]/transports список транспортных средств производителя
    GET /api/manufacturer/[id]/transport/[id] описание одного транспортного средства производителя
    GET /api/manufacturer/[id]/transport/[id] описание одного транспортного средства производителя

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

    Это приблизительно, только для получения информации
    Ещё отдельно для добавления, обновления, удаления

    Можно отойти от концепции REST и взять RPC. Тогда в ответ можно возвращать сколько угодно информации в каком угодно виде.

    GET /api/get-transport-type-of-manufacturer?manufacturer_id=[id]&transport-type_id=[id] вернуть описание типа транспортных средств производителя
    Ответ написан
    Комментировать