WordPress установить CORS

10

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

/**
 * COORS
 */

add_filter('allowed_http_origins', 'add_allowed_origins');
function add_allowed_origins($origins)
{
    $origins[] = 'https://yourdomain.com';

    return $origins;
}
0
 

WordPress Carbon fields пофиксить кнопку «Выбрать изображениe»

17

Есть небольшая проблема с руссифицированным отображением поля картинки. Например:

Как видно кнопка выбрать изображение не совсем корректно помещается в поле. Конечно далее логичным шагом будет расширить поле по ширине.
Field::make('image', 'some-image', 'Изображение')
    ->set_width(100),
Мы видим, что поле расширилось. Но внутренний контейнер остался таким же. Не могу сказать с чем это связано. Но логично, что эта область например должна растянуться по ширине поля.
Немного пофиксим CSS. CSS класс называется .cf-file__inner. Он изолирован и будет относиться к изображениям и файлам, что будет так же актуально.

Добавим кастомный CSS для админки.

function my_admin_style() {
    echo  get_stylesheet_directory_uri().'/assets/css/myadmin.css';
    wp_enqueue_style('admin-styles', get_stylesheet_directory_uri().'/assets/css/myadmin.css');
}
add_action('admin_enqueue_scripts', 'my_admin_style');

И также код css.

.cf-file__inner{
    width: 100% !important;
}

Получаем результат.

0
 

Python запись traceback в текстовое поле

32

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

import traceback
import logging
from .models import CustomLogger

try:
    1/0 #error
except Exception as e:
    logging.error(traceback.format_exc())
    CustomLogger.log(str(traceback.format_exc()))
    
0
 

Django сменить темную тему

25

После установки приложения мы можете неожиданно обнаружить темную тему админки. Это немного странно. Но версии 3.2 есть такая фича.

The admin now supports theming, and includes a dark theme that is enabled according to browser settings. See Theming support.

https://docs.djangoproject.com/en/3.2/releases/3.2/#minor-features

Чтобы активировать светлую стандартную тему можно сделать следущее.
Создаем файл в директории с темплейтами админки templates/admin/base.html

Код файла

{% extends 'admin/base.html' %}

{% block extrahead %}{{ block.super }}
<style>
/* VARIABLE DEFINITIONS */
:root {
  --primary: #79aec8;
  --secondary: #417690;
  --accent: #f5dd5d;
  --primary-fg: #fff;

  --body-fg: #333;
  --body-bg: #fff;
  --body-quiet-color: #666;
  --body-loud-color: #000;

  --header-color: #ffc;
  --header-branding-color: var(--accent);
  --header-bg: var(--secondary);
  --header-link-color: var(--primary-fg);

  --breadcrumbs-fg: #c4dce8;
  --breadcrumbs-link-fg: var(--body-bg);
  --breadcrumbs-bg: var(--primary);

  --link-fg: #447e9b;
  --link-hover-color: #036;
  --link-selected-fg: #5b80b2;

  --hairline-color: #e8e8e8;
  --border-color: #ccc;

  --error-fg: #ba2121;

  --message-success-bg: #dfd;
  --message-warning-bg: #ffc;
  --message-error-bg: #ffefef;

  --darkened-bg: #f8f8f8; /* A bit darker than --body-bg */
  --selected-bg: #e4e4e4; /* E.g. selected table cells */
  --selected-row: #ffc;

  --button-fg: #fff;
  --button-bg: var(--primary);
  --button-hover-bg: #609ab6;
  --default-button-bg: var(--secondary);
  --default-button-hover-bg: #205067;
  --close-button-bg: #888; /* Previously #bbb, contrast 1.92 */
  --close-button-hover-bg: #747474;
  --delete-button-bg: #ba2121;
  --delete-button-hover-bg: #a41515;

  --object-tools-fg: var(--button-fg);
  --object-tools-bg: var(--close-button-bg);
  --object-tools-hover-bg: var(--close-button-hover-bg);
}


</style>
{% endblock %}

После этого применятся стандартные стили админки.

0
 

Python сжатие изображения через TinyPng. Сэкономьте гигабайты места на сервере (python tiny png compressing)

97

Возникла задача оптимизации места. Да думаю эта задача типична для всех, кто работает с объемами данных, даже не обязательно большими. Все таки 1-5 гигабайт на сервере только для картинок — это уже весомо.

Решил написать простой скрипт для компрессии и желательно без потери качества. Я думал на каком ЯП это сделать. Php — не хотелось. Node.js — прикольно, много на нем написал, но так лень ставить скобочки ). Поэтому я выбрал python. Да и питон стоит почти на всех серверах рядом с php.

Tiny PNG

Уже лет 5ть. когда еще только начинал программировать я активно использовал отличный сервис TinyPng. Каждый раз захожу и это панда поднимает мне настроение 🙂

У них все понятно и отличная документация дл разных языков.
Для начала надо зарегистрироваться и получить API ключ. Это делается просто и понятно

Скрипт

Для начал заводим виртуальное окружение. Устанавливаем модуль tiny png для python

pip install --upgrade tinify

Я сразу вставлю весь скрипт с понятными комментариями. Я лишь объясню порядок. Сначала естественно ипортируем необходимы библиотеки. Все стандартные для питона 3ей версии.
Дале проставляем переменны api ключа, целевую директорию, и размер картинки начиная с которого производить компрессию.

Есть функции:

tinify_img() — сжатие с помощью библиотеки TinyPng
copy_and_overwrite() — копирования директории для того, чтобы при записи не было ошибок, на директорию которой не существует. Так же это будет удобно посмотреть результат а потом, просто скопировать результат заменив все исходные данные.

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

import tinify
# from os import walk
import os
import imghdr
import shutil
from pathlib import Path

# TinyPNG apikey
tinify.key = "YOU_API_KEY"
# targetdir
root_dir = 'test_folder'
# the maximum size(in bytes) of the image the ones above are compressed
img_size_limit = 1000000  

# tynyfy by tinypng
def tinyfy_img(path):
    source = tinify.from_file(path)
    source.to_file("./tinyfied/" + path)
    print("tiny to " + "./tinyfied/" + path)

# copy dir
def copy_and_overwrite(from_path, to_path):
    if os.path.exists(to_path):
        shutil.rmtree(to_path)
    shutil.copytree(from_path, to_path)
    print(f"dir {from_path} copied to {to_path}")

print("======== PATH RECOURSIVE ==========")

# copy root dir
copy_and_overwrite(root_dir, './tinyfied/' + root_dir)

paths = Path(root_dir).glob('**/*')
for path in paths:
    # because path is object not string
    path_in_str = str(path)
    if os.path.isfile(path):
        file_image_type = imghdr.what(path_in_str)
        if file_image_type != None:
            if (os.path.getsize(path_in_str) > img_size_limit ):
                print(path_in_str)
                tinyfy_img(path_in_str)
                print("============")

print("Tinyfied done 👍👍👍!")

На этом собственно все. Надеюсь гайд был полезен ☺️.
Ссылка на гитхаб https://github.com/alexTitakoff/tinyfy-py

0
 

Django tinymce добавление загрузки картинок в редактор. (django tinymce upload image)

179

При стандартных настройках загрузка картинки не доступна. Редактор предлагает вставить внешний источник. Будь то это сторонний сток или полный путь до картинки на вашем сайте.

Как решить эту проблему.

Изначально скрипт вызова редактора находится app/main/static/django_tinymce/init_tinymce.js. У вас это может быть другой путь. Важно что в файле init_tinymce.js идет вызов редактора и сбор настроек из settings.py.

В моем случае конфиг выглядел так. Есть нюанс — в джанго конфиге нет возможности прописать коллбек-функции для реализации дополнительной обработки.

TINYMCE_DEFAULT_CONFIG = {
    "height": "320px",
    "width": "960px",
    "menubar": "file edit view insert format tools table help",
    "plugins": "media image preview advlist autolink lists link  charmap print preview anchor searchreplace visualblocks code "
               "fullscreen insertdatetime media table paste code help wordcount spellchecker",
    "toolbar": "undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft "
               "aligncenter alignright alignjustify | outdent indent |  numlist bullist checklist | forecolor "
               "backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap emoticons | "
               "fullscreen  preview save print | insertfile image media pageembed template link anchor codesample | "
               "a11ycheck ltr rtl | showcomments addcomment code",
    "custom_undo_redo_levels": 10,
    "language": "ru",  # To force a specific language instead of the Django current language.
    "image_title": "true",
    "image_caption": "true",
    "automatic_uploads": "true",
}

Можно прописать обработку в файле init_tinymce.js. Я не стал так делать. Хотя можете меня поправить. Если нормально, то можно и в нем. На строчке получения конфига перед инициализацией добавляем коллбек.

mce_conf.file_picker_callback = function (cb, value, meta) {
    var input = document.createElement("input");
    input.setAttribute("type", "file");
    if (meta.filetype == "image") {
        input.setAttribute("accept", "image/*");
    }
    if (meta.filetype == "media") {
        input.setAttribute("accept", "video/*");
    }
    input.onchange = function () {
        var file = this.files[0];
        var reader = new FileReader();
        reader.onload = function () {
            var id = "blobid" + (new Date()).getTime();
            var blobCache = tinymce.activeEditor.editorUpload.blobCache;
            var base64 = reader.result.split(",")[1];
            var blobInfo = blobCache.create(id, file, base64);
            blobCache.add(blobInfo);
            cb(blobInfo.blobUri(), {title: file.name});
        };
        reader.readAsDataURL(file);
    };
    input.click();
}
// Вызов редактора
tinyMCE.init(mce_conf);

1.Убираем стандартный вызов

Это был быстрый способ. Я же решил делать это обособлено в дополнительном скрипте.
Поэтому комментируем или удаляем конфиг из settings.py

2. Добавляем js

Как добавлять дополнительный css и js я рассказывал в другой статье. В данном случае подключение выглядит так.

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    class Media:
        css = {
            'all': (
                '/static/admin/css/custom.css',
            )
        }
        js = (
            '/static/admin/js/custom.js',
            )

Сам скрипт. В нем мы убираем редактор из дефолтного вызова tinymce и вызывает новый с нашими настройками. У вас могут быть другие плагины и стиль тулбара эдитора.

if ($('.tinymce').length) {
    tinymce.activeEditor.destroy();
}

tinymce.init({
    elements: "id_content",
    selector: "textarea#id_content",
    height: "700",
    width: "100%", menubar: "file edit view insert format tools table help",
    plugins: "media image preview advlist autolink lists link  charmap print preview anchor searchreplace visualblocks code fullscreen insertdatetime media table paste code help wordcount spellchecker",
    toolbar: "undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent |  numlist bullist checklist | forecolor backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap emoticons | fullscreen  preview save print | insertfile image media pageembed template link anchor codesample | a11ycheck ltr rtl | showcomments addcomment code",
    image_title: true,
    image_caption: true,
    automatic_uploads: true,
    image_advtab: true,
    language: "ru",
    file_picker_types: "image media",
     // обработчик загрузчика файла
    file_picker_callback: function (cb, value, meta) {
        var input = document.createElement("input");
        input.setAttribute("type", "file");
        if (meta.filetype == "image") {
            input.setAttribute("accept", "image/*");
        }
        if (meta.filetype == "media") {
            input.setAttribute("accept", "video/*");
        }
        input.onchange = function () {
            var file = this.files[0];
            var reader = new FileReader();
            reader.onload = function () {
                var id = "blobid" + (new Date()).getTime();
                var blobCache = tinymce.activeEditor.editorUpload.blobCache;
                var base64 = reader.result.split(",")[1];
                var blobInfo = blobCache.create(id, file, base64);
                blobCache.add(blobInfo);
                cb(blobInfo.blobUri(), {title: file.name});
            };
            reader.readAsDataURL(file);
        };
        input.click();
    },
    content_style: "body { font-family:Helvetica,Arial,sans-serif; font-size:14px }"
});

И собственно на этом все.


Примечание: обязательно ставьте курсор на текст иначе будет ошибка при загрузке картинки. Редактор по умолчанию не загружает в папку, а вставляет картинку в BLOB формате

0
 

Django добавление кастомного контента и действий в админку(Django add custom actions and content in admin view)

172

Передо мной стояла задача гибко расширить функционал стандартной админки джанги.

Вьюшка таблицы

Рассмотрим добавление дополнительного действия в лист, на примере пользователей. Стандартный пример — выгрузка пользователей в Exel. Вы видим обычный лист с дефолтными действиями.

Добавим небольшой HTML в шапку. Для этого в admin.py нашего модуля добавим дополнительный класс, который будет переопределять отображение модели в админке.

from django.contrib import admin
from django.contrib.auth.models import User
#Переопределяем вывод модели Юзеров
class CustomAdminUsers(admin.ModelAdmin):
    readonly_fields = ['address_report']
    list_display = ('first_name', 'email', 'is_active', 'date_joined', 'last_login',)
    # определяем дополнительный темплейт
    change_list_template = "admin/user_profile_templ.html"  

admin.site.unregister(User)
admin.site.register(User, CustomAdminUsers)

change_list_template — собственно директива определяющая дополнительный шаблон.
Он находится по пути yourmodule/static/templates/admin

Сам шаблон. В нем форма и указанный экшн.

{% extends 'admin/change_list.html' %}
{% block object-tools %}
<form action="to-exel/" method="POST">
    {% csrf_token %}
    <input class="exel-btn" type="submit" value="Выгрузить  в Exel"/>
</form>
{{ block.super }}
{% endblock %}

Отлично у нас отобразился шаблон.

Теперь нужно обработать экшн а admin.py в нашем классе.

from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib import messages # сообщения джанги


#Переопределяем вывод модели Юзеров
class CustomAdminUsers(admin.ModelAdmin):
    readonly_fields = ['address_report']
    list_display = ('first_name', 'email', 'is_active', 'date_joined', 'last_login',)
    change_list_template = "admin/user_profile_templ.html" # определяем дополнительный темплейт
    
    # Определяем дополнительные урлы (код сам взял из stackoverflow)
    def get_urls(self):
        urls = super(CustomAdminUsers, self).get_urls()
        custom_urls = [url('^to-exel/$', self.to_exel, name='to_exel'), ]
        return custom_urls + urls
    # На обработчик
    def to_exel(self, request):
        # логика экшена
        messages.success(request, f"Пользователи успешно выгружены")
        return HttpResponseRedirect("../")

admin.site.unregister(User)
admin.site.register(User, CustomAdminUsers)

Собственно все для вьюшки листа.

Вьюшка редактирования модели

Теперь выведем дополнительную информацию в самой странице редактирования. Тут еще быстрее и проще. Ставим поле в readonly. И определяем функцию вывода. Либо текст, либо любой html. Соответсвенно можно брать любую информацию из других моделей и преобразовывать в нужный контент.

from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib import messages # сообщения джанги
from django.utils.html import format_html


#Переопределяем вывод модели Юзеров
class CustomAdminUsers(admin.ModelAdmin):
    readonly_fields = ['some_content']
    list_display = ('first_name', 'email', 'is_active', 'date_joined', 'last_login',)
    change_list_template = "admin/user_profile_templ.html" # определяем дополнительный темплейт
    
    # Дополнительный контент
    def some_content(self, instance):
        return format_html("<a href='%s'>%s</a>" % ('somelink.ru', 'Пример ссылки'))

    # Определяем дополнительные урлы (код сам взял из stackoverflow)
    def get_urls(self):
        urls = super(CustomAdminUsers, self).get_urls()
        custom_urls = [url('^to-exel/$', self.to_exel, name='to_exel'), ]
        return custom_urls + urls
    # На обработчик
    def to_exel(self, request):
        # логика экшена
        messages.success(request, f"Пользователи успешно выгружены")
        return HttpResponseRedirect("../")

admin.site.unregister(User)
admin.site.register(User, CustomAdminUsers)

Получаем результат в конце после редактируемых полей.

0
 

Django как добавить кастом стили и скрипты js css. (Django add custom styles and js)

195

https://gist.github.com/rg3915/26076942ef4b0564cfa4b398a92c9b51

Как то мне потребовалось немного изменить стиль и js админки. В частности мульти селекта.
Для этого потребовалось подключение кастомных скриптов и стилей.

Собственно для этого используется класс Media в admin.py при регистрации модели. Пути стилей идут от статики из папки admin.

from .models import Post


@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    class Media:
        css = {
            'all': ('/static/admin/css/custom.css',)
        }
        js = (
            '/static/admin/js/vendor/jquery/jquery.js',
            '/static/admin/js/vendor/select2/select2.full.js',
            '/static/admin/js/custom.js',
            )
0
 

October CMS BackendAuth получение данных бэкенд юзера

138

Иногда требуется информация от админа. То есть не просто юзера, а юзера именно из админки.
Для это используется класс BackendAuth.

use Backend\Facades\BackendAuth;

BackendAuth::check() // true false  проверка на юзера
BackendAuth::user() // вся информация о юзере
BackendAuth::user()->id // конкретное поле

Соответсвенно можете вытянуть любую нужную информацию

0
 

Woocommerce Почта РФ плагин фиксированная стоимость.

186

Есть плагин https://wordpress.org/support/plugin/russian-post-and-ems-for-woocommerce/.
У автора нет функционала фиксированной цены. По крайней мере на данный момент. Хотя в аналогичном сдек плагине это реализовано.

Как указано в топике самим автором https://wordpress.org/support/topic/фиксированная-стоимость-доставки/ можно сделать через фильтр.

add_filter( 'woocommerce_package_rates', 'override_ups_rates' );
function override_ups_rates( $rates ) {
    foreach( $rates as $rate_key => $rate ){
        // Check if the shipping method ID is UPS
        if( ($rate->method_id == 'rpaefw_post_calc') ) {
            // Set cost to zero
            $rates[$rate_key]->cost = 350;

        }
    }
    return $rates;
}

0