Category Archives: програмиране

Преди FOSDEM 25

от Ясен Праматаров
лиценз CC BY

Отмятам едно по едно неща, преди да замина за FOSDEM. Остана ми утре и после в петък отивам на най-голямата опънсорс сбирка на планетата – 1090 лекции и събития в няколко сгради на кампуса на ULB, само за два дни. Най-голяма в галактиката направо… или поне в Слънчевата система.

Разбира се, никой не може да види всички лекции, да не говорим, че трябва и да се обиколят щандове на проекти, да се види човек с приятели, докато тича между сградите. Тръгваш да си отбелязваш каквото ти е интересно, виждаш, че във всеки час има поне 2-3 застъпващи се… и се отказваш да записваш. Но е важна атмосферата и ентусиазма, не да видиш буквално всичко. Като в живота, един вид…

Та отмятането – днес привърших някои неща в Jilo. Като след всяка голяма промяна, сигурно сега има сто бъга за оправяне, но като цяло се държи добре. И уеб интерфейсът, и сървърът, и агентите. Следващата стъпка е да се концентрирам пак върху парсването на логовете, за да идват по-точни и по-подробни данни. Също и да се събират повече статистики наживо от конференциите. Красивите цветни графики не са отказали никого, тъй де. Но тия неща ще са за след Брюксел.

Пак ми изостават видео нещата, но и това ще стане. Чакам малко техника (микрофони, стойки и т.н.), които пък ще ми влязат в употреба и за записване на китари и песнички. Красота.

Ето и Jilo, да се изфукам, за който се интересува:

демо: https://work.lindeas.com/jilo-web-demo/

инфо и линкове: https://lindeas.com/jilo

Пресен бетон и агенти на Go

от Ясен Праматаров
лиценз CC BY

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

За беда точно сега пък няма никаква работа за клиенти – пу, пу, че ей-сега ще се обади някой – и пиша мои си неща.

Движа Jilo (за мониторинг на Jitsi сървъри) и отделно TotalMeet, дето ми беше отдавна идеята за сайт, дето хората да си правят видео срещи (пак на Jitsi, де). Имам и едни видеота да редактирам, пак за същите неща, ама то от легло не става.

От всичко това видимо е само Jilo. Има ги в github нещата.

Тръгнах да пиша jilo-agent, който ще работи на всяка машинка, за да пищи като има някакъв проблем или да показва конфигурацията, евентуално да я редактира – такива работи. Без да губя време, почнах на PHP и то вярно, че лесно и става, ама после се сещам, че покрай него трябва уеб сървър, че и да върви php на него… Освен фронтенд машините, друго почти няма с уеб сървър, а и дори на тях php не е по подразбиране.

ОК, викам си, дай на Bash, като другия агент на Jilo, дето пък сканира за събития от логовете. Да, ама и то не ми хареса нещо. Дай тогава, викам, примерно на Go – таман ще по понауча поне малко, нали всички за него говорят.

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

Да, ама скриптовете на php, барабар с коментарите (аз ги пиша заедно с кода, щото после кой ще се връща да се сеща и да допълва) е максимум 6-7 килобайта. Bash скриптът и той някъде там. А Go програмчето и то е така, но като го компилирам, дори и максимално орязано и компресирано, е почти 2 мегабайта.
Вярно – преносимо, пускаш го навсякъде, не е като php с уеб сървър… ама то и bash има на всичките сървъри.

Изобщо… нямам търпение да почна да ставам. Не стъпвайте в мокър бетон! Цимент и вар да не докосват кожа – няма значение колко често се миете, гори веднага и после цяла седмица кожата е в рани. А глезените са много кофти място. Уж си добре, ама като стъпиш на крака и падаш.

Горкият Ахил…

Един фокус, мишка и Сафари

от Гонзо
лиценз CC BY-NC-SA

За да направя така, че падащото меню да се прибира при цъкане извън него реших да използвам събитието focusout на елементът, съдържащ целия компонент. Събитието focusout може да бъде прихванато от всеки елемент в йерархията, независимо дали самия елемент може да приема фокус, така че беше идеално за целта. Щом фокуса отида другаде, а това става и когато се цъкне с мишката извън него или пък потребителя се прехвърли на друг елемент с tab, значи падащото меню трябва да се прибере. Да, ама не и в Сафари. Там нещата винаги работят по друг начин.

Оказа се, че в Сафари взаимодействията с мишката не водят до промяна на фокуса. Т.е. цъкането с мишката върху бутон не поставя фокуса върху него. Съответно цъкането извън бутона няма да предизвика загуба на фокус от бутона. К’во правим сега?

Реших проблема като зорлем поставих елемента на фокус при цъкане с мишката върху него. От там нататък всичко продължава да действа както преди.

dropdown.addEventListener( 'mouseup', function( e ) {
  this.focus();
  e.preventDefault();
} );

Дано това да ви помогне и на вас ако срещнете подобен проблем. А самия компонент в действие може да разгледате на адрес https://www.thenaturaladventure.com/tours/. Обратната връзка е добре дошла.

Ограничаване на видимите снимки в галерия

от Гонзо
лиценз CC BY-NC-SA

Примерът, който ще дам е със стандартен блок за галерия в WordPress, но подходът може да се използва и в други случаи. Целта ни е да покажем първите девет снимки, като върху деветата покажем и броя скрити снимки. Тъй като използваме скрипт за разглеждане на картинките в пълния им размер, искаме естествено скритите снимки да са видими за него. Нещо такова:

Ключовият момент е използването на CSS Grid Layout за оформяне на галерията. Чрез използването на подходящи селектори можем да разположим всички елементи след да кажем деветия в последната клетка от решетката, като по този начин ограничим броя видими снимки.

.blocks-gallery-grid { 
  display: grid; 
  grid-auto-rows: auto; 
  grid-gap: 2em; 
  width: 100%;
}

.columns-3 .blocks-gallery-grid  {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.columns-3 .blocks-gallery-grid > :nth-child(9),
.columns-3 .blocks-gallery-grid > :nth-child(9) ~ * {
  grid-column: -2/-1;
  grid-row: 3;
}

Примерът е за галерия с по три снимки на ред, и след като искаме да са видими първите девет, следователно с три видими реда снимки. Първите двe правила задават решетката за галерията, основния трик е в третото правило. То казва, че деветия и всички елементи след него трябва да се разположат на третия ред от решетката в последната колона. За съжаление CSS Grid не ни позволява да посочваме автоматично създадените редове с отрицателни индекси когато не сме указали точния брой редове в решетката. Ако използвате инспектора във Firefox за да видите как браузъра описва решетката на галерията, ще забележите, че с индекс -1 е означена линията над първия ред. Поради тази причина се налага да посочим реда експлицитно.

До тук добре, но как да покажем броя скрити снимки? И как да направим така, че потребител, използващ само клавиатурата, да се ориентира какво се случва докато се придвижва с Tab през иначе скритите снимки? За да покажем броя скрити снимки ще използваме брояч в CSS, с който да преброим скритите снимки и да покажем стойността му в псевдо-елемент в последната снимка. И ще направим видима снимката, която в момента е на фокус, като я „повдигнем“ над останалите скрити снимки със z-index.

.blocks-gallery-grid { 
  counter-reset: remaining-items;
}

.columns-3 .blocks-gallery-grid > :nth-child(9),
.columns-3 .blocks-gallery-grid > :nth-child(9) ~ * {
  counter-increment: remaining-items;
}

.columns-3 .blocks-gallery-grid > :nth-child(9) ~ :last-child::after {
  content: "+" counter(remaining-items);
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba( 0, 0, 0, .75 );
  color: #fff;
  font-size: 3em;
}

.columns-3 .blocks-gallery-grid > :nth-child(9) ~ * {
  pointer-events: none;
}

.columns-3 .blocks-gallery-grid > :nth-child(9):focus-within,
.columns-3 .blocks-gallery-grid > :nth-child(9) ~ *:focus-within {
  position: relative;
  z-index: 2;
  pointer-events: all;
}

.columns-3 .blocks-gallery-grid > :last-child:focus-within::after {
  opacity: 0;
}

Освен, че показваме брояч, в горните правила изключваме обработването на събития от мишката (или докосвания ако екрана на устройството реагира на тях) за скритите картинки. Но когато дадена картинка е на фокус и съответно е над останалите, бихме искали да реагира на събитията, и за последната снимка искаме да скрием брояча, за да е видима.

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

Достъпен превключвател за нощен режим в сайта

от Гонзо
лиценз CC BY-NC-SA

В какво се състои проблемът в решението на Атанас? За превключване към нощен режим той използва div със закачена JavaScript функция, която да обслужва събитието click, т.е. единствено интеракция с мишка или натискане с пръст. Но тъй като div не е интерактивен елемент в HTML той не може да приема фокус от клавиатурата и няма да реагира на никакви взаимодействия с нея. Това го прави неизползваем за онези потребители, които поради някаква причина не могат да използват мишка.

Проблемът може лесно да се реши ако използваме елемента button. Той по подразбиране приема фокус, реагира както ва взаимодействия с мишката, така и на клавишите за нов ред и интервал. Може да бъде стилизиран със CSS по същия начин както всеки друг елемент. Да видим как ще изглежда кодът тогава (ще подчертая промените с получер шрифт):

<button type="button" class="wpnm-button">
    <div class="wpnm-button-inner-left"></div>
    <div class="wpnm-button-inner"></div>
</button>
.wpnm-button {
    padding: 0;
    border: none;
    background: none;
    font-size: 1rem;
}

След като променихме елемента, се налага да направим и някои промени в стиловете – тъй като бутоните има подразбиращи се стилове, които да ги накарат да изглеждат като бутони, ние ще трябва да ги пренапишем.

Тъй като вече използваме интерактивен елемент, можем да добавим стилове, които да го отличават когато фокусът е върху него, когато е посочен с мишката или натиснат. Но това ще го оставим за домашно…

Вече имаме хубав бутон, достъпен за потребителите, използващи клавиатура, но на бутонът му липсва текстов етикет. Той е важен за потребителите, използващи екранен четец – когато курсорът на четеца или фокуса попадне на този бутон, той няма да може да даде подходящо описание на потребителя и потребителя няма да може да разбере какво прави този бутон. Най-добре би било текстовия етикет да е видим непосредствено до заобления превключвател, но за да не разваляме изчистения дизайн ще добавим скрит етикет чрез атрибута aria-label. Освен това ще е добре потребителя да може да разбере дали в момента нощния режим е включен или изключен. Състоянието на бутона можем да укажем с атрибута aria-pressed. Този атрибут служи за указване на състоянието на бутони с две състояние (включено – изключено, натиснат – ненатиснат), точно като нашия превключвател към нощен режим.

<button type="button" class="wpnm-button" aria-label="<?php esc_attr_e( 'Dark mode switch', 'textdomain' ); ?>" aria-pressed="false">
    <div class="wpnm-button-inner-left"></div>
    <div class="wpnm-button-inner"></div>
</button>
jQuery(function($) {
    /*Click on dark mode icon. Add dark mode classes and wrappers. 
    Store user preference through sessions*/
    var $button = $('.wpnm-button'),
        $body = $('body');
    $button.click(function() {
        // Show either moon or sun.
        $button.toggleClass('active');
        // If dark mode is selected.
        if ($button.hasClass('active')) {
            //Add dark mode class to the body
            $body.addClass('dark-mode');
            //Save user preference to Storage
            localStorage.setItem('darkMode', true);
            // Reflect the state on the button itself.
            $button.attr('aria-pressed', true);
        } else {
            $body.removeClass('dark-mode');
            localStorage.removeItem('darkMode');
            // Reflect the state on the button itself.
            $button.attr('aria-pressed', false);
        }
    })
    //Check Storage. Display user preference 
    if (localStorage.getItem("darkMode")) {
        $body.addClass('dark-mode');
        $button.addClass('active');
        // Reflect the state on the button itself.
        $button.attr('aria-pressed', true);
    }
})

Както виждате, за етикета на бутона използвах PHP код, който да позволи етикетът да бъде част от преводите на темата или разширението. По този начин с малко помощ от общността на WordPress темата или разширението ни може да достигне до повече потребители.

Освен, че добавих промяна на атрибута aria-pressed, в JavaScript добавих и една оптимизация – можем да запазим в променливи jQuery референциите към бутона и елемента body, за да си спестим търсенето в DOM при всяко изпълняване на части от кода. Макар в случая да не е чак толкова критично, ако страницата има твърде много елементи или имаме много скриптове, подобен подход може да има съществен ефект върху производителността.

Това е минимума от промени, с които да направим превключвателя достъпен. Можем да подобрим още нещата, като например направим етикета видим когато фокуса е върху бутона. Това може да стане с техниката, описана от Adrian Rosseli за добавяне на достъпен етикет на емотикони, и която използвам за бутоните за споделяне в разширението ми Minimal Share Buttons (има ги и под тази статия). Можем да използваме чист JavaScript вместо jQuery, с което да намалим обема на изтегляните скриптове (освен ако така или иначе не използваме и други скриптове, зависещи от jQuery). И можем вместо допълнителен клас active върху бутона да използваме атрибута aria-pressed и в CSS за промяна на изгледа когато е включен нощния режим. За целта ще ни помогне CSS селектор по атрибут: .wpnm-button[aria-pressed="true"].

Как да накараме един блок да се разпъва според два елемента на куп

от Гонзо
лиценз CC BY-NC-SA

За пример ще използвам блок „Обложка“ от новия редактор на WordPress (известен като Гутенберг), който представлява картинка, върху която има текст и/или друго съдържание. Такъв блок може да играе ролята на рекламен банер или заглавие на секция.

Това е обложка

И в нея можем да поставим много текст. Например можем да добавим още едно изречение, което не казва нищо. Обаче не е достатъчно, така че ще добавим и още и още.

В момента Гутенберг поставя изображението като фон на блока и му дава минимална височина. Обаче така пропорциите на блока нямат връзка с пропорциите на изображението и е трудно да го направите винаги да е да кажем квадратен или в пропорция 4 към 3. Освен това поставяйки изображението като фон в атрибут style, губим възможността да се се възползваме от отзивчивите изображения и ще караме потребителя да зарежда едно и също (голямо) изображение независимо от размера на екрана му.

<div class="wp-block-cover has-very-dark-gray-background-color has-background-dim" style="background-image:url(https://greatgonzo.net/wp-content/uploads/2019/08/Foggy_Forest_by_Jake_Stewart.jpg)">
  <div class="wp-block-cover__inner-container">
    <p style="text-align:center" class="has-huge-font-size">Това е обложка</p>
    <p style="text-align:center">И в нея можем да поставим много текст. Например можем да добавим още едно изречение, което не казва нищо. Обаче не е достатъчно, така че ще добавим и още и още.
    <div class="wp-block-button aligncenter">
      <a class="wp-block-button__link" href="#">Цъкнете тук!</a>
    </div>
  </div>
</div>

За да постигнем желания ефект, ще поставим изображението като елемент img и ще разположим съдържанието върху него. Сигурно първото, което ви хрумва е да използвате абсолютно позициониране, но това не е добър вариант, защото тогава съдържанието няма да разпъва блока. Как иначе да сложим два елемента един връз друг? Със CSS Grid естествено. CSS Grid ни позволява да поставим повече от един елемент в дадена клетка на решетката и ние ще се възползваме от тази възможност:

<div class="wp-block-cover has-very-dark-gray-background-color has-background-dim uses-css-grid">
  <img src="https://greatgonzo.net/wp-content/uploads/2019/08/Foggy_Forest_by_Jake_Stewart-1024x684.jpg" aria-hidden="true" class="wp-image-2499" srcset="https://greatgonzo.net/wp-content/uploads/2019/08/Foggy_Forest_by_Jake_Stewart-1024x684.jpg 1024w, https://greatgonzo.net/wp-content/uploads/2019/08/Foggy_Forest_by_Jake_Stewart-300x200.jpg 300w, https://greatgonzo.net/wp-content/uploads/2019/08/Foggy_Forest_by_Jake_Stewart-768x513.jpg 768w" sizes="(max-width: 1024px) 100vw, 1024px">
  <div class="wp-block-cover__inner-container">
    <p style="text-align:center" class="has-huge-font-size">Това е обложка</p>
    <p style="text-align:center">И в нея можем да поставим много текст. Например можем да добавим още едно изречение, което не казва нищо. Обаче не е достатъчно, така че ще добавим и още и още.
    <div class="wp-block-button aligncenter">
      <a class="wp-block-button__link" href="#">Цъкнете тук!</a>
    </div>
  </div>
</div>
.wp-block-cover.uses-css-grid > * {
   grid-column: 1;
   grid-row: 1;
   min-width: 100%;
   min-height: 100%;
 }
 
.wp-block-cover.uses-css-grid > img {
   border: none !important;
   object-fit: cover;
 }
 
.wp-block-cover.uses-css-grid .wp-block-cover__inner-container {
   display: flex;
   flex-direction: column;
   justify-content: center;
   padding: 3em;
   box-sizing: border-box;
 }

Горният CSS освен, че поставя картинката и текстовото съдържание в единствената клетка от решетката, дефинирана върху блока, се грижи текстовото съдържание да е центрирано вертикално в блока, и картинката винаги да го запълва. Ето и крайния резултат:

Това е обложка

И в нея можем да поставим много текст. Например можем да добавим още едно изречение, което не казва нищо. Обаче не е достатъчно, така че ще добавим и още и още.

Ето го още веднъж, но този път с увеличен шрифт, за да демонстрирам как съдържанието също разпъва блока:

Това е обложка

И в нея можем да поставим много текст. Например можем да добавим още едно изречение, което не казва нищо. Обаче не е достатъчно, така че ще добавим и още и още.

Как да използваме Autoptimize с HTTP/2

от Гонзо
лиценз CC BY-NC-SA

В по-новите версии на Autoptimize има възможност чрез филтър да съставите списък с разрешени за обединяване скриптове. Това позволява да обедините скриптовете, които са задължителни за всички страници в сайта и да оставите скриптовете, които се зареждат само на определени страници извън пакета. Това решава и един друг проблем с Autoptimize – когато различни скриптове се зареждат на различни страници, пакетите, които се генерират са различни. Това води до безконтролно увеличаване на кеша на Autoptimize и до забавяне на зареждането за потребителите, защото вместо да заредят кеширан файл, всеки път теглят различен.

И така, да започнем с обединяването на задължителните скриптове:

/**
 * Whitelist scripts to autoptimize
 *
 * @return: whitelist
 */
function gonzo_js_whitelist() {
  $whitelist = array(
    'jquery.js',
    'jquery-migrate.min.js',
    'wp-embed.min.js'
  );
  return join( ',', $whitelist );
}
add_filter( 'autoptimize_filter_js_whitelist', 'gonzo_js_whitelist' );

След като вече имаме пакет с най-важните скриптове, нужни за сайта, трябва да се погрижим за две неща:

  • Да зареждаме обединените скриптове преди останалите, защото пакетът съдържа jQuery, а някои от останалите скриптове най-вероятно разчитат на него.
  • Да зареждаме скриптовете асинхронно, за да не блокират изобразяването на страницата.

Генерираният от Autoptimize скрипт елемент може да поставим веднага след елемента title така:

/**
 * Where in the HTML optimized JS is injected.
 *
 * @param array $replacetag, containing the html-tag and the method (inject "before", "after" or "replace")
 * @return array with updated values
 */
function gonzo_override_js_replacetag( $replacetag ) {
    return array( '</title>', "after" );
}
add_filter( 'autoptimize_filter_js_replacetag', 'gonzo_override_js_replacetag', 10, 1 );

За да зареждаме всички скриптове асинхронно, и в същото време последователно, ще добавим атрибут defer към тях. Така скриптовете не само се зареждат асинхронно, но и не блокират изобразяването на страницата когато се изтеглят. Освен това атрибута defer кара скриптовете да се изпълнят по реда в документа.

/*
 * Defer all javascripts
 */
function gonzo_defer_js($tag, $handle) {
  if( ! is_admin() ){
    $tag = str_replace( ' src', ' defer="defer" src', $tag );
  }
  return $tag;
}
add_filter( 'script_loader_tag', 'gonzo_defer_js', 10, 2 );

/**
 * Change flag added to Javascript.
 *
 * @param $defer: default value, "" when forced in head, "defer " when not forced in head
 * @return: new value
 */
function gonzo_override_defer( $defer ) {
  return "defer ";
}
add_filter( 'autoptimize_filter_js_defer', 'gonzo_override_defer', 10, 1 );

И на края, за да гарантираме, че скриптовете ще се кешират правилно от браузърите и прокситата, ще премахнем GET променливата с версията, която WordPress добавя към адресите на всички ресурси:

/*
 * Remove query string from static assets
 */
function gonzo_remove_script_version( $src ){
  return remove_query_arg( 'ver', $src );
}
add_filter( 'script_loader_src', 'gonzo_remove_script_version' );

Естествено, подобен подход можем да приложим и към CSS файловете, които сайта зарежда. На края ще имаме бърз сайт за удоволствие на потребителите и печалба на клиента.

Експортиране на заглавия, съдържащи HTML, от WordPress

от Гонзо
лиценз CC BY-NC-SA

WordPress позволява да се въвежда HTML на много места, на които не бихте си помислили да го направите. Например заглавията на публикациите – например за да откроите някои думи, да маркирате правилно абревиатура или по друга причина. Например повечето статии в „Как се пише“ съдържат HTML, за да откроят думите, за които се отнася статията. Обаче ако ви се наложи да експортирате това съдържание, ще откриете, че заглавията са изчистени от всякакъв HTML и форматирането е загубено. Така стандартният експорт на WordPress става безполезен за пазене на архив на съдържанието или за прехвърлянето му на друга инсталация. Обаче има лесно решение.

Ако поровите в кода на функцията export_wp(), ще видите две интересни неща:

  1. Там е дефинирана функцията wxr_cdata(), която затваря подадения стринг в CDATA блок.
  2. Заглавията на публикацитиите се филтрита през филтъра the_title_rss.

Значи за да можем да експортираме заглавията заедно с HTML-а, трябва да махнем от този филтър функцията, която чисти HTML-а (strip_tags естествено, както и esc_html()) и да добавим wxr_cdata(). Всъщност това второ няма да стане, защото функцията е дефинирана само в контекста на export_wp(), така че ще трябва просто да я клонирате.

function htr_cdata_post_title ( $title ) {
	$title = '<![CDATA[' . $title . ']]>';
  return $title;
}
add_filter( 'the_title_rss', 'htr_cdata_post_title' );

remove_filter( 'the_title_rss', 'strip_tags' );
remove_filter( 'the_title_rss', 'esc_html' );

Това е всичко, при… опа, това беше от друго шоу.

Установяване на правилен локал на формите, създадени с Contact Form 7

от Гонзо
лиценз CC BY-NC-SA

Наскоро ми се наложи да правя сайт с WordPress на Иврит. За незапознатите с особеностите на близкоизточните езици ще напомня, че се пишат от дясно на ляво и нямат главни букви. Освен това чуждите думи, изписани на латиница (или друга писменост, която се пише от ляво на дясно), както и числа, изписани с арабски цифри, се пишат както си трябва – от ляво на дясно. WordPress поддържа достатъчно добре всякакви писмености и езици, правилната локализация също е важно условие за одобряване на темите в wordpress.org. Аз обаче се сблъсках с един проблем на разширението Contact Form 7, който не е очевиден, когато сайтът е на език, изписван от ляво на дясно. В случая с Иврит обаче, въпреки че всичко останало по сайт сменяше посоката на подравняване на текста и елементите, полетата във формата оставаха подравнени вляво. Бърз преглед на генерирания HTML показа наличието на атрибути lang и dir на елемента, който съдържа формата, за манипулиране на които атрибути няма настройка. Проблемът е, че в моя случай тези атрибути имаха стойности като за английски локал, което не само, че обръщаше подравняването на текста, но би имало неприятни последици и за потребителите на екранни четци – ако екранният четец превключи на друг език, това би било най-малкото доста объркващо.

След кратко търсене в сайта на разширението и в Гугъл намерих въпрос, зададен от потребител, използващ Contact Form 7 в многоезичен сайт на английски и арабски. Неговият проблем беше подобен на моя – формите на арабски си оставаха подравнени вляво. Отговорът за мен изглежда доста нелепо: локала на формата се установява на базата на локала на администрацията по време на създаване на формата и се наследява при клониране на формата. При условие, че често в многоезичните (а и не само) сайтове администраторите на сайта използват локал, различен от този на публичния сайт, и като имаме предвид, че новите версии на WordPress дават възможност в администрацията потребителите да избират локал, различен от основния локал на сайта, това поведение на Contact Form 7 има сериозни последствия върху достъпността на сайта. Проблемът може да се отрази не само на потребителите, използващи екранни четци, но и на всички останали – въпреки, че разширението блокира валидирането на формите в браузъра, това може да бъде отменено от други разширения, и тогава съобщенията на браузъра ще са на езика, установен от локала на формата, а не на езика на сайта.

Понеже не намерих конкретно решение на проблема, освен да си сменям езика в администрацията – това не беше решение в случая – се зарових в кода на разширението. И намерих точно каквото ми трябваше – кука за действие, чрез което могат да се манипулират свойствата на обекта, отговарящ за генерирането на формата, точно преди превръщането ѝ в HTML. Ето и кодът, който написах:

function gonzo_fix_cf7_locale( $cf7 ) {
  $cf7->set_locale( get_locale() );
}
add_action( 'wpcf7_contact_form', 'gonzo_fix_cf7_locale' );

Това, което правя е просто – пренаписвам локала на формата с основния локал на сайта. Решението е универсално и ще работи винаги, когато формата е на същия език като сайта.

Флекс в неделя сутрин: заглавие с линии от ляво и дясно

от Гонзо
лиценз CC BY-NC-SA

Не знам дали ще има и други публикации, че да се превърне това в рубрика, но ми хареса идеята да си поиграя с флекса в неделя сутрин. И така, искаме на направим заглавка, която има от ляво и от дясно линии до края на блока със съдържание и искаме да го направим с минимален брой елементи. С флексбокс става с точно един елемент. С помощта на двата псевдоелемента ще нарисуваме линиите, а флексбокс ни е нужен, за да подравним нещата. Тайната е в това, че ако флекс контейнера съдържа текст, около него се създава анонимен елемент, който да служи като флекс елемент, и така не ни е необходим допълнителен елемент около текста. Най-добре да ви покажа кода:

HTML:

<h1>Флекс в неделя сутрин</h1>

CSS:

h1 {
  display: flex;
  flex-flow: row nowrap;
  align-items: center;
}

h1:before, h1:after {
  content: '';
  flex: 1;
  height: 0;
  border-top: 1px solid currentColor;
}

h1:before {
  margin-right: .5em;
}

h1:after {
  margin-left: .5em;
}

Резултата:

Флекс

Ако има нещо неясно, питайте!