Язык html: возможности и ограничения

Небольшой тест

Вопрос 1

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

Ответ

Правильно.

Ответ

Неправильно – имена переменных не должны начинаться с подчеркивания.

Ответ

Неправильно — имена переменных должны начинаться со строчной буквы.

Ответ

Недопустимо – имена переменных не могут содержать пробелов.

Ответ

Неправильно – имена переменных должны начинаться со строчной буквы.

Ответ

Недопустимо – – это ключевое слово.

Ответ

Правильно.

Ответ

Недопустимо – имена переменных не могут начинаться с цифры.

Ответ

Правильно.

Запуск программы

В оперативной памяти процесса находятся код и данные, загруженные из файла. При запуске программы из командной строки, обычно создается новый процесс и в его память загружается файл с программой. Загрузка файла делается вызовом одной из функций семейства exec (см. ). Функции отличаются способом передачи параметров, а также тем, используется ли переменная окружения PATH для поиска исполняемого файла. Например execl в качестве первого параметра принимает имя исполняемого файла, вторым и последующими – строки аргументы, передаваемые в argv[], и, наконец, последний параметр должен быть NULL, он дает процедуре возможность определить, что параметров больше нет.

Пример с двумя ошибками:

Ошибка 1:
Первый аргумент передаваемый программе это имя самой программы. В данном примере в списке процессов будет видна программа с именем -l, запущенная без параметров.

Ошибка 2:
Поскольку код из файла будет загружен в текущий процесс, то старый код и данные, в том числе будет затерты. Первый не сработает никогда.

Как добавить таблицу в WordPress с помощью плагина TablePress?

Теперь, когда у вас есть все плагины Top WordPress для вашей рекламы, что еще вам нужно? Вы просто добавите таблицу через плагин. Хотя процедура добавления таблицы через плагин очень проста, я собираюсь продемонстрировать шаги по созданию таблицы через TablePress. Процедура останется такой же, только с небольшими изменениями, не требующими пояснений, с другими модулями таблиц. Эти шаги следующие

ШАГ 1.   После установки и активации вы можете увидеть таблицу в меню панели управления.

ШАГ 2: На панели инструментов перейдите в TablePress → Add New.

ШАГ 3: Вы можете увидеть страницу с запросом подробных сведений о таблице. Заполните имя таблицы, описание, количество строк и столбцов, которые вам нужны. Нажмите Добавить таблицу, как показано ниже.

ШАГ 4: Теперь создается идентификатор таблицы или шорткод, который вы можете использовать на странице, на которой вы хотите отобразить таблицу. Здесь вы можете добавить содержимое таблицы.

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

Теперь нажмите кнопку «Сохранить изменения».

ШАГ 5: Теперь перейдите на страницу, где вы хотите эту таблицу. Вставьте шорткод таблицы и обновите.

ШАГ 6: Результирующая таблица будет выглядеть примерно так на активной странице.

Это очень просто. Вы сделали!!

Как сделать 301 редирект вручную в .htaccess

Один из лучших способов настроить редирект — использовать файл .htaccess в корневой папке сайта.

Это простой и быстрый способ, который не создает запросов к базе данных в отличие от плагина. Это микроскопически отразится на скорости вашего сайта.

Этот способ работает только на серверах Apache.

Редирект страницы

Чтобы создать 301 редирект одной страницы, добавьте этот код в .htaccess:

Замените и на ваши URL. Страницы могут иметь расширения *.html, *.php и другие, или не иметь расширений, как в этом примере.

Редирект страницы на другой домен

Чтобы добавить 301 редирект одной страницы на другой домен, добавьте этот код в .htaccess:

У страниц могут быть расширения *.html, *.php или другие

Обратите внимание на http / https

Редирект сайта на новый домен

Чтобы сделать редирект всего сайта, добавьте этот код в .htaccess:

С этим способом все ссылки на вашем сайте останутся такими же, то есть страница станет

Обратите внимание на http / https

Редирект на www-версию сайта

Если вы хотите, чтобы ваши посетители приходили только на www-версию сайта, добавьте это правило в .htaccess:

Обратите внимание на http / https

Переопределение поведения сериализации и десериализации¶

Если вам нужно изменить поведение сериализации или десериализации класса сериализатора, вы можете сделать это, переопределив методы или .

Некоторые причины, по которым это может быть полезно, включают…

  • Добавление нового поведения для новых базовых классов сериализаторов.

  • Небольшое изменение поведения существующего класса.

  • Улучшение производительности сериализации для часто используемой конечной точки API, которая возвращает много данных.

Подписи для этих методов следующие:

Принимает экземпляр объекта, который требует сериализации, и должен вернуть примитивное представление. Обычно это означает возврат структуры встроенных в Python типов данных. Точные типы, которые могут быть обработаны, зависят от классов рендеринга, которые вы настроили для своего API.

Может быть переопределена для изменения стиля представления. Например:

def to_representation(self, instance):
    """Convert `username` to lowercase."""
    ret = super().to_representation(instance)
    ret'username' = ret'username'.lower()
    return ret

Reviews and Rating

Перейти к плагину

У Reviews and Rating более узкая специализация. Этот плагин оценки статей WordPress рассчитан на владельцев коммерческих ресурсов, использующих для продвижения компании инструментарий Google My Business (GMB) в целом и систему отзывов на картах и в других сервисах Google в частности. Reviews and Rating собирает данные по вашему уникальному Google Place ID и выводит их в виде отформатированного списка, колонок или блоков отзывов на сайте WordPress. Также плагин дает возможность посетителям оценивать контент вашего ресурса в Google.

Reviews and Rating, краткая справка:

Особенности Reviews and Rating:

  • шорткод и виджет для потребительских отзывов/рейтингов ;
  • 5 дополнительных шорткодов;
  • 96 настраиваемых тем;
  • высокий уровень кастомизации, большое количество опций настройки;
  • демонстрационный режим без использования Google API;
  • «живой» предварительный просмотр в консоли администрирования;
  • поддержка Structured Data (Schema.org);
  • на 100% респонсивный дизайн;
  • подробная документация.

Плагин полностью функционален в базовой версии «из коробки». Нет никаких премиальных подписок и скрытых платежей. Для корректной работы расширения вам потребуется ключ Google API и респонсивны. Также крайне рекомендуется создать профиль в биллинговом облачном сервисе Google. Расширение Reviews and Rating получило максимально возможную оценку в официальном рейтинге WP, 5.0 баллов, и большое количество положительных отзывов от владельцев коммерческих ресурсов на WordPress.  

Элементы функционального подохда к программированию

При написании
программ приветствуется такой подход, который называется функциональным
программированием
. Продемонстрирую его на следующем примере. Предположим,
нам нужна функция, которая находит максимальное значение из двух чисел:

def max2(a, b):
    if a > b:
        return a
    return b

И вызвать мы ее
можем так:

print( max2(2, -3) )

Затем, нам
потребовалась функция, которая бы находила максимальное из трех чисел. Как ее
можно реализовать? Используя идею функционального программирования, это можно
сделать следующим образом:

def max3(a, b, c):
    return max2(a, max2(b, c))

И вызвать так:

print( max3(2, -3, 5) )

Смотрите, здесь
оператор return возвращает
значение, которое возвращает функция max2. Но, прежде чем она будет
выполнена, вызовется другая функция max2, которая определит максимальное
среди чисел b и c. То есть,
прежде чем вызвать первую функцию max2 необходимо вычислить ее
параметры: первый просто берется их x, а второй вычисляется вложенной
функцией max2. Вот так это
работает и вот что из себя представляет элемент функционального подхода к
программированию.

Причем,
благодаря гибкости языка Python, мы можем вызвать эту функцию и
для нахождения максимальной строки:

print( max3("ab", "cd", "abc") )

так как строки
могут спокойно сравниваться между собой. И вообще, любые величины, которые
можно сравнивать на больше и меньше, можно подставлять в качестве аргументов
функции
max3 и max2.

Ключевые слова

C++ резервирует набор из 92 слов (по состоянию на C++20) для собственного использования. Эти слова называются ключевыми словами (keywords, или зарезервированными словами), и каждое из этих ключевых слов имеет в языке C++ особое значение.

Ниже приведен список всех ключевых слов C++ (до C++20):

Ключевые слова, отмеченные (C++20), были добавлены в C++20. Если ваш компилятор не совместим с C++20 (или имеет функциональные возможности C++20, но по умолчанию они отключены), эти ключевые слова могут не работать.

C++ также определяет специальные идентификаторы: , , и . Они имеют особое значение при использовании в определенных контекстах, но не зарезервированы.

Вы уже встречали некоторые из этих ключевых слов, включая и . Эти ключевые слова и специальные идентификаторы вместе с набором операторов определяют весь язык C++ (за исключением команд препроцессора). Поскольку ключевые слова и специальные идентификаторы имеют особое значение, ваши IDE, скорее всего, изменят цвет текста этих слов (часто на синий), чтобы выделить их среди других идентификаторов.

К тому времени, когда вы пройдете эту серию обучающих статей, вы поймете, что делают почти все эти слова!

Ninja Kick: WordPress Contact Form Plugin

Ninja Kick: WordPress Contact Form Plugin дает дополнительный толчок вашим формам WordPress.

Вы можете использовать ее как самостоятельную контактную форму или в соединении с популярными плагинами форм, например, такими как Contact Form 7, Gravity Forms, и др.

Пользователи сайта могут немедленно вызвать контактную форму без необходимости ждать, пока загрузится страница.

Ее особенности:

  • встроенный MailChimp
  • адаптивный дизайн
  • 30 встроенных фонов
  • палитра выбора цвета для простой стилизации
  • поддерживается на мобильных устройствах
  • и многое др.

Этот плагин форми не поддерживает WPML, но следует отметить, что RTL нужно настроить для коректной работы с Google Chrome.

Ninja Kick: WordPress Contact Form Plugin — это стильный, быстрозагружающийся плагин, который отлично работает как форма контактов, или для оформления рассылки новостей, или более даже сложные формы, которые можно разработать, используя более продвинутые плагины форм WordPress.

PID

Каждый процесс имеет уникальный на данный момент времени идентификатор PID. Поменять PID процесса невозможно.

Значения PID 0 и 1 зарезервированы. Процесс с PID==0 не используется, PID==1 — принадлежит программе .

Максимальное значение PID в Linux равняется PID_MAX-1. Текущее значение PID_MAX можно посмотреть командой:

По умолчанию это 2^16 (32768) однако в 64-разрядных Linux его можно увеличить до 2^22 (4194304):

назначаются последовательно. При создании нового процесса вызовом ищется , больший по значению, чем тот, который был возвращён предыдущим вызовом . Если при поиске достигнуто значение , то поиск продолжается с PID=2. Такое поведение выбрано потому, что некоторые программы могут проверять завершение процесса по существованию его PID. В этой ситуации желательно, чтобы PID не использовался некоторое время после завершения процесса.

Ограничения дженериков#

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

functionloggingIdentity<Type>(argType)Type{

console.log(arg.length)

return arg

}

Copy

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

Нам необходимо создать интерфейс, описывающий ограничение. В следующем примере мы создаем интерфейс с единственным свойством и используем его с помощью ключевого слова для применения органичения:

interfaceLengthwise{

lengthnumber

}

functionloggingIdentity<TypeextendsLengthwise>(argType)Type{

console.log(arg.length)

return arg

}

Copy

Поскольку дженерик был ограничен, он больше не может работать с любым типом:

loggingIdentity(3)

Copy

Мы должны передавать ему значения, отвечающие всем установленным требованиям:

loggingIdentity({ length10, value3})

Copy

UID и GID

С процессом связано понятие «владельца» и «группы», определяющие права доступа процесса к другим процессам и файлам в файловой системе. «Владелец» и «группа», это числовые идентификатор UID и GID, являющийся атрибутами процесса. В отличие от файла, процесс может принадлежать нескольким группам одновременно. Пользователь в диалоговом сеансе имеет право на доступ к своим файлам поскольку диалоговая программа (shell), которую он использует, выполняется в процессе с тем же UIDом, что и UID, указанный в атрибутах файлов.

Процесс может поменять своего владельца и группу в двух случаях:

NEX-Forms — The Ultimate WordPress Form Builder

NEX-Forms — The Ultimate WordPress Form Builder — это именно то.

Он максимальный.

На первый взгляд вы увидите функции, которые вы ожидаете:

  • математическая логика
  • drag and drop
  • полностью адаптивный
  • условная логика
  • пошаговые формы
  • автоответчик электронной почты
  • и т.д….

Но когда вы «копнете немного глубже», тогда увидите, что он назван «максимальным» не ради рекламы.

Более заметные особенности включают:

  • всплывающие формы
  • больше 660 векторных иконок
  • более 50 элементов форм
  • более 1200 шрифтов Google
  • невероятная интеграция шрифтов
  • и даже больше

Наконец, это система макетов с сеткой — NEX-Forms — The Ultimate WordPress Form Builder, которая предоставляет вам широкий контроль над шаблоном форми с использованием функции drag and drop.

Usernoise Pro Modal Feedback & Contact Form

С помощью этой модальной форми обратной связи собирать отзывы от пользователей и клиентов просто и быстро.

The Usernoise Pro Modal Feedback & Contact Form имеет отличный минималистичный стиль и высокий уровень конфигурации.

Вы можете:

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

И она интегрируется с Akismet для обработки спама.

Usernoise Pro Modal Feedback & Contact Form лишен множества «рюшечек», все же это мощный способ соединения и сбора сообщений обратной связи.

Внутренние типы манипуляций со строками (intrisic string manipulation types)#

предоставляет несколько типов, которые могут использоваться при работе со строками. Эти типы являются встроенными и находятся в файлах , создаваемых .

Uppercase — переводит каждый символ строки в верхний регистр

typeGreeting=’Hello, world’

typeShoutyGreeting=Uppercase<Greeting>

typeASCIICacheKey<Strextendsstring>=`ID-${Uppercase<Str>}`

typeMainID=ASCIICacheKey<‘my_app’>

Copy

Lowercase — переводит каждый символ в строке в нижний регистр

typeGreeting=’Hello, world’

typeQuietGreeting=Lowercase<Greeting>

typeASCIICacheKey<Strextendsstring>=`id-${Lowercase<Str>}`

typeMainID=ASCIICacheKey<‘MY_APP’>

Copy

Capitilize — переводит первый символ строки в верхний регистр

typeLowercaseGreeting=’hello, world’

typeGreeting=Capitalize<LowercaseGreeting>

Copy

Uncapitilize — переводит первый символ строки в нижний регистр

typeUppercaseGreeting=’HELLO WORLD’

typeUncomfortableGreeting=Uncapitalize<UppercaseGreeting>

Copy

Вот как эти типы реализованы:

functionapplyStringMapping(symbolSymbol, strstring){

switch(intrinsicTypeKinds.get(symbol.escapedNameasstring)){

caseIntrinsicTypeKind.Uppercase

return str.toUpperCase()

caseIntrinsicTypeKind.Lowercase

return str.toLowerCase()

caseIntrinsicTypeKind.Capitalize

return str.charAt().toUpperCase()+ str.slice(1)

caseIntrinsicTypeKind.Uncapitalize

return str.charAt().toLowerCase()+ str.slice(1)

}

return str

}

Copy

Ссылки и объекты

Представление переменных в языке Python сильно отличается от традиционных
языков программирования: Pascal, C, Java. Любая сущность, с которой работает
Python-программа, является объектом. Числа, строки, списки, множества
являются объектами. Названия функций также являются объектами и в некотором
смысле ничем не отличаются от чисел, кроме того, что у функций есть операции,
нетипичные для чисел (например, вызов функции при помощи операции
), а другие операции, наоборот, отсутствуют.

У каждого объекта есть свой тип (это также называется классом):
, , ,
и т.д.

Переменные в свою очередь являются ссылками на объекты:
в любой момент времени переменная связана с каким-то объектом
в памяти. Если мы выполняем присваивание:

a = 5

то имя переменной связывается с объектом типа
, в котором хранится значение 5.

Если теперь выполнить другое присваивание:

a = 'hello'

то старая связь разрывается, создается новый объект типа
, в котором хранится значение ,
и переменная связывается с этим объектом.

Если же теперь выполнить операцию:

def a():
    pass

то в памяти создается объект типа ,
хранящий тело функции из одной инструкции ,
и переменная теперь связывается с этой функцией.

Если же справа от оператора поставить
имя другого объекта:

b = a

то второй объект связывается с тем же объектом, с которым
был связан первый объект: теперь и
становятся ссылками на один и тот же объект и являются неразличимыми,
пока для одного из них связь не будет разорвана при помощи оператора

Интересным свойством является то, что переменные могут ссылаться
на один и тот же объект. Проверить, ссылаются ли две переменные
на один объект или на разные можно при помощи оператора
, возвращающего значение ,
если ссылки совпадают:

a is b

При этом переменные могут ссылаться на различные, но равные
объекты, то есть оператов для них вернет
. Пример различных, но равных объектов:

a = 'a' * 1000
b = 'aa' * 500

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

a = 2 + 3
b = 1 + 4

При выполнении второго оператора , интерпретатор
обнаружит, что в памяти уже есть объект со значением
и не будет создавать другой объект с таким же значением.

Такое повторное использование применяется только для небольших чисел,
строк и объектов типа , поскольку для больших объектов
проверка того, что объект уже хранится в памяти требует много времени.

Валидация¶

При десериализации данных всегда нужно вызывать перед попыткой получить доступ к проверенным данным или сохранить экземпляр объекта. Если возникнут ошибки валидации, свойство будет содержать словарь, представляющий сообщения об ошибках. Например:

serializer = CommentSerializer(data={'email' 'foobar', 'content' 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': , 'created': }

Каждый ключ в словаре будет именем поля, а значения будут списками строк любых сообщений об ошибках, соответствующих этому полю. Также может присутствовать ключ , который будет перечислять любые общие ошибки валидации. Имя ключа может быть настроено с помощью параметра REST framework.

При десериализации списка элементов ошибки будут возвращены в виде списка словарей, представляющих каждый из десериализованных элементов.

Возникновение исключения при недопустимых данных

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

Эти исключения автоматически обрабатываются обработчиком исключений по умолчанию, который предоставляет фреймворк REST, и по умолчанию возвращают ответы .

# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

Валидация на полевом уровне

Вы можете задать пользовательскую валидацию на уровне полей, добавив методы в ваш подкласс . Они аналогичны методам в формах Django.

Эти методы принимают единственный аргумент, который является значением поля, требующего проверки.

Ваши методы должны возвращать подтвержденное значение или вызывать ошибку . Например:

from rest_framework import serializers

class BlogPostSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    content = serializers.CharField()

    def validate_title(self, value):
        """
        Check that the blog post is about Django.
        """
        if 'django' not in value.lower():
            raise serializers.ValidationError("Blog post is not about Django")
        return value

Примечание: Если ваше объявлено в вашем сериализаторе с параметром , то этот шаг проверки не будет выполняться, если поле не включено.

Валидация на уровне объекта

Чтобы выполнить любую другую проверку, требующую доступа к нескольким полям, добавьте метод под названием в ваш подкласс . Этот метод принимает единственный аргумент, который является словарем значений полей. При необходимости он должен вызывать сигнал или просто возвращать проверенные значения. Например:

from rest_framework import serializers

class EventSerializer(serializers.Serializer):
    description = serializers.CharField(max_length=100)
    start = serializers.DateTimeField()
    finish = serializers.DateTimeField()

    def validate(self, data):
        """
        Check that start is before finish.
        """
        if data'start' > data'finish']:
            raise serializers.ValidationError("finish must occur after start")
        return data

Встроенные функции

В Питоне есть множество встроенных в (1) стандартный функционал (built-in functions) и (2) дополнительные библиотеки (library functions) функций, и мы много раз их использовали.

Рассмотрим функцию для создания гистограммы plt.hist(). Вначале импортируем библиотеки.

1
2

importmatplotlib.pyplot asplt

importnumpy asnp

Сгенерируем данные, которые передадим этой функции (эти же данные мы создавали и использовали на восьмом занятии вводного курса).

1
2
3
4
5

# установим точку отсчета для воспроизведения такого же результата

np.random.seed(42)

 
# и сгенерируем данные о росте

height=list(np.round(np.random.normal(180,10,1000)))

Теперь построим гистограмму передав ей в качестве параметров и аргументов наши данные и количество интервалов.

1
2

plt.hist(height,bins=10)

plt.show()

Как мы видим, достаточно обратиться к соответствующей библиотеке (
plt), вызвать эту функцию по имени (
hist) и задать параметры и их аргументы (
heightи
bins=10), и будет исполнен тот код, который заложили в нее создатели библиотеки Matplotlib.

Теперь несколько слов про параметры и аргументы функции.

Параметры и аргументы функции

Для начала определимся с терминами:

  • параметр — это то, что запрашивает функция при вызове (например,
    bins, количество интервалов)
  • аргумент — значение этого параметра (в нашем случае,
    10).

Возникает вопрос, что же такое
height? Логично предположить, что это аргумент (ведь это наши данные). Но тогда как функция узнает, какому параметру он соответствует?

Все дело в том, что параметры и их аргументы могут быть позиционными (positional) и именованными (keyword).

В первом случае, достаточно указать аргумент и поставить его в правильном порядке (позиции). Функция сама поймет, какой параметр ей передают. Во втором случае, нужно указать и название параметра, и аргумент.

Позиционный параметр можно сделать именованным, и тогда порядок важен не будет.

1
2
3

# данные в этой функции обозначаются через x

plt.hist(bins=10,x=height)

plt.show()

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

1
2
3
4

# у параметра bins есть аргумент по умолчанию (как раз 10 интервалов)
# а значит, этот параметр можно не указывать

plt.hist(height)

plt.show()

Как вы видите, результат во всех трех случаях совершенно одинаковый.

Если вы сомневаетесь в том, какие параметры принимает функция и что является результатом ее работы, полезно обратиться к документации в Интернете. Например, по функции plt.hist() ее можно найти вот здесь⧉.

Стоит отметить, что функция может как принимать один, два или несколько параметров, так и не принимать их вовсе. Например, функция print(), если не указывать параметры, выдает пустую строку.

1
2
3

print(‘Первая строка’)

print()

print(‘Третья строка’)

1
2
3

Первая строка
 
Третья строка

Функции и методы

Некоторые функции называются методами. Методы — это функции, которые можно применить только к конкретному объекту. Другими словами, если обычная функция будет выполнена «сама по себе», это просто участок кода, которому дали имя, то методу для исполнения нужен объект (например, строка, список или словарь)

При этом, что важно, у каждого объекта свои методы

Предположим, у нас есть строка, и мы хотим сделать первую букву каждого слова заглавной. Для этого у строки есть метод .title().

1
2
3
4
5

# создаем строковый объект и

some_string=’machine learning’

 
# применяем к нему метод .title()

some_string.title()

1 ‘Machine Learning’

А теперь попробуем применить этот метод к списку, состоящему из тех же слов, что и строка.

1
2

some_list=’machine’,’learning’

some_list.title()

Как мы видим, Питон выдал ошибку.

Рейтинг
( Пока оценок нет )
Editor
Editor/ автор статьи

Давно интересуюсь темой. Мне нравится писать о том, в чём разбираюсь.

Понравилась статья? Поделиться с друзьями:
Люкс-хост
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: