From d80dd91769b5e1c4259a31a72b240d22576c30d3 Mon Sep 17 00:00:00 2001 From: touche Date: Sat, 24 Oct 2026 05:49:49 +0300 Subject: [PATCH] Initial commit: WordPress plugins collection --- .gitignore | 3 + S1-floating-video.zip | Bin 0 -> 4854 bytes S1-floating-video/assets/floating-video.css | 83 +++++ S1-floating-video/assets/floating-video.js | 35 ++ S1-floating-video/estetika-floating-video.php | 327 ++++++++++++++++++ floatUp_Opt/float-up-optimized-v1.1.0.zip | Bin 0 -> 6512 bytes floatUp_Opt/float-up-wp5.4.zip | Bin 0 -> 6510 bytes floating-social-icons-secure.zip | Bin 0 -> 10382 bytes s1-custom-scroll-to-top/assets/arrow-up.svg | 38 ++ s1-custom-scroll-to-top/assets/path2.png | Bin 0 -> 220 bytes s1-custom-scroll-to-top/assets/script.js | 32 ++ s1-custom-scroll-to-top/assets/style.css | 48 +++ .../s1-custom-scroll-to-top.php | 218 ++++++++++++ .../s1-custom-scroll-to-top.zip | Bin 0 -> 4517 bytes s1-disable-gutenberg.zip | Bin 0 -> 2418 bytes s1-disable-notices.zip | Bin 0 -> 1769 bytes s1-floating-social-icons.zip | Bin 0 -> 8661 bytes s1-gallery.zip | Bin 0 -> 18934 bytes s1-hide-admin/readme.txt | 38 ++ s1-hide-admin/s1-hide-admin.php | 232 +++++++++++++ s1-hide-admin/s1-hide-admin.zip | Bin 0 -> 5040 bytes s1-seo-meta.zip | Bin 0 -> 10168 bytes .../admin/class-s1-seo-meta-admin.php | 45 +++ s1-seo-meta73/admin/css/s1-seo-meta-admin.css | 93 +++++ s1-seo-meta73/admin/js/s1-seo-meta-admin.js | 56 +++ .../includes/class-s1-seo-meta-output.php | 109 ++++++ .../includes/class-s1-seo-meta-post.php | 164 +++++++++ .../includes/class-s1-seo-meta-term.php | 196 +++++++++++ s1-seo-meta73/languages/s1-seo-meta.pot | 65 ++++ s1-seo-meta73/readme.txt | 48 +++ s1-seo-meta73/s1-seo-meta.php | 56 +++ s1-seo-meta73/s1-seo-meta73.zip | Bin 0 -> 10168 bytes s1-seo-meta74.zip | Bin 0 -> 22525 bytes .../admin/class-s1-seo-meta-admin.php | 45 +++ s1-seo-meta74/admin/css/s1-seo-meta-admin.css | 108 ++++++ s1-seo-meta74/admin/js/s1-seo-meta-admin.js | 111 ++++++ .../includes/class-s1-seo-meta-output.php | 109 ++++++ .../includes/class-s1-seo-meta-post.php | 164 +++++++++ .../includes/class-s1-seo-meta-term.php | 236 +++++++++++++ s1-seo-meta74/languages/s1-seo-meta.pot | 65 ++++ s1-seo-meta74/readme.txt | 48 +++ s1-seo-meta74/s1-seo-meta.php | 56 +++ s1-seo-meta74/s1-seo-meta73.zip | Bin 0 -> 10168 bytes .../s1-show-category-description.php | 102 ++++++ .../s1-show-category-description.zip | Bin 0 -> 1492 bytes .../s1-smart-sticky-anything.php | 137 ++++++++ .../s1-smart-sticky-header.php.zip | Bin 0 -> 2096 bytes 47 files changed, 3067 insertions(+) create mode 100644 .gitignore create mode 100644 S1-floating-video.zip create mode 100644 S1-floating-video/assets/floating-video.css create mode 100644 S1-floating-video/assets/floating-video.js create mode 100644 S1-floating-video/estetika-floating-video.php create mode 100644 floatUp_Opt/float-up-optimized-v1.1.0.zip create mode 100644 floatUp_Opt/float-up-wp5.4.zip create mode 100644 floating-social-icons-secure.zip create mode 100644 s1-custom-scroll-to-top/assets/arrow-up.svg create mode 100644 s1-custom-scroll-to-top/assets/path2.png create mode 100644 s1-custom-scroll-to-top/assets/script.js create mode 100644 s1-custom-scroll-to-top/assets/style.css create mode 100644 s1-custom-scroll-to-top/s1-custom-scroll-to-top.php create mode 100644 s1-custom-scroll-to-top/s1-custom-scroll-to-top.zip create mode 100644 s1-disable-gutenberg.zip create mode 100644 s1-disable-notices.zip create mode 100644 s1-floating-social-icons.zip create mode 100644 s1-gallery.zip create mode 100644 s1-hide-admin/readme.txt create mode 100644 s1-hide-admin/s1-hide-admin.php create mode 100644 s1-hide-admin/s1-hide-admin.zip create mode 100644 s1-seo-meta.zip create mode 100644 s1-seo-meta73/admin/class-s1-seo-meta-admin.php create mode 100644 s1-seo-meta73/admin/css/s1-seo-meta-admin.css create mode 100644 s1-seo-meta73/admin/js/s1-seo-meta-admin.js create mode 100644 s1-seo-meta73/includes/class-s1-seo-meta-output.php create mode 100644 s1-seo-meta73/includes/class-s1-seo-meta-post.php create mode 100644 s1-seo-meta73/includes/class-s1-seo-meta-term.php create mode 100644 s1-seo-meta73/languages/s1-seo-meta.pot create mode 100644 s1-seo-meta73/readme.txt create mode 100644 s1-seo-meta73/s1-seo-meta.php create mode 100644 s1-seo-meta73/s1-seo-meta73.zip create mode 100644 s1-seo-meta74.zip create mode 100644 s1-seo-meta74/admin/class-s1-seo-meta-admin.php create mode 100644 s1-seo-meta74/admin/css/s1-seo-meta-admin.css create mode 100644 s1-seo-meta74/admin/js/s1-seo-meta-admin.js create mode 100644 s1-seo-meta74/includes/class-s1-seo-meta-output.php create mode 100644 s1-seo-meta74/includes/class-s1-seo-meta-post.php create mode 100644 s1-seo-meta74/includes/class-s1-seo-meta-term.php create mode 100644 s1-seo-meta74/languages/s1-seo-meta.pot create mode 100644 s1-seo-meta74/readme.txt create mode 100644 s1-seo-meta74/s1-seo-meta.php create mode 100644 s1-seo-meta74/s1-seo-meta73.zip create mode 100644 s1-show-category-description/s1-show-category-description.php create mode 100644 s1-show-category-description/s1-show-category-description.zip create mode 100644 s1-smart-sticky-anything/s1-smart-sticky-anything.php create mode 100644 s1-smart-sticky-anything/s1-smart-sticky-header.php.zip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe18e15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.backup +.DS_Store +Thumbs.db diff --git a/S1-floating-video.zip b/S1-floating-video.zip new file mode 100644 index 0000000000000000000000000000000000000000..22dbe86b65273fca48e2987be522690d334c11c3 GIT binary patch literal 4854 zcmai21yB@P+g?I&$t47&Lt0Adk_HJCmIb6!SR|GdBqSCDMRI9WaOse4a93avmS#aZ z1zeDplJe(%bN{*b|Kk77nK?D@d7nA&JacBA=aC)}F+JdqCI%O`{Kw+Yf%Iy&^YL-= z^$~mYXV7(k_f=3gQ<1e0Apnqh4FI_LSI`qTPdi_zhqH)3)X~XP)WOH+t+8SJv^eNu zNboi!HEmq}M^zYSPIoNZaz!=Ij0!`Y44&qBOlaM`is`%gG8?j7y?ogHYE`v&>2~K& z)|5K*T{qygn-z2Ak(m4TtBPR<9a)l4DWa5vHrX3_A?B-F!pWh!RQ z6`vtgg5Q2W_~l&!vyJ;QQh|>jVPMN+L}cI+k>l?pO;G!KMOkHTMK>47af)k+LEM98 zSX(wTKv!5<0>hj5qxs$dY3HZuh5M#lf4)Ulbtnl;hkAf8H3aAHU3kMDn@WED z1&6jZcL*abc(XHeJv_-zwFKYdZaQt;Fc?=k9Ir-87jYmA35Gwsk>Ytqv=Gv8$5}#* zoqtYc`W**{ZBC-2BvGJa)qLqDqY$3opafK$UCRCjl(Cqus`=hqgS5DIVQ!&lFRawPa3~7e>H%Hrbat8%Eun$ zwyqObJ5Grm_9%m~>tn5@csLn_a8$B`D5+h*n*dna-BxiahbG#I-y|14+_b$J1ni1R`rY88;+c#Tvn?bk zCn$^K(~HL#{m8V@;L~pdv#S0kg?qPvHU(CgxI3fj_o?^bO1Y>{ewSgKl=Y9xpi>`H zn$8M6N@&k$AZH-&kD9N&eRLhZ7PUVB@BJ7k8f|v%5f!_JEZNx<3)e9ZZ2hzlZ%(Ts z?$hmTmQ#%rz9YLK62L^T3Nn&nw5w9BbM`pTz9lb$8YTb;bZSbM_no1Cvi!x++XfPr zI-69HE93w`hcE!Za>Y<5A73Y5=ukOA`M-8FG1F2TH0mA$>$-=Wc4@MBjNrw!iQ>*!@;AR z)@XQV%q^UimDR=g)c89K-E>muiCw(NX7xz zP7!}jzuh@0NPcVY`;0M-7 zu<}T;F*7AYDBY3jF3;ku^AglIoA`|~k7}s)$g$mli7!jOBKg@3hm;aV`CwdCfHoxU zokeR~-DR>-S@koaCb0#H^C|2bUCqQ|Y(c%>R+Onp`d3ryAj8&{nqVlT_4zt>8oTin zOAOZWE|6Au6&$!qmMEW3U*XV{g*>x@;H=F8SCb3JwZyI}CA^Laf66yOOOk>Ny`I0* zscZ+MQ)y;of2sW~-JQgQ#U1W7G-1Cf)nWa!DZ%F56SL^b?#U&sdr_oerz5ttJi}e0 zQ#vOK7e6Twwd7xUFyT`U=3DzBoj;pd?wWcbLJ@?(21^APA~s1ldWQo*(W2`M-`RF#1=Imrn8>4DiC&$ zkC^p^EiLg|X+ixgAUJ3cSHE0)3L@gTkfT$Rz@WBa5{gVP{q}*@hN-2GObK9}c5Bb! zQTgkNWchgYgJ3LoNalm#=MtZ7m6@n#8%Fk81r>hP$3SVdoQsGxh4J-GXLv8xo*u+I zyOEw+@JZgz@E0%`%pB zY><~KP;o^Si_9EvS-UIhL_DgMkqN)W=0*OPPGd76+s3YmSFl5!qMtsoGlayelzLa~ z#@O`)FT$R9I}-Hevv33U+iyH$Z>$X$&?WD_9AVQHrhgF?`MKY7iDujju_xptAZ$G` z|CJMSu$gAJA6`zj>FqK9NDXWJ^7n^pI|OA()hO0pZ+hMcxzE`*Bvn6@%;tXE;Eoi2 zPFjVre+gqZ1qnvh+&)i(U5cD)IPQeV?mCZ_ZuiobNm@`p6`tRo*3a}|7%gDs^ixre zh%pKYWt2toav+S!otr>Xi7{k{&C#{Fwo}5ILS2^a#jO_Bw{aNNaLn2i!?Dy}L40}F zq!t}I4>RF=cd=@07oaG$RYUfIKeVFf8J7 zK}B%LR3|()9!*~wsZCXs&(2C%R95c3@lM7jibd6SWBV4jiBoJ1F+-Y1ijYl~n->ig zgs0&fg2&nUnZOgYDwc@Po4?~&x)lHnjj1CW z=XceLLfn(6D;y)U2kw3)%jk|WzdJx*d~L$n+j5oG{>+VQMn7{jZYr%O%SN^TwdNo% ztRzP*A}8K*oAhIq-n+@i6HyILaJz;7NXW5RPJk4gBm)0)JPGJfuS*-lE<}- z`EO*>M~??%iyhZ=wgY`6G8bew#@F=qdQMNTDX$jm?A_(bH%M_3E}Xa z7SO*X%$x&n^TAv=K}2mVfB+%S_m$s%2_5?h07x}uQhV?c``9FjI+C*?o+EeO)eBZG^T?A8F7?yFeL0$4Yv)nGvb+0w84R`7ATbu`mcY zf1mC_tSXY{JWHV9$9tart{$!TpeM&nYfHNn7ARjDc2pjyqF5r6FD{Yvvw&5rIl*Pp)%< z`y-1V5-{yY`gPavH)5IZpfp>X7!~((-{$ott8;w7jZzg+y#JuF-7Uzfo4I8fDXmAs zBJaoQo|ByY(8v}Ej(M0y!FHkuulYJ?x@NS55KHd6BlUA^%%Qe-{UD^IDk{5`=8a%( zPpbL+Y3AAL!>Eg6vN&Aq+a6%gbOyYku^am~0a*9I6hDcyoj_3=9_7+P&}$ zyzzcI&DzGs%A)1@{gUc09M0?2Su2Lty1VgQ!8E~cG4Ym}$5xt2=iS`e=>&o06qV-? zd&M$(&T!mF$b;pgLj|E^9iaCi2eU3k^c`#>!snjNr5a`-5-$y*KI3NEqdxW z-_Rc!(P-Ct?$o{&=q;5~k&_ZEnf~@NF84qf_#2V$dG4pmOUXBIPIRq(gf(pZ5INMD z(lIfubhOV54&OBi;6PDV7a64DlX^VQmZe_LL*tQFBh1QO3?3rgU+VH2#OK)wjbq63 zG#fvCcYlhTRMYEgZ#MD@%$vn`g=fn>8x$wc3cIj0%%{mE@NT1*7s~M1MU&Da89A!3FU$z03 zUmX@3+!Sv*Oj zwfB`zggEUGJc<|k@m5b66XaG(qO;i;r714z>Zi^%H3{HBi=q1JQ=RJl2lZ6jY}`zf zgIp)tHj^LSX-UEy|BwZ3OT>j0o9-J6;dzHUH{(Ai1C)DL^qY^Hj02KN9bh-s$!Y?H zp63yf(pB^m+cmV_qC@#q}MZ@;OrN*@_qhS+Ru;qsddba{qRzmp?(i4I{Q>8CC9r*g&< zP^7_(KT~MrdN#~EKiyPI0XCBp=%L`}m5d(sgTlg*pGoEek`+6|(3$rfm*?nbk3rw4 zGm+O}XS*C)`K>F<7jday3l5gGQf1>^;27DTY}$?TDIyDmffO7|6fZg!1-aR^ru%o4 zAdHs*zq6a8ZNb<^_i6TrwHG9lqdSke)fe~YCIm%m-kaD`5eZi*wA_EfNn%~#Y5CSh z^-BKD?(@DuNE+zuufegC0B;|6TptsWk+S(#Q9WN$Z}5sj;;(%dTtmpVJ;d023%vpc z*H7Iec%(-_c#r5m*(kTJ1i~K;6n3Hd$NZcJ|MyU?{|Bi>iS6RQ uA^oeE`a6=ql>zYoNUOg;%lgHHUhvC*SHWu}e-xDXs%Kx-SCi(C-Twekf5FuN literal 0 HcmV?d00001 diff --git a/S1-floating-video/assets/floating-video.css b/S1-floating-video/assets/floating-video.css new file mode 100644 index 0000000..9decb37 --- /dev/null +++ b/S1-floating-video/assets/floating-video.css @@ -0,0 +1,83 @@ +.floating-video-container { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 9999; +} + +.floating-video-mini { + width: 120px; + height: 180px; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 6px 20px rgba(0,0,0,0.35); + background: #000; + position: relative; +} + +.floating-video-mini video { + width: 100%; + height: 100%; + object-fit: cover; +} + +.expand-btn, .close-btn { + position: absolute; + background: rgba(0,0,0,0.6); + color: white; + border: none; + border-radius: 50%; + width: 32px; + height: 32px; + font-size: 18px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; +} + +.expand-btn { bottom: 8px; right: 44px; } +.close-btn { bottom: 8px; right: 8px; } + +.expand-btn:hover, .close-btn:hover { + background: rgba(0,0,0,0.85); + transform: scale(1.15); +} + +/* Модальное окно */ +.video-modal { + display: none; + position: fixed; + inset: 0; + background: rgba(0,0,0,0.92); + z-index: 10000; + align-items: center; + justify-content: center; + padding: 20px; + box-sizing: border-box; +} + +.modal-content { + position: relative; + max-width: 96vw; + max-height: 96vh; +} + +.modal-content video { + max-width: 100%; + max-height: 90vh; + border-radius: 8px; +} + +.modal-close { + position: absolute; + top: -45px; + right: 0; + background: none; + border: none; + color: white; + font-size: 48px; + cursor: pointer; + line-height: 1; +} \ No newline at end of file diff --git a/S1-floating-video/assets/floating-video.js b/S1-floating-video/assets/floating-video.js new file mode 100644 index 0000000..bfaf55a --- /dev/null +++ b/S1-floating-video/assets/floating-video.js @@ -0,0 +1,35 @@ +document.addEventListener('DOMContentLoaded', () => { + const mini = document.getElementById('miniPlayer'); + const modal = document.getElementById('videoModal'); + const expandBtn = mini.querySelector('.expand-btn'); + const closeMini = mini.querySelector('.close-btn'); + const modalClose = modal.querySelector('.modal-close'); + + // Развернуть видео + expandBtn.addEventListener('click', () => { + modal.style.display = 'flex'; + // Можно раскомментировать, если хотите включить звук в полном экране: + // modal.querySelector('video').muted = false; + }); + + // Закрыть модальное окно + modalClose.addEventListener('click', () => { + modal.style.display = 'none'; + }); + + modal.addEventListener('click', e => { + if (e.target === modal) modal.style.display = 'none'; + }); + + // Полностью скрыть мини-плеер + closeMini.addEventListener('click', () => { + mini.style.display = 'none'; + }); + + // Запускаем видео в мини-окне (на всякий случай) + const miniVideo = mini.querySelector('video'); + miniVideo.play().catch(() => { + // Автозапуск заблокирован → почти всегда так в Chrome при первом посещении + console.log("Автозапуск заблокирован браузером"); + }); +}); \ No newline at end of file diff --git a/S1-floating-video/estetika-floating-video.php b/S1-floating-video/estetika-floating-video.php new file mode 100644 index 0000000..b6b26e2 --- /dev/null +++ b/S1-floating-video/estetika-floating-video.php @@ -0,0 +1,327 @@ + 'enabled'] + ); + + // Поле: URL видео + add_settings_field( + 'video_url', + 'URL видео (mp4)', + 'est_field_text_cb', + 'est-floating-video', + 'est_main_section', + ['id' => 'video_url', 'type' => 'url', 'placeholder' => 'https://.../video.mp4'] + ); + + // Поле: ширина мини-плеера + add_settings_field( + 'width', + 'Ширина мини-видео (px)', + 'est_field_number_cb', + 'est-floating-video', + 'est_main_section', + ['id' => 'width', 'min' => 80, 'max' => 300] + ); + + // Поле: высота мини-плеера + add_settings_field( + 'height', + 'Высота мини-видео (px)', + 'est_field_number_cb', + 'est-floating-video', + 'est_main_section', + ['id' => 'height', 'min' => 120, 'max' => 500] + ); + + // Поле: отступ снизу (bottom) + add_settings_field( + 'bottom_offset', + 'Отступ снизу (px) — полезно над чатом/кнопками', + 'est_field_number_cb', + 'est-floating-video', + 'est_main_section', + ['id' => 'bottom_offset', 'min' => 10, 'max' => 300] + ); + // Поле: показывать на мобильных +add_settings_field( + 'show_on_mobile', + 'Показывать на мобильных устройствах', + 'est_field_checkbox_cb', + 'est-floating-video', + 'est_main_section', + ['id' => 'show_on_mobile'] +); + + + +} +add_action('admin_init', 'est_floating_video_settings_init'); + +// Колбэки для полей +function est_main_section_callback() { + echo '

Настройте отображение плавающего промо-видео.

'; +} + +function est_field_checkbox_cb($args) { + $options = get_option('est_floating_video_options', []); + $value = isset($options[$args['id']]) ? $options[$args['id']] : '1'; + ?> + /> + + + + + px + +
+

+
+ +
+
+ true], 'names'); + + foreach ($post_types as $post_type) { + add_meta_box( + 'est_floating_video_meta', + 'Плавающее видео для этой записи', + 'est_floating_video_meta_box_callback', + $post_type, + 'side', + 'high' + ); + } +} +add_action('add_meta_boxes', 'est_floating_video_add_meta_box'); + +function est_floating_video_meta_box_callback($post) { + wp_nonce_field('est_floating_video_meta_nonce', 'est_floating_video_nonce'); + + $video_url = get_post_meta($post->ID, 'est_floating_video_url', true); + ?> +

+ +

+ + !empty($options['enabled']), + ]); +} +add_action('wp_enqueue_scripts', 'est_floating_video_enqueue'); + +function est_floating_video_output() { + $options = get_option('est_floating_video_options', []); + + // Глобальные настройки по умолчанию + $global_enabled = !empty($options['enabled']); + $show_on_mobile = isset($options['show_on_mobile']) ? (bool)$options['show_on_mobile'] : true; + $global_video_url = !empty($options['video_url']) ? $options['video_url'] : ''; + + // Определяем, нужно ли вообще показывать + if (!$global_enabled) { + return; + } + + // Проверка мобильного устройства + if (wp_is_mobile() && !$show_on_mobile) { + return; + } + + // Определяем видео: сначала индивидуальное, потом глобальное + $video_url = $global_video_url; + + if (is_singular()) { + $custom_url = get_post_meta(get_the_ID(), 'est_floating_video_url', true); + if (!empty($custom_url)) { + $video_url = $custom_url; + } + } + + // Если в итоге нет видео — выходим + if (empty($video_url)) { + return; + } + + $width = $options['width'] ?? 120; + $height = $options['height'] ?? 180; + $bottom = $options['bottom_offset'] ?? 20; + + $video_url = esc_url($video_url); + ?> +
+
+ + + +
+ +
+ +
+
+ 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 literal 0 HcmV?d00001 diff --git a/s1-custom-scroll-to-top/assets/arrow-up.svg b/s1-custom-scroll-to-top/assets/arrow-up.svg new file mode 100644 index 0000000..bf905ca --- /dev/null +++ b/s1-custom-scroll-to-top/assets/arrow-up.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/s1-custom-scroll-to-top/assets/path2.png b/s1-custom-scroll-to-top/assets/path2.png new file mode 100644 index 0000000000000000000000000000000000000000..09da062d9424bd8bdd7c57fe3f916f96cddfd286 GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa&H|6fVxZVz5N14{zaj-FC|Tkf zQ4*Y=R#Ki=l*&+EUaps!mtCBkSdglhUz9%kosASwQKqMhV~EDYw-+}GHW+ZUCLVAL zKcN$;9_eVUJGb%8l&Kp|#hznMVff$oc;Bmq9Svf}@f|y+1@XQ07h`G&cKLSgmTVJq z@9jfR6!|ZUZ!&P5&^CQa5Qp#6d%xS>Th55QyZl<$=e6eqU$*=47tOF0d7m9u4z!fP M)78&qol`;+05hyn1ONa4 literal 0 HcmV?d00001 diff --git a/s1-custom-scroll-to-top/assets/script.js b/s1-custom-scroll-to-top/assets/script.js new file mode 100644 index 0000000..6a011fa --- /dev/null +++ b/s1-custom-scroll-to-top/assets/script.js @@ -0,0 +1,32 @@ +jQuery(document).ready(function($) { + + const btn = $('#cstt-scroll-top'); + if (!btn.length) return; + + // Устанавливаем класс позиции + btn.addClass('cstt-' + csttData.position); + + // Устанавливаем изображение + const icon = btn.find('.cstt-icon'); + if (csttData.custom_img) { + icon.css('background-image', `url(${csttData.custom_img})`); + } else { + icon.css('background-image', `url(${csttData.default_img})`); + } + + // Показываем/скрываем кнопку + $(window).on('scroll', function() { + if ($(this).scrollTop() > 400) { + btn.addClass('show'); + } else { + btn.removeClass('show'); + } + }); + + // Клик → наверх + btn.on('click', function(e) { + e.preventDefault(); + $('html, body').animate({scrollTop: 0}, 500); + }); + +}); \ No newline at end of file diff --git a/s1-custom-scroll-to-top/assets/style.css b/s1-custom-scroll-to-top/assets/style.css new file mode 100644 index 0000000..9d9f263 --- /dev/null +++ b/s1-custom-scroll-to-top/assets/style.css @@ -0,0 +1,48 @@ +#cstt-scroll-top { + position: fixed; + z-index: 9999; + width: 50px; + height: 50px; + background: #333; + color: #fff; + border-radius: 50%; + box-shadow: 0 4px 10px rgba(0,0,0,0.25); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; + text-decoration: none; +} + +#cstt-scroll-top.show { + opacity: 0.85; + visibility: visible; +} + +#cstt-scroll-top:hover { + opacity: 1; + transform: scale(1.08); +} + +/* Позиции */ +.cstt-left-bottom { bottom: 25px; left: 25px; } +.cstt-right-bottom { bottom: 25px; right: 25px; } +.cstt-left-top { top: 25px; left: 25px; } +.cstt-right-top { top: 25px; right: 25px; } +.cstt-center-bottom { + bottom: 25px; + left: 50%; + transform: translateX(-50%); +} + +/* Иконка */ +.cstt-icon { + display: block; + width: 28px; + height: 28px; + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} \ No newline at end of file diff --git a/s1-custom-scroll-to-top/s1-custom-scroll-to-top.php b/s1-custom-scroll-to-top/s1-custom-scroll-to-top.php new file mode 100644 index 0000000..e9e2a75 --- /dev/null +++ b/s1-custom-scroll-to-top/s1-custom-scroll-to-top.php @@ -0,0 +1,218 @@ + $position, + 'custom_img' => $custom_img ? esc_url($custom_img) : '', + 'default_img' => CSTT_URL . 'assets/arrow-up.svg', + ]); +} +add_action('wp_enqueue_scripts', 'cstt_enqueue_frontend_assets'); + + +/** + * HTML кнопки (добавляем в footer) + */ +function cstt_render_button() { + ?> + + + + 'string', + 'sanitize_callback' => 'sanitize_text_field', + 'default' => 'right-bottom' + ]); + + register_setting('cstt_settings_group', 'cstt_custom_image', [ + 'type' => 'string', + 'sanitize_callback' => 'esc_url_raw', + ]); + + add_settings_section( + 'cstt_main_section', + 'Настройки кнопки «Наверх»', + '__return_false', + 'cstt-settings' + ); + + add_settings_field( + 'cstt_position', + 'Положение кнопки', + 'cstt_position_field_callback', + 'cstt-settings', + 'cstt_main_section' + ); + + add_settings_field( + 'cstt_custom_image', + 'Своя иконка (рекомендуется 64×64 px)', + 'cstt_image_field_callback', + 'cstt-settings', + 'cstt_main_section' + ); +} +add_action('admin_init', 'cstt_register_settings'); + + +function cstt_position_field_callback() { + $value = get_option('cstt_position', 'right-bottom'); + $positions = [ + 'left-bottom' => 'Левый нижний угол', + 'right-bottom' => 'Правый нижний угол (по умолчанию)', + 'left-top' => 'Левый верхний угол', + 'right-top' => 'Правый верхний угол', + 'center-bottom' => 'По центру снизу', + ]; + ?> + + +
+ + +

+ Вставьте прямую ссылку на изображение (PNG, JPG, SVG, WebP).
+ Рекомендуемый размер: 64×64 px или больше, квадратное, желательно с прозрачным фоном. +

+ +
+ Превью:
+ + + Изображение не отображается (проверьте ссылку) + +
+
+ + + +
+

Настройки кнопки «Наверх»

+
+ +
+
+ j35+Jw_+yN3K0|9~)++7kZ5abT- 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 W=Yadm%m%>!zU6+Omfgtz`uZPI+UDf| literal 0 HcmV?d00001 diff --git a/s1-gallery.zip b/s1-gallery.zip new file mode 100644 index 0000000000000000000000000000000000000000..0761c73192124ff6db96707179fa266bd4e397e9 GIT binary patch literal 18934 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 QFz^H65nvld#s$O!0P_>RZ2$lO literal 0 HcmV?d00001 diff --git a/s1-hide-admin/readme.txt b/s1-hide-admin/readme.txt new file mode 100644 index 0000000..49d2c2d --- /dev/null +++ b/s1-hide-admin/readme.txt @@ -0,0 +1,38 @@ +=== S1 Hide Admin === +Plugin URI: https://1studi.ru +Author: S1 +Author URI: https://1studi.ru +Requires at least: 5.5 +Tested up to: 6.6 +Stable tag: 1.0.0 +License: GPLv2 or later + +Очень простой и лёгкий плагин: скрывает wp-login.php и wp-admin за +собственным адресом-слагом. + +== Как это работает == + +* Прямые запросы к wp-login.php и к /wp-admin/ (для неавторизованных + посетителей) отдают обычную 404 — вместо формы входа или редиректа + на неё. +* Настоящая страница входа доступна только по адресу + ваш-сайт.ru/ваш-слаг (задаётся в Настройки → S1 Hide Admin). +* Все ссылки, которые генерирует само ядро WordPress (кнопка выхода, + «забыли пароль», регистрация), автоматически подменяются на новый + адрес — искать и патчить тему не нужно. +* admin-ajax.php и admin-post.php никогда не блокируются — обычные + формы и AJAX на сайте продолжают работать как обычно. +* При активации плагина сразу устанавливается слаг по умолчанию — + "login", чтобы сайт не остался незащищённым до первого захода + в настройки. Смените его на что-то менее предсказуемое. + +== Важно == + +Это простая мера "security through obscurity" — она прячет форму +входа от автоматического перебора и ботов-сканеров, но не заменяет +надёжный пароль, двухфакторную аутентификацию и своевременные +обновления WordPress. + +Обязательно запишите новый адрес входа после смены слага. Восстановить +доступ, если слаг забыт, можно только переименовав/удалив файл +плагина через FTP или файловый менеджер хостинга. diff --git a/s1-hide-admin/s1-hide-admin.php b/s1-hide-admin/s1-hide-admin.php new file mode 100644 index 0000000..e40899b --- /dev/null +++ b/s1-hide-admin/s1-hide-admin.php @@ -0,0 +1,232 @@ +404

Страница не найдена.

', + '404', + array( 'response' => 404 ) + ); + } + + /** Путь текущего запроса относительно корня сайта, без query-строки, в нижнем регистре, без слэшей по краям. */ + private static function get_request_path() { + $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : ''; + $path = wp_parse_url( $request_uri, PHP_URL_PATH ); + $path = is_string( $path ) ? $path : ''; + + $home_path = wp_parse_url( home_url( '/' ), PHP_URL_PATH ); + $home_path = is_string( $home_path ) ? trim( $home_path, '/' ) : ''; + + $path = trim( $path, '/' ); + + // Если сайт установлен в подпапке — отрезаем этот префикс от пути запроса. + if ( $home_path && 0 === strpos( $path, $home_path ) ) { + $path = trim( substr( $path, strlen( $home_path ) ), '/' ); + } + + return strtolower( $path ); + } + + private static function is_wp_admin_path( $path ) { + return 'wp-admin' === $path || 0 === strpos( $path, 'wp-admin/' ); + } + + /* ------------------------------------------------------------------ */ + /* Подмена ссылок, которые генерирует само ядро WordPress */ + /* ------------------------------------------------------------------ */ + + /** + * wp_login_url(), wp_logout_url(), wp_registration_url(), wp_lostpassword_url() + * в ядре всегда вызывают site_url('wp-login.php', 'login'|'login_post') — + * подменяем результат на наш слаг, чтобы нигде на сайте не светился + * настоящий адрес wp-login.php. + */ + public static function filter_login_url( $url, $path, $scheme = null ) { + if ( in_array( $scheme, array( 'login', 'login_post' ), true ) ) { + $url = str_replace( 'wp-login.php', self::get_slug(), $url ); + } + return $url; + } + + /* ------------------------------------------------------------------ */ + /* Настройки */ + /* ------------------------------------------------------------------ */ + + public static function get_slug() { + $slug = get_option( self::OPTION_KEY, self::DEFAULT_SLUG ); + return $slug ? $slug : self::DEFAULT_SLUG; + } + + public static function add_settings_page() { + add_options_page( + 'S1 Hide Admin', + 'S1 Hide Admin', + 'manage_options', + 's1-hide-admin', + array( __CLASS__, 'render_settings_page' ) + ); + } + + public static function maybe_save_settings() { + if ( empty( $_POST['s1_hide_admin_nonce'] ) || ! wp_verify_nonce( $_POST['s1_hide_admin_nonce'], self::NONCE_ACTION ) ) { + return; + } + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + $slug = isset( $_POST['s1_hide_admin_slug'] ) ? sanitize_title( wp_unslash( $_POST['s1_hide_admin_slug'] ) ) : ''; + + // Запрещаем очевидно ломающие значения: пустое, wp-admin, wp-login(.php). + $forbidden = array( '', 'wp-admin', 'wp-login', 'wp-login.php' ); + if ( in_array( $slug, $forbidden, true ) ) { + add_action( 'admin_notices', function () { + echo '

S1 Hide Admin: этот слаг использовать нельзя (пусто, wp-admin или wp-login). Настройка не сохранена.

'; + } ); + return; + } + + update_option( self::OPTION_KEY, $slug ); + + add_action( 'admin_notices', function () { + echo '

Адрес входа обновлён.

'; + } ); + } + + public static function render_settings_page() { + $slug = self::get_slug(); + $login_url = home_url( '/' . $slug ); + ?> +
+

S1 Hide Admin

+

Стандартные адреса wp-login.php и /wp-admin/ для неавторизованных посетителей скрыты — вместо них показывается обычная страница 404. Вход в панель управления возможен только по адресу ниже.

+ +
+

Важно: сохраните этот адрес, прежде чем закрывать страницу. Если вы забудете новый слаг, попасть в админку через обычный wp-login.php не получится — восстановить доступ можно будет только через переименование/удаление файла плагина по FTP или через файловый менеджер хостинга.

+
+ + + + + + + + +
+ + + + + + + + +
+
+ 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 YFV*`0JAr}u&qdU~iTvBwNdKAs1-Fb}jQ{`u literal 0 HcmV?d00001 diff --git a/s1-seo-meta.zip b/s1-seo-meta.zip new file mode 100644 index 0000000000000000000000000000000000000000..25bd017b109a126d04c0b1e3487ec68bb8eec7b8 GIT binary patch literal 10168 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 vG5qq$@3p^8{jH+@Tl)AJ`1mj2zfC6pH&&>pQ9J element showing the count. + * @param {number} len Current character length. + * @param {number} soft Ideal upper boundary (turns amber above this). + * @param {number} hard Hard limit (turns red at or above this). + */ + function updateCounter( $counter, len, soft, hard ) { + $counter.text( len ); + $counter + .removeClass( 's1-count-ok s1-count-warn s1-count-over' ) + .addClass( + len >= hard ? 's1-count-over' : + len >= soft ? 's1-count-warn' : + len > 0 ? 's1-count-ok' : '' + ); + } + + function initCounters() { + // Title field + var $titleInput = $( '#s1_seo_title' ); + var $titleCounter = $( '#s1_seo_title_count' ); + + if ( $titleInput.length && $titleCounter.length ) { + // Initialise on page load. + updateCounter( $titleCounter, $titleInput.val().length, 50, 60 ); + + $titleInput.on( 'input', function () { + updateCounter( $titleCounter, $( this ).val().length, 50, 60 ); + } ); + } + + // Description field + var $descInput = $( '#s1_seo_description' ); + var $descCounter = $( '#s1_seo_desc_count' ); + + if ( $descInput.length && $descCounter.length ) { + updateCounter( $descCounter, $descInput.val().length, 120, 160 ); + + $descInput.on( 'input', function () { + updateCounter( $descCounter, $( this ).val().length, 120, 160 ); + } ); + } + } + + $( document ).ready( initCounters ); + +} )( jQuery ); diff --git a/s1-seo-meta73/includes/class-s1-seo-meta-output.php b/s1-seo-meta73/includes/class-s1-seo-meta-output.php new file mode 100644 index 0000000..09e6039 --- /dev/null +++ b/s1-seo-meta73/includes/class-s1-seo-meta-output.php @@ -0,0 +1,109 @@ + and tags in the . + * + * @package S1_SEO_Meta + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +class S1_SEO_Meta_Output { + + public function __construct() { + // Output meta tags as early as possible in wp_head. + add_action( 'wp_head', array( $this, 'output_meta_tags' ), 1 ); + + // Override the document tag via wp_title (classic themes). + add_filter( 'wp_title', array( $this, 'filter_wp_title' ), 20, 1 ); + + // Override the document <title> tag via document_title_parts (block / modern themes). + add_filter( 'document_title_parts', array( $this, 'filter_document_title_parts' ), 20 ); + } + + /** + * Determine the SEO title and description for the current page and echo them. + */ + public function output_meta_tags() { + $seo_title = $this->get_current_title(); + $seo_desc = $this->get_current_description(); + + if ( $seo_desc ) { + echo '<meta name="description" content="' . esc_attr( $seo_desc ) . '" />' . "\n"; + } + } + + /** + * Filter wp_title() for classic themes that use it. + * + * @param string $title Current title string. + * @return string + */ + public function filter_wp_title( $title ) { + $seo_title = $this->get_current_title(); + return $seo_title ? $seo_title : $title; + } + + /** + * Filter document_title_parts for modern themes that use add_theme_support('title-tag'). + * + * @param array $parts Title parts array. + * @return array + */ + public function filter_document_title_parts( $parts ) { + $seo_title = $this->get_current_title(); + + if ( $seo_title ) { + // Replace the 'title' part; remove 'site' to avoid duplication. + $parts['title'] = $seo_title; + unset( $parts['tagline'], $parts['site'] ); + } + + return $parts; + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + /** + * Return the SEO title for whatever is currently being displayed. + * + * @return string Empty string when no custom title is set. + */ + private function get_current_title() { + if ( is_singular() ) { + return S1_SEO_Meta_Post::get_title( get_the_ID() ); + } + + if ( is_category() || is_tag() || is_tax() ) { + $term = get_queried_object(); + if ( $term instanceof WP_Term ) { + return S1_SEO_Meta_Term::get_title( $term->term_id ); + } + } + + return ''; + } + + /** + * Return the SEO description for whatever is currently being displayed. + * + * @return string Empty string when no custom description is set. + */ + private function get_current_description() { + if ( is_singular() ) { + return S1_SEO_Meta_Post::get_description( get_the_ID() ); + } + + if ( is_category() || is_tag() || is_tax() ) { + $term = get_queried_object(); + if ( $term instanceof WP_Term ) { + return S1_SEO_Meta_Term::get_description( $term->term_id ); + } + } + + return ''; + } +} diff --git a/s1-seo-meta73/includes/class-s1-seo-meta-post.php b/s1-seo-meta73/includes/class-s1-seo-meta-post.php new file mode 100644 index 0000000..9bc8420 --- /dev/null +++ b/s1-seo-meta73/includes/class-s1-seo-meta-post.php @@ -0,0 +1,164 @@ +<?php +/** + * Handles saving and retrieving SEO meta for posts, pages and custom post types. + * + * @package S1_SEO_Meta + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +class S1_SEO_Meta_Post { + + /** + * Meta key for SEO title. + */ + const META_TITLE = '_s1_seo_title'; + + /** + * Meta key for SEO description. + */ + const META_DESC = '_s1_seo_description'; + + public function __construct() { + add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) ); + add_action( 'save_post', array( $this, 'save_meta' ), 10, 2 ); + } + + /** + * Register the meta box for all public post types. + */ + public function add_meta_box() { + $post_types = get_post_types( array( 'public' => true ), 'names' ); + + foreach ( $post_types as $post_type ) { + add_meta_box( + 's1-seo-meta-box', + __( 'S1 SEO: Title & Description', 's1-seo-meta' ), + array( $this, 'render_meta_box' ), + $post_type, + 'normal', + 'high' + ); + } + } + + /** + * Render the meta box HTML. + * + * @param WP_Post $post Current post object. + */ + public function render_meta_box( $post ) { + wp_nonce_field( 's1_seo_meta_save', 's1_seo_meta_nonce' ); + + $seo_title = get_post_meta( $post->ID, self::META_TITLE, true ); + $seo_desc = get_post_meta( $post->ID, self::META_DESC, true ); + ?> + <div class="s1-seo-wrap"> + <div class="s1-seo-field"> + <label for="s1_seo_title"> + <?php esc_html_e( 'SEO Title', 's1-seo-meta' ); ?> + </label> + <input + type="text" + id="s1_seo_title" + name="s1_seo_title" + value="<?php echo esc_attr( $seo_title ); ?>" + placeholder="<?php echo esc_attr( get_the_title( $post->ID ) ); ?>" + maxlength="666" + /> + <div class="s1-seo-counter"> + <span class="s1-seo-count" id="s1_seo_title_count">0</span>/60 + <?php esc_html_e( 'characters (recommended: 50–60)', 's1-seo-meta' ); ?> + </div> + </div> + + <div class="s1-seo-field"> + <label for="s1_seo_description"> + <?php esc_html_e( 'SEO Description', 's1-seo-meta' ); ?> + </label> + <textarea + id="s1_seo_description" + name="s1_seo_description" + rows="3" + maxlength="666" + placeholder="<?php esc_attr_e( 'Write a short description of this page…', 's1-seo-meta' ); ?>" + ><?php echo esc_textarea( $seo_desc ); ?></textarea> + <div class="s1-seo-counter"> + <span class="s1-seo-count" id="s1_seo_desc_count">0</span>/160 + <?php esc_html_e( 'characters (recommended: 120–160)', 's1-seo-meta' ); ?> + </div> + </div> + </div> + <?php + } + + /** + * Save SEO meta fields when the post is saved. + * + * @param int $post_id Post ID. + * @param WP_Post $post Post object. + */ + public function save_meta( $post_id, $post ) { + // Verify nonce. + if ( + ! isset( $_POST['s1_seo_meta_nonce'] ) || + ! wp_verify_nonce( sanitize_key( $_POST['s1_seo_meta_nonce'] ), 's1_seo_meta_save' ) + ) { + return; + } + + // Skip autosaves and revisions. + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { + return; + } + if ( wp_is_post_revision( $post_id ) ) { + return; + } + + // Check permissions. + $post_type_obj = get_post_type_object( $post->post_type ); + if ( ! current_user_can( $post_type_obj->cap->edit_post, $post_id ) ) { + return; + } + + // Save title. + if ( isset( $_POST['s1_seo_title'] ) ) { + update_post_meta( + $post_id, + self::META_TITLE, + sanitize_text_field( wp_unslash( $_POST['s1_seo_title'] ) ) + ); + } + + // Save description. + if ( isset( $_POST['s1_seo_description'] ) ) { + update_post_meta( + $post_id, + self::META_DESC, + sanitize_textarea_field( wp_unslash( $_POST['s1_seo_description'] ) ) + ); + } + } + + /** + * Static helper: retrieve SEO title for a given post. + * + * @param int $post_id Post ID. + * @return string + */ + public static function get_title( $post_id ) { + return (string) get_post_meta( $post_id, self::META_TITLE, true ); + } + + /** + * Static helper: retrieve SEO description for a given post. + * + * @param int $post_id Post ID. + * @return string + */ + public static function get_description( $post_id ) { + return (string) get_post_meta( $post_id, self::META_DESC, true ); + } +} diff --git a/s1-seo-meta73/includes/class-s1-seo-meta-term.php b/s1-seo-meta73/includes/class-s1-seo-meta-term.php new file mode 100644 index 0000000..fc73b87 --- /dev/null +++ b/s1-seo-meta73/includes/class-s1-seo-meta-term.php @@ -0,0 +1,196 @@ +<?php +/** + * Handles saving and retrieving SEO meta for taxonomy terms + * (categories, tags, and all public custom taxonomies). + * + * @package S1_SEO_Meta + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +class S1_SEO_Meta_Term { + + /** + * Option-table key prefix used to store term meta (WP < 4.4 fallback aware). + * From WP 4.4+ term meta is stored in wp_termmeta via get/update_term_meta(). + */ + const META_TITLE = 's1_seo_title'; + const META_DESC = 's1_seo_description'; + + public function __construct() { + add_action( 'init', array( $this, 'register_term_hooks' ) ); + } + + /** + * Register add/edit/save hooks for every public taxonomy. + */ + public function register_term_hooks() { + $taxonomies = get_taxonomies( array( 'public' => true ), 'names' ); + + foreach ( $taxonomies as $taxonomy ) { + // Fields on the "Add new term" form. + add_action( "{$taxonomy}_add_form_fields", array( $this, 'add_form_fields' ) ); + // Fields on the "Edit term" form. + add_action( "{$taxonomy}_edit_form_fields", array( $this, 'edit_form_fields' ), 10, 2 ); + // Save on create. + add_action( "created_{$taxonomy}", array( $this, 'save_term_meta' ), 10, 2 ); + // Save on update. + add_action( "edited_{$taxonomy}", array( $this, 'save_term_meta' ), 10, 2 ); + } + } + + /** + * Render fields on the "Add new term" screen. + * + * @param string $taxonomy Current taxonomy slug. + */ + public function add_form_fields( $taxonomy ) { + wp_nonce_field( 's1_seo_term_save', 's1_seo_term_nonce' ); + ?> + <div class="form-field s1-seo-term-wrap"> + <h3 class="s1-seo-term-heading"><?php esc_html_e( 'S1 SEO', 's1-seo-meta' ); ?></h3> + + <div class="form-field"> + <label for="s1_seo_title"><?php esc_html_e( 'SEO Title', 's1-seo-meta' ); ?></label> + <input + type="text" + id="s1_seo_title" + name="s1_seo_title" + value="" + maxlength="666" + /> + <p class="s1-seo-counter"> + <span id="s1_seo_title_count">0</span>/60 <?php esc_html_e( 'characters', 's1-seo-meta' ); ?> + </p> + </div> + + <div class="form-field"> + <label for="s1_seo_description"><?php esc_html_e( 'SEO Description', 's1-seo-meta' ); ?></label> + <textarea + id="s1_seo_description" + name="s1_seo_description" + rows="3" + maxlength="666" + ></textarea> + <p class="s1-seo-counter"> + <span id="s1_seo_desc_count">0</span>/160 <?php esc_html_e( 'characters', 's1-seo-meta' ); ?> + </p> + </div> + </div> + <?php + } + + /** + * Render fields on the "Edit term" screen. + * + * @param WP_Term $term Current term object. + * @param string $taxonomy Current taxonomy slug. + */ + public function edit_form_fields( $term, $taxonomy ) { + wp_nonce_field( 's1_seo_term_save', 's1_seo_term_nonce' ); + + $seo_title = get_term_meta( $term->term_id, self::META_TITLE, true ); + $seo_desc = get_term_meta( $term->term_id, self::META_DESC, true ); + ?> + <tr class="form-field s1-seo-term-row"> + <td colspan="2"> + <h3 class="s1-seo-term-heading"><?php esc_html_e( 'S1 SEO', 's1-seo-meta' ); ?></h3> + </td> + </tr> + + <tr class="form-field"> + <th scope="row"> + <label for="s1_seo_title"><?php esc_html_e( 'SEO Title', 's1-seo-meta' ); ?></label> + </th> + <td> + <input + type="text" + id="s1_seo_title" + name="s1_seo_title" + value="<?php echo esc_attr( $seo_title ); ?>" + placeholder="<?php echo esc_attr( $term->name ); ?>" + maxlength="60" + /> + <p class="description s1-seo-counter"> + <span id="s1_seo_title_count"><?php echo esc_html( mb_strlen( $seo_title ) ); ?></span>/60 + <?php esc_html_e( 'characters (recommended: 50–60)', 's1-seo-meta' ); ?> + </p> + </td> + </tr> + + <tr class="form-field"> + <th scope="row"> + <label for="s1_seo_description"><?php esc_html_e( 'SEO Description', 's1-seo-meta' ); ?></label> + </th> + <td> + <textarea + id="s1_seo_description" + name="s1_seo_description" + rows="3" + maxlength="160" + ><?php echo esc_textarea( $seo_desc ); ?></textarea> + <p class="description s1-seo-counter"> + <span id="s1_seo_desc_count"><?php echo esc_html( mb_strlen( $seo_desc ) ); ?></span>/160 + <?php esc_html_e( 'characters (recommended: 120–160)', 's1-seo-meta' ); ?> + </p> + </td> + </tr> + <?php + } + + /** + * Save term SEO meta. + * + * @param int $term_id Term ID. + */ + public function save_term_meta( $term_id ) { + if ( + ! isset( $_POST['s1_seo_term_nonce'] ) || + ! wp_verify_nonce( sanitize_key( $_POST['s1_seo_term_nonce'] ), 's1_seo_term_save' ) + ) { + return; + } + + if ( ! current_user_can( 'manage_categories' ) ) { + return; + } + + if ( isset( $_POST['s1_seo_title'] ) ) { + update_term_meta( + $term_id, + self::META_TITLE, + sanitize_text_field( wp_unslash( $_POST['s1_seo_title'] ) ) + ); + } + + if ( isset( $_POST['s1_seo_description'] ) ) { + update_term_meta( + $term_id, + self::META_DESC, + sanitize_textarea_field( wp_unslash( $_POST['s1_seo_description'] ) ) + ); + } + } + + /** + * Static helper: retrieve SEO title for a term. + * + * @param int $term_id Term ID. + * @return string + */ + public static function get_title( $term_id ) { + return (string) get_term_meta( $term_id, self::META_TITLE, true ); + } + + /** + * Static helper: retrieve SEO description for a term. + * + * @param int $term_id Term ID. + * @return string + */ + public static function get_description( $term_id ) { + return (string) get_term_meta( $term_id, self::META_DESC, true ); + } +} diff --git a/s1-seo-meta73/languages/s1-seo-meta.pot b/s1-seo-meta73/languages/s1-seo-meta.pot new file mode 100644 index 0000000..edc23d6 --- /dev/null +++ b/s1-seo-meta73/languages/s1-seo-meta.pot @@ -0,0 +1,65 @@ +# Copyright (C) 2024 S1 +# This file is distributed under the same license as the S1 SEO Meta plugin. +msgid "" +msgstr "" +"Project-Id-Version: S1 SEO Meta 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-01-01 00:00:00+00:00\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"X-Generator: WP-CLI\n" +"X-Domain: s1-seo-meta\n" + +#. Plugin Name of the plugin +msgid "S1 SEO Meta" +msgstr "" + +#. Plugin URI of the plugin +msgid "https://github.com/S1/s1-seo-meta" +msgstr "" + +#. Description of the plugin +msgid "Добавляет мета-теги Title и Description для страниц, публикаций, рубрик, категорий и других типов записей." +msgstr "" + +#. Author of the plugin +msgid "S1" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +msgid "S1 SEO: Title & Description" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +#: includes/class-s1-seo-meta-term.php +msgid "SEO Title" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +#: includes/class-s1-seo-meta-term.php +msgid "SEO Description" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +msgid "characters (recommended: 50–60)" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +msgid "characters (recommended: 120–160)" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +msgid "Write a short description of this page…" +msgstr "" + +#: includes/class-s1-seo-meta-term.php +msgid "S1 SEO" +msgstr "" + +#: includes/class-s1-seo-meta-term.php +msgid "characters" +msgstr "" diff --git a/s1-seo-meta73/readme.txt b/s1-seo-meta73/readme.txt new file mode 100644 index 0000000..dbabd91 --- /dev/null +++ b/s1-seo-meta73/readme.txt @@ -0,0 +1,48 @@ +=== S1 SEO Meta === +Contributors: s1 +Tags: seo, meta, title, description, meta tags +Requires at least: 5.8 +Tested up to: 6.5 +Requires PHP: 7.4 +Stable tag: 1.0.0 +License: GPLv2 or later +License URI: https://www.gnu.org/licenses/gpl-2.0.html + +Adds custom SEO Title and Description meta fields to posts, pages, custom post types, categories, tags, and all public taxonomies. + +== Description == + +S1 SEO Meta adds a lightweight, no-bloat SEO meta box to every post/page editor and taxonomy term screen in the WordPress admin. Simply fill in your desired **SEO Title** and **SEO Description** and the plugin will: + +* Output `<meta name="description">` in the `<head>`. +* Override the page `<title>` tag (supports both classic `wp_title` and modern `document_title_parts` hooks). +* Show a live character counter with colour-coded feedback (green / amber / red). + +Works with posts, pages, custom post types, categories, tags, and all registered public taxonomies. + +== Installation == + +1. Upload the `s1-seo-meta` folder to `/wp-content/plugins/`. +2. Activate the plugin from the **Plugins** menu in WordPress admin. +3. Open any post, page, or term — the **S1 SEO: Title & Description** box will appear. + +== Frequently Asked Questions == + += Will this conflict with Yoast SEO or Rank Math? = +It may conflict if both plugins try to output a `<meta name="description">`. It is recommended to use only one SEO plugin at a time. + += Does it support custom post types? = +Yes. The meta box is added automatically to every post type registered with `public => true`. + += Does it support custom taxonomies? = +Yes. Fields appear on every taxonomy registered with `public => true`. + +== Changelog == + += 1.0.0 = +* Initial release. + +== Upgrade Notice == + += 1.0.0 = +First release. diff --git a/s1-seo-meta73/s1-seo-meta.php b/s1-seo-meta73/s1-seo-meta.php new file mode 100644 index 0000000..2dc6bfc --- /dev/null +++ b/s1-seo-meta73/s1-seo-meta.php @@ -0,0 +1,56 @@ +<?php +/** + * Plugin Name: S1 SEO Meta + * Plugin URI: https://1studi.ru + * Description: Добавляет мета-теги Title и Description для страниц, публикаций, рубрик, категорий и других типов записей. + * Version: 1.0.1 + * Requires at least: 5.8 + * Requires PHP: 7.3 + * Author: S1 + * License: GPL v2 or later + * License URI: https://www.gnu.org/licenses/gpl-2.0.html + * Text Domain: s1-seo-meta + * Domain Path: /languages + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +define( 'S1_SEO_META_VERSION', '1.0.0' ); +define( 'S1_SEO_META_FILE', __FILE__ ); +define( 'S1_SEO_META_DIR', plugin_dir_path( __FILE__ ) ); +define( 'S1_SEO_META_URL', plugin_dir_url( __FILE__ ) ); + +/** + * Load plugin text domain for translations. + */ +function s1_seo_meta_load_textdomain() { + load_plugin_textdomain( 's1-seo-meta', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); +} +add_action( 'plugins_loaded', 's1_seo_meta_load_textdomain' ); + +/** + * Load required files. + */ +require_once S1_SEO_META_DIR . 'includes/class-s1-seo-meta-post.php'; +require_once S1_SEO_META_DIR . 'includes/class-s1-seo-meta-term.php'; +require_once S1_SEO_META_DIR . 'includes/class-s1-seo-meta-output.php'; + +if ( is_admin() ) { + require_once S1_SEO_META_DIR . 'admin/class-s1-seo-meta-admin.php'; +} + +/** + * Initialise all plugin components. + */ +function s1_seo_meta_init() { + new S1_SEO_Meta_Post(); + new S1_SEO_Meta_Term(); + new S1_SEO_Meta_Output(); + + if ( is_admin() ) { + new S1_SEO_Meta_Admin(); + } +} +add_action( 'init', 's1_seo_meta_init' ); diff --git a/s1-seo-meta73/s1-seo-meta73.zip b/s1-seo-meta73/s1-seo-meta73.zip new file mode 100644 index 0000000000000000000000000000000000000000..e6560c21e1f02936e12b82d7bc42929fed39fedd GIT binary patch literal 10168 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%0DRvj-<>OF7)#Zqq4Rj*nbw(p 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 literal 0 HcmV?d00001 diff --git a/s1-seo-meta74.zip b/s1-seo-meta74.zip new file mode 100644 index 0000000000000000000000000000000000000000..135c55530d39af94ed14ab0ba2f0ff01c37a3c03 GIT binary patch literal 22525 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 Mt^k?~G@XF~07?yfzyJUM literal 0 HcmV?d00001 diff --git a/s1-seo-meta74/admin/class-s1-seo-meta-admin.php b/s1-seo-meta74/admin/class-s1-seo-meta-admin.php new file mode 100644 index 0000000..dbb10ec --- /dev/null +++ b/s1-seo-meta74/admin/class-s1-seo-meta-admin.php @@ -0,0 +1,45 @@ +<?php +/** + * Admin-only bootstrap: enqueues CSS and JS for the meta box and term forms. + * + * @package S1_SEO_Meta + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +class S1_SEO_Meta_Admin { + + public function __construct() { + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); + } + + /** + * Enqueue admin styles and scripts on relevant screens. + * + * @param string $hook_suffix Current admin page hook. + */ + public function enqueue_assets( $hook_suffix ) { + $screens = array( 'post.php', 'post-new.php', 'edit-tags.php', 'term.php' ); + + if ( ! in_array( $hook_suffix, $screens, true ) ) { + return; + } + + wp_enqueue_style( + 's1-seo-meta-admin', + S1_SEO_META_URL . 'admin/css/s1-seo-meta-admin.css', + array(), + S1_SEO_META_VERSION + ); + + wp_enqueue_script( + 's1-seo-meta-admin', + S1_SEO_META_URL . 'admin/js/s1-seo-meta-admin.js', + array( 'jquery' ), + S1_SEO_META_VERSION, + true + ); + } +} diff --git a/s1-seo-meta74/admin/css/s1-seo-meta-admin.css b/s1-seo-meta74/admin/css/s1-seo-meta-admin.css new file mode 100644 index 0000000..7876905 --- /dev/null +++ b/s1-seo-meta74/admin/css/s1-seo-meta-admin.css @@ -0,0 +1,108 @@ +/* ========================================================================== + S1 SEO Meta — Admin Styles + ========================================================================== */ + +/* ---- Meta box wrapper --------------------------------------------------- */ +.s1-seo-wrap { + padding: 4px 0; +} + +.s1-seo-field { + margin-bottom: 16px; +} + +.s1-seo-field:last-child { + margin-bottom: 0; +} + +/* ---- Labels ------------------------------------------------------------- */ +.s1-seo-field label, +.s1-seo-term-heading { + display: block; + font-weight: 600; + font-size: 13px; + margin-bottom: 6px; + color: #1d2327; +} + +.s1-seo-term-heading { + font-size: 14px; + border-bottom: 1px solid #ddd; + padding-bottom: 8px; + margin-top: 4px; +} + +/* ---- Inputs ------------------------------------------------------------- */ +.s1-seo-field input[type="text"], +.s1-seo-field textarea { + width: 100%; + max-width: 100%; + box-sizing: border-box; + border: 1px solid #8c8f94; + border-radius: 4px; + padding: 8px 10px; + font-size: 13px; + line-height: 1.5; + color: #1d2327; + background: #fff; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +.s1-seo-field input[type="text"]:focus, +.s1-seo-field textarea:focus { + border-color: #2271b1; + box-shadow: 0 0 0 1px #2271b1; + outline: none; +} + +.s1-seo-field textarea { + resize: vertical; + min-height: 70px; +} + +/* ---- Character counter -------------------------------------------------- */ +.s1-seo-counter { + font-size: 11px; + color: #646970; + margin-top: 5px; +} + +.s1-seo-count { + font-weight: 600; +} + +.s1-seo-count.s1-count-ok { + color: #00a32a; /* green */ +} + +.s1-seo-count.s1-count-warn { + color: #dba617; /* amber */ +} + +.s1-seo-count.s1-count-over { + color: #d63638; /* red */ +} + +/* ---- Term form tweaks --------------------------------------------------- */ +.s1-seo-term-row td { + padding-bottom: 0 !important; +} + +#s1-seo-meta-box .inside { + padding: 12px; +} + +/* ---- wp_editor styles for term forms ----------------------------------- */ +.s1-seo-term-wrap .wp-editor-container { + border: 1px solid #8c8f94; + border-radius: 4px; + margin-top: 5px; +} + +.s1-seo-term-wrap .wp-editor-area { + min-height: 100px; +} + +.s1-seo-term-wrap .mce-toolbar { + border-bottom: 1px solid #ddd !important; +} diff --git a/s1-seo-meta74/admin/js/s1-seo-meta-admin.js b/s1-seo-meta74/admin/js/s1-seo-meta-admin.js new file mode 100644 index 0000000..7f671de --- /dev/null +++ b/s1-seo-meta74/admin/js/s1-seo-meta-admin.js @@ -0,0 +1,111 @@ +/** + * S1 SEO Meta — Admin Scripts + * Live character counter with colour feedback. + * Supports both regular textareas and TinyMCE editors (wp_editor). + */ +( function ( $ ) { + 'use strict'; + + /** + * Update a counter element with the current length and apply colour classes. + * + * @param {jQuery} $counter The <span> element showing the count. + * @param {number} len Current character length. + * @param {number} soft Ideal upper boundary (turns amber above this). + * @param {number} hard Hard limit (turns red at or above this). + */ + function updateCounter( $counter, len, soft, hard ) { + $counter.text( len ); + $counter + .removeClass( 's1-count-ok s1-count-warn s1-count-over' ) + .addClass( + len >= hard ? 's1-count-over' : + len >= soft ? 's1-count-warn' : + len > 0 ? 's1-count-ok' : '' + ); + } + + /** + * Get content from TinyMCE editor or fallback to textarea. + * + * @param {string} editorId The ID of the editor/textarea. + * @return {string} The editor content. + */ + function getEditorContent( editorId ) { + if ( typeof tinymce !== 'undefined' && tinymce.get( editorId ) ) { + return tinymce.get( editorId ).getContent(); + } + return $( '#' + editorId ).val() || ''; + } + + /** + * Count characters in HTML content (strips tags). + * + * @param {string} html HTML content. + * @return {number} Character count without HTML tags. + */ + function countTextChars( html ) { + var tempDiv = document.createElement( 'div' ); + tempDiv.innerHTML = html; + return tempDiv.textContent || tempDiv.innerText || ''; + } + + function initCounters() { + // Title field (always regular input) + var $titleInput = $( '#s1_seo_title' ); + var $titleCounter = $( '#s1_seo_title_count' ); + + if ( $titleInput.length && $titleCounter.length ) { + // Initialise on page load. + updateCounter( $titleCounter, $titleInput.val().length, 50, 60 ); + + $titleInput.on( 'input', function () { + updateCounter( $titleCounter, $( this ).val().length, 50, 60 ); + } ); + } + + // Description field (may be TinyMCE or textarea) + var $descCounter = $( '#s1_seo_desc_count' ); + + if ( $descCounter.length ) { + var editorId = 's1_seo_description'; + var initialContent = getEditorContent( editorId ); + var initialCount = countTextChars( initialContent ).length; + + // Initialise on page load. + updateCounter( $descCounter, initialCount, 120, 160 ); + + // Handle TinyMCE editor changes + if ( typeof tinymce !== 'undefined' ) { + tinymce.on( 'AddEditor', function( e ) { + if ( e.editor.id === editorId ) { + e.editor.on( 'keyup change', function() { + var content = e.editor.getContent(); + var count = countTextChars( content ).length; + updateCounter( $descCounter, count, 120, 160 ); + } ); + } + } ); + + // If editor already exists + if ( tinymce.get( editorId ) ) { + tinymce.get( editorId ).on( 'keyup change', function() { + var content = tinymce.get( editorId ).getContent(); + var count = countTextChars( content ).length; + updateCounter( $descCounter, count, 120, 160 ); + } ); + } + } + + // Fallback for regular textarea + $( '#' + editorId ).on( 'input', function () { + var content = $( this ).val(); + var count = countTextChars( content ).length; + updateCounter( $descCounter, count, 120, 160 ); + } ); + } + } + + $( document ).ready( initCounters ); + +} )( jQuery ); diff --git a/s1-seo-meta74/includes/class-s1-seo-meta-output.php b/s1-seo-meta74/includes/class-s1-seo-meta-output.php new file mode 100644 index 0000000..09e6039 --- /dev/null +++ b/s1-seo-meta74/includes/class-s1-seo-meta-output.php @@ -0,0 +1,109 @@ +<?php +/** + * Outputs <title> and <meta name="description"> tags in the <head>. + * + * @package S1_SEO_Meta + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +class S1_SEO_Meta_Output { + + public function __construct() { + // Output meta tags as early as possible in wp_head. + add_action( 'wp_head', array( $this, 'output_meta_tags' ), 1 ); + + // Override the document <title> tag via wp_title (classic themes). + add_filter( 'wp_title', array( $this, 'filter_wp_title' ), 20, 1 ); + + // Override the document <title> tag via document_title_parts (block / modern themes). + add_filter( 'document_title_parts', array( $this, 'filter_document_title_parts' ), 20 ); + } + + /** + * Determine the SEO title and description for the current page and echo them. + */ + public function output_meta_tags() { + $seo_title = $this->get_current_title(); + $seo_desc = $this->get_current_description(); + + if ( $seo_desc ) { + echo '<meta name="description" content="' . esc_attr( $seo_desc ) . '" />' . "\n"; + } + } + + /** + * Filter wp_title() for classic themes that use it. + * + * @param string $title Current title string. + * @return string + */ + public function filter_wp_title( $title ) { + $seo_title = $this->get_current_title(); + return $seo_title ? $seo_title : $title; + } + + /** + * Filter document_title_parts for modern themes that use add_theme_support('title-tag'). + * + * @param array $parts Title parts array. + * @return array + */ + public function filter_document_title_parts( $parts ) { + $seo_title = $this->get_current_title(); + + if ( $seo_title ) { + // Replace the 'title' part; remove 'site' to avoid duplication. + $parts['title'] = $seo_title; + unset( $parts['tagline'], $parts['site'] ); + } + + return $parts; + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + /** + * Return the SEO title for whatever is currently being displayed. + * + * @return string Empty string when no custom title is set. + */ + private function get_current_title() { + if ( is_singular() ) { + return S1_SEO_Meta_Post::get_title( get_the_ID() ); + } + + if ( is_category() || is_tag() || is_tax() ) { + $term = get_queried_object(); + if ( $term instanceof WP_Term ) { + return S1_SEO_Meta_Term::get_title( $term->term_id ); + } + } + + return ''; + } + + /** + * Return the SEO description for whatever is currently being displayed. + * + * @return string Empty string when no custom description is set. + */ + private function get_current_description() { + if ( is_singular() ) { + return S1_SEO_Meta_Post::get_description( get_the_ID() ); + } + + if ( is_category() || is_tag() || is_tax() ) { + $term = get_queried_object(); + if ( $term instanceof WP_Term ) { + return S1_SEO_Meta_Term::get_description( $term->term_id ); + } + } + + return ''; + } +} diff --git a/s1-seo-meta74/includes/class-s1-seo-meta-post.php b/s1-seo-meta74/includes/class-s1-seo-meta-post.php new file mode 100644 index 0000000..9bc8420 --- /dev/null +++ b/s1-seo-meta74/includes/class-s1-seo-meta-post.php @@ -0,0 +1,164 @@ +<?php +/** + * Handles saving and retrieving SEO meta for posts, pages and custom post types. + * + * @package S1_SEO_Meta + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +class S1_SEO_Meta_Post { + + /** + * Meta key for SEO title. + */ + const META_TITLE = '_s1_seo_title'; + + /** + * Meta key for SEO description. + */ + const META_DESC = '_s1_seo_description'; + + public function __construct() { + add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) ); + add_action( 'save_post', array( $this, 'save_meta' ), 10, 2 ); + } + + /** + * Register the meta box for all public post types. + */ + public function add_meta_box() { + $post_types = get_post_types( array( 'public' => true ), 'names' ); + + foreach ( $post_types as $post_type ) { + add_meta_box( + 's1-seo-meta-box', + __( 'S1 SEO: Title & Description', 's1-seo-meta' ), + array( $this, 'render_meta_box' ), + $post_type, + 'normal', + 'high' + ); + } + } + + /** + * Render the meta box HTML. + * + * @param WP_Post $post Current post object. + */ + public function render_meta_box( $post ) { + wp_nonce_field( 's1_seo_meta_save', 's1_seo_meta_nonce' ); + + $seo_title = get_post_meta( $post->ID, self::META_TITLE, true ); + $seo_desc = get_post_meta( $post->ID, self::META_DESC, true ); + ?> + <div class="s1-seo-wrap"> + <div class="s1-seo-field"> + <label for="s1_seo_title"> + <?php esc_html_e( 'SEO Title', 's1-seo-meta' ); ?> + </label> + <input + type="text" + id="s1_seo_title" + name="s1_seo_title" + value="<?php echo esc_attr( $seo_title ); ?>" + placeholder="<?php echo esc_attr( get_the_title( $post->ID ) ); ?>" + maxlength="666" + /> + <div class="s1-seo-counter"> + <span class="s1-seo-count" id="s1_seo_title_count">0</span>/60 + <?php esc_html_e( 'characters (recommended: 50–60)', 's1-seo-meta' ); ?> + </div> + </div> + + <div class="s1-seo-field"> + <label for="s1_seo_description"> + <?php esc_html_e( 'SEO Description', 's1-seo-meta' ); ?> + </label> + <textarea + id="s1_seo_description" + name="s1_seo_description" + rows="3" + maxlength="666" + placeholder="<?php esc_attr_e( 'Write a short description of this page…', 's1-seo-meta' ); ?>" + ><?php echo esc_textarea( $seo_desc ); ?></textarea> + <div class="s1-seo-counter"> + <span class="s1-seo-count" id="s1_seo_desc_count">0</span>/160 + <?php esc_html_e( 'characters (recommended: 120–160)', 's1-seo-meta' ); ?> + </div> + </div> + </div> + <?php + } + + /** + * Save SEO meta fields when the post is saved. + * + * @param int $post_id Post ID. + * @param WP_Post $post Post object. + */ + public function save_meta( $post_id, $post ) { + // Verify nonce. + if ( + ! isset( $_POST['s1_seo_meta_nonce'] ) || + ! wp_verify_nonce( sanitize_key( $_POST['s1_seo_meta_nonce'] ), 's1_seo_meta_save' ) + ) { + return; + } + + // Skip autosaves and revisions. + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { + return; + } + if ( wp_is_post_revision( $post_id ) ) { + return; + } + + // Check permissions. + $post_type_obj = get_post_type_object( $post->post_type ); + if ( ! current_user_can( $post_type_obj->cap->edit_post, $post_id ) ) { + return; + } + + // Save title. + if ( isset( $_POST['s1_seo_title'] ) ) { + update_post_meta( + $post_id, + self::META_TITLE, + sanitize_text_field( wp_unslash( $_POST['s1_seo_title'] ) ) + ); + } + + // Save description. + if ( isset( $_POST['s1_seo_description'] ) ) { + update_post_meta( + $post_id, + self::META_DESC, + sanitize_textarea_field( wp_unslash( $_POST['s1_seo_description'] ) ) + ); + } + } + + /** + * Static helper: retrieve SEO title for a given post. + * + * @param int $post_id Post ID. + * @return string + */ + public static function get_title( $post_id ) { + return (string) get_post_meta( $post_id, self::META_TITLE, true ); + } + + /** + * Static helper: retrieve SEO description for a given post. + * + * @param int $post_id Post ID. + * @return string + */ + public static function get_description( $post_id ) { + return (string) get_post_meta( $post_id, self::META_DESC, true ); + } +} diff --git a/s1-seo-meta74/includes/class-s1-seo-meta-term.php b/s1-seo-meta74/includes/class-s1-seo-meta-term.php new file mode 100644 index 0000000..e557cb9 --- /dev/null +++ b/s1-seo-meta74/includes/class-s1-seo-meta-term.php @@ -0,0 +1,236 @@ +<?php +/** + * Handles saving and retrieving SEO meta for taxonomy terms + * (categories, tags, and all public custom taxonomies). + * + * @package S1_SEO_Meta + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +class S1_SEO_Meta_Term { + + /** + * Option-table key prefix used to store term meta (WP < 4.4 fallback aware). + * From WP 4.4+ term meta is stored in wp_termmeta via get/update_term_meta(). + */ + const META_TITLE = 's1_seo_title'; + const META_DESC = 's1_seo_description'; + + public function __construct() { + add_action( 'init', array( $this, 'register_term_hooks' ) ); + } + + /** + * Register add/edit/save hooks for every public taxonomy. + */ + public function register_term_hooks() { + $taxonomies = get_taxonomies( array( 'public' => true ), 'names' ); + + foreach ( $taxonomies as $taxonomy ) { + // Fields on the "Add new term" form. + add_action( "{$taxonomy}_add_form_fields", array( $this, 'add_form_fields' ) ); + // Fields on the "Edit term" form. + add_action( "{$taxonomy}_edit_form_fields", array( $this, 'edit_form_fields' ), 10, 2 ); + // Save on create. + add_action( "created_{$taxonomy}", array( $this, 'save_term_meta' ), 10, 2 ); + // Save on update. + add_action( "edited_{$taxonomy}", array( $this, 'save_term_meta' ), 10, 2 ); + } + } + + /** + * Render fields on the "Add new term" screen. + * + * @param string $taxonomy Current taxonomy slug. + */ + public function add_form_fields( $taxonomy ) { + wp_nonce_field( 's1_seo_term_save', 's1_seo_term_nonce' ); + ?> + <div class="form-field s1-seo-term-wrap"> + <h3 class="s1-seo-term-heading"><?php esc_html_e( 'S1 SEO', 's1-seo-meta' ); ?></h3> + + <div class="form-field"> + <label for="s1_seo_title"><?php esc_html_e( 'SEO Title', 's1-seo-meta' ); ?></label> + <input + type="text" + id="s1_seo_title" + name="s1_seo_title" + value="" + maxlength="666" + /> + <p class="s1-seo-counter"> + <span id="s1_seo_title_count">0</span>/60 <?php esc_html_e( 'characters', 's1-seo-meta' ); ?> + </p> + </div> + + <div class="form-field"> + <label for="s1_seo_description"><?php esc_html_e( 'SEO Description', 's1-seo-meta' ); ?></label> + <?php + $settings = array( + 'media_buttons' => false, + 'textarea_rows' => 5, + 'teeny' => true, + 'tinymce' => array( + 'toolbar1' => 'bold,italic,underline,link,unlink', + 'toolbar2' => '', + ), + ); + wp_editor( '', 's1_seo_description', $settings ); + ?> + <p class="s1-seo-counter"> + <span id="s1_seo_desc_count">0</span>/160 <?php esc_html_e( 'characters (recommended: 120–160)', 's1-seo-meta' ); ?> + </p> + </div> + </div> + <?php + } + + /** + * Render fields on the "Edit term" screen. + * + * @param WP_Term $term Current term object. + * @param string $taxonomy Current taxonomy slug. + */ + public function edit_form_fields( $term, $taxonomy ) { + wp_nonce_field( 's1_seo_term_save', 's1_seo_term_nonce' ); + + $seo_title = get_term_meta( $term->term_id, self::META_TITLE, true ); + $seo_desc = get_term_meta( $term->term_id, self::META_DESC, true ); + ?> + <tr class="form-field s1-seo-term-row"> + <td colspan="2"> + <h3 class="s1-seo-term-heading"><?php esc_html_e( 'S1 SEO', 's1-seo-meta' ); ?></h3> + </td> + </tr> + + <tr class="form-field"> + <th scope="row"> + <label for="s1_seo_title"><?php esc_html_e( 'SEO Title', 's1-seo-meta' ); ?></label> + </th> + <td> + <input + type="text" + id="s1_seo_title" + name="s1_seo_title" + value="<?php echo esc_attr( $seo_title ); ?>" + placeholder="<?php echo esc_attr( $term->name ); ?>" + maxlength="60" + /> + <p class="description s1-seo-counter"> + <span id="s1_seo_title_count"><?php echo esc_html( mb_strlen( $seo_title ) ); ?></span>/60 + <?php esc_html_e( 'characters (recommended: 50–60)', 's1-seo-meta' ); ?> + </p> + </td> + </tr> + + <tr class="form-field"> + <th scope="row"> + <label for="s1_seo_description"><?php esc_html_e( 'SEO Description', 's1-seo-meta' ); ?></label> + </th> + <td> + <?php + $settings = array( + 'media_buttons' => false, + 'textarea_rows' => 5, + 'teeny' => true, + 'tinymce' => array( + 'toolbar1' => 'bold,italic,underline,link,unlink', + 'toolbar2' => '', + ), + ); + wp_editor( $seo_desc, 's1_seo_description', $settings ); + ?> + <p class="description s1-seo-counter"> + <span id="s1_seo_desc_count"><?php echo esc_html( mb_strlen( wp_strip_all_tags( $seo_desc ) ) ); ?></span>/160 + <?php esc_html_e( 'characters (recommended: 120–160)', 's1-seo-meta' ); ?> + </p> + </td> + </tr> + <?php + } + + /** + * Save term SEO meta. + * + * @param int $term_id Term ID. + */ + public function save_term_meta( $term_id ) { + if ( + ! isset( $_POST['s1_seo_term_nonce'] ) || + ! wp_verify_nonce( sanitize_key( $_POST['s1_seo_term_nonce'] ), 's1_seo_term_save' ) + ) { + return; + } + + if ( ! current_user_can( 'manage_categories' ) ) { + return; + } + + if ( isset( $_POST['s1_seo_title'] ) ) { + update_term_meta( + $term_id, + self::META_TITLE, + sanitize_text_field( wp_unslash( $_POST['s1_seo_title'] ) ) + ); + } + + if ( isset( $_POST['s1_seo_description'] ) ) { + $description = wp_unslash( $_POST['s1_seo_description'] ); + + // Разрешаем безопасные HTML теги для SEO описания + $allowed_tags = array( + 'a' => array( + 'href' => array(), + 'title' => array(), + 'target' => array(), + ), + 'b' => array(), + 'strong' => array(), + 'i' => array(), + 'em' => array(), + 'br' => array(), + 'p' => array(), + 'span' => array(), + 'ul' => array(), + 'ol' => array(), + 'li' => array(), + ); + + // Для администраторов разрешаем все теги, для остальных - только безопасные + if ( current_user_can( 'unfiltered_html' ) ) { + $sanitized = $description; + } else { + $sanitized = wp_kses( $description, $allowed_tags ); + } + + update_term_meta( + $term_id, + self::META_DESC, + $sanitized + ); + } + } + + /** + * Static helper: retrieve SEO title for a term. + * + * @param int $term_id Term ID. + * @return string + */ + public static function get_title( $term_id ) { + return (string) get_term_meta( $term_id, self::META_TITLE, true ); + } + + /** + * Static helper: retrieve SEO description for a term. + * + * @param int $term_id Term ID. + * @return string + */ + public static function get_description( $term_id ) { + return (string) get_term_meta( $term_id, self::META_DESC, true ); + } +} diff --git a/s1-seo-meta74/languages/s1-seo-meta.pot b/s1-seo-meta74/languages/s1-seo-meta.pot new file mode 100644 index 0000000..edc23d6 --- /dev/null +++ b/s1-seo-meta74/languages/s1-seo-meta.pot @@ -0,0 +1,65 @@ +# Copyright (C) 2024 S1 +# This file is distributed under the same license as the S1 SEO Meta plugin. +msgid "" +msgstr "" +"Project-Id-Version: S1 SEO Meta 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-01-01 00:00:00+00:00\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"X-Generator: WP-CLI\n" +"X-Domain: s1-seo-meta\n" + +#. Plugin Name of the plugin +msgid "S1 SEO Meta" +msgstr "" + +#. Plugin URI of the plugin +msgid "https://github.com/S1/s1-seo-meta" +msgstr "" + +#. Description of the plugin +msgid "Добавляет мета-теги Title и Description для страниц, публикаций, рубрик, категорий и других типов записей." +msgstr "" + +#. Author of the plugin +msgid "S1" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +msgid "S1 SEO: Title & Description" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +#: includes/class-s1-seo-meta-term.php +msgid "SEO Title" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +#: includes/class-s1-seo-meta-term.php +msgid "SEO Description" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +msgid "characters (recommended: 50–60)" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +msgid "characters (recommended: 120–160)" +msgstr "" + +#: includes/class-s1-seo-meta-post.php +msgid "Write a short description of this page…" +msgstr "" + +#: includes/class-s1-seo-meta-term.php +msgid "S1 SEO" +msgstr "" + +#: includes/class-s1-seo-meta-term.php +msgid "characters" +msgstr "" diff --git a/s1-seo-meta74/readme.txt b/s1-seo-meta74/readme.txt new file mode 100644 index 0000000..dbabd91 --- /dev/null +++ b/s1-seo-meta74/readme.txt @@ -0,0 +1,48 @@ +=== S1 SEO Meta === +Contributors: s1 +Tags: seo, meta, title, description, meta tags +Requires at least: 5.8 +Tested up to: 6.5 +Requires PHP: 7.4 +Stable tag: 1.0.0 +License: GPLv2 or later +License URI: https://www.gnu.org/licenses/gpl-2.0.html + +Adds custom SEO Title and Description meta fields to posts, pages, custom post types, categories, tags, and all public taxonomies. + +== Description == + +S1 SEO Meta adds a lightweight, no-bloat SEO meta box to every post/page editor and taxonomy term screen in the WordPress admin. Simply fill in your desired **SEO Title** and **SEO Description** and the plugin will: + +* Output `<meta name="description">` in the `<head>`. +* Override the page `<title>` tag (supports both classic `wp_title` and modern `document_title_parts` hooks). +* Show a live character counter with colour-coded feedback (green / amber / red). + +Works with posts, pages, custom post types, categories, tags, and all registered public taxonomies. + +== Installation == + +1. Upload the `s1-seo-meta` folder to `/wp-content/plugins/`. +2. Activate the plugin from the **Plugins** menu in WordPress admin. +3. Open any post, page, or term — the **S1 SEO: Title & Description** box will appear. + +== Frequently Asked Questions == + += Will this conflict with Yoast SEO or Rank Math? = +It may conflict if both plugins try to output a `<meta name="description">`. It is recommended to use only one SEO plugin at a time. + += Does it support custom post types? = +Yes. The meta box is added automatically to every post type registered with `public => true`. + += Does it support custom taxonomies? = +Yes. Fields appear on every taxonomy registered with `public => true`. + +== Changelog == + += 1.0.0 = +* Initial release. + +== Upgrade Notice == + += 1.0.0 = +First release. diff --git a/s1-seo-meta74/s1-seo-meta.php b/s1-seo-meta74/s1-seo-meta.php new file mode 100644 index 0000000..ebaf56a --- /dev/null +++ b/s1-seo-meta74/s1-seo-meta.php @@ -0,0 +1,56 @@ +<?php +/** + * Plugin Name: S1 SEO Meta + * Plugin URI: https://1studi.ru + * Description: Добавляет мета-теги Title и Description для страниц, публикаций, рубрик, категорий и других типов записей. С визуальным редактором для описания категорий. + * Version: 1.1.0 + * Requires at least: 5.8 + * Requires PHP: 7.3 + * Author: S1 + * License: GPL v2 or later + * License URI: https://www.gnu.org/licenses/gpl-2.0.html + * Text Domain: s1-seo-meta + * Domain Path: /languages + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +define( 'S1_SEO_META_VERSION', '1.1.0' ); +define( 'S1_SEO_META_FILE', __FILE__ ); +define( 'S1_SEO_META_DIR', plugin_dir_path( __FILE__ ) ); +define( 'S1_SEO_META_URL', plugin_dir_url( __FILE__ ) ); + +/** + * Load plugin text domain for translations. + */ +function s1_seo_meta_load_textdomain() { + load_plugin_textdomain( 's1-seo-meta', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); +} +add_action( 'plugins_loaded', 's1_seo_meta_load_textdomain' ); + +/** + * Load required files. + */ +require_once S1_SEO_META_DIR . 'includes/class-s1-seo-meta-post.php'; +require_once S1_SEO_META_DIR . 'includes/class-s1-seo-meta-term.php'; +require_once S1_SEO_META_DIR . 'includes/class-s1-seo-meta-output.php'; + +if ( is_admin() ) { + require_once S1_SEO_META_DIR . 'admin/class-s1-seo-meta-admin.php'; +} + +/** + * Initialise all plugin components. + */ +function s1_seo_meta_init() { + new S1_SEO_Meta_Post(); + new S1_SEO_Meta_Term(); + new S1_SEO_Meta_Output(); + + if ( is_admin() ) { + new S1_SEO_Meta_Admin(); + } +} +add_action( 'init', 's1_seo_meta_init' ); diff --git a/s1-seo-meta74/s1-seo-meta73.zip b/s1-seo-meta74/s1-seo-meta73.zip new file mode 100644 index 0000000000000000000000000000000000000000..e6560c21e1f02936e12b82d7bc42929fed39fedd GIT binary patch literal 10168 zcmb7K1yo$wvTfX5f&^%SyF+jZ?k+)sy9Rf+;M%ymySuvtmn2Bg;2J`J5A$ATl9%^q z{=ck6Us%<9clF(;>YS>Qmx6%A1Uy}Nu2EWl-2D3&^y90Zp_Q36gZ#g3U;&^<8^?_f zs4*}AARht%ApX<Fz*5iNp3a_$&fds|&dSI^kM586(%YKarYmaO%rc^SM0)qXNRPjW zN4ZS!TPu<Uhs0zHR2!9HckUo}sv>Zsn0=U7mVlh@mckNwJJ4EN%i?-j@aeity%M)f zPsv%)=yg3s)55ZbiSPru{2h+10K$S1oY34!b$}?ot$P4+de*W*7eUIzCkJ*DRM<~f zIc})j0cWwG75w|;5jX~%I3&^&aC6JN9xh*(^NQ~+&F)0KP`sV$0b+X<ix>tfG-Y?G zJ}}A3y*XY@Hbzk6S=1Eu;1e|E;#cy`q*cN>70s|9)>mIe`3`%nj0}%0)BG4k)6mtM z+v}+M?7_xGO#E|buw&wfZZW5IR;t0liPfFWr=bzZWr1|in%}A7&v9~Unve51n)HQX z@{~#l%C27X+j6r;@bp~jB2sgpU4P{ACU6To_XJ7Cku8{Znd6~U?yzm@Xo8du?6@*m z0;A43=HDC19vmQkmG50j74T59{cMn>IU%h_2m;Udu}@MkoI(<fiRd2Ot$x7pcV;+c zt8O2o-5+~U*02T2^75H<wy&JnG-d)bV`-@^!fG?prmAf)UF-%<J>50Enr?&`%j+;% zwy$~Eg1tjIY3l?$vN*Rfd%ablR^2&9NLn)%ZGK1r07(B2xxoJ=1$+CSB=clPm4JKW z0uBJU!vFw8zu5gQ7z2Cza+PW88AeP`oz9J#P>|VPY=cy*vb#VC3@IwgCeo=b#wS)) z4DK53_X@<PXdc9G{0_qn&2(X{DrQL+uC+4lE7G+xddF)c-~g+lwzu05Z4~||=eH;> z{nYep^Q#GYhu>K$pm#uVp<6_Y(-oi*URL-oDVUHC{94_`k>E1Swnl4=oW(Ir^z3l7 z`@8{Ph8rb7vG&x-qs8)8O|Xh);brI{8*cov(vq{}L-6+$;gzdluEA6ZZoqX3{nrKg zrEb$uNX68Yu<1O5A$Hmz7m&c~5zE$$w9kr3S;QSw(%^PD_sC}V31uVADxB}pZgt6} ziqT(&En4zvI_ZR~5vXdp*ivJW%(a?%F4TNRoWL>oQbD5KvrmjdV)3cOH{C|0on_LR z!dZnsxi!UF7U^(&Zzn7l@tKIot^<2oa!(0dFDT=X4@dN1-jV%l`8?;;3e$;RLiyPj z5t{^|0q5l}M|Y<Ki~D@+N1DS>Q2n0vxqEX(78N|kD-p5W-IlDA_V~>3f!je+)6*A> zu!YkJ&_Xi4R{pEW2q&@dhBdg8E%)DNCP_g{vJc~PPV=rS7^mM&YK`JW6D{R{zEgK? zp?7Ut>z3jI;(`#OoA*@ahqpo_Cx}uD!t`7)p!MktckQg6v1q?Byxj`975i(I$S7&n zmo}#_I&^}>3fD`F*vlY;Pj3iKnqj;#hUlJubL>IgVbSu(YE=;KLHC4Q$wEh&rtN?E z+Kp9}s(5hvJr|$A;JHi3nW@N_f%^h~YzYu_HOU&Fc{f|><E=@x<fCVc)9EJU(p~zR z+CS&Rw8?eTJH(cA1SNV_6V|V>!nF+~oq)f@{hzTA?oZL0|E%pNvl<h?wdbR@6(8dz z!Ov#DOWWK&U3n#Ph7r|MwnLw^M5bk=fDWDKpa!7`r~nrr584@tC@eSI7jBqQET{fM z**%N04A_F=7unT=_7lmRaW1x=1ift1VvL>-xO~}qCtn8TTt+#Bdg|nJ*q6yYFf{}8 z`!Q({sVPfDmv>~7u&hQ(6siP1+pCt@j}Y}y(c)>~C`gEB!+5&9%gvjHU8Yn>`b-~5 zLPLmlvyj5#-z8h8z5$(odSNUw5~0X0pQEK1B7t01F?y%aN|`)gWS?^$Z(~mF2qE{W zgSF@iW|yFNJ!V&DFJ(A5E?h^2n;0ZJytM@uY{_n~3`2u_&vCVthGd5DWnj--kH@+v zCShupjd+{1FM0WuIUX4sV=AJhj#UgiTFWOH(RZtHo>iramI0iKl%$%7%HHo0H+ESg z2by`Wyz(eam{(qHzZ*|`m#N{vGl}HIQOuI<?Tl7v6i>4ny|ySz94H)Ry}B+_A2j1O z7|(M|sYy2FtUJ&wwybRkMR>Hr_Xa})wz*yUY(h~n3!SukDjAEp9OK=pQ0yYl$~bWc zqqw-<Yj=lz863nlg(P%6;;PS>i_B2G2CpzQIGoz9r*h{r_ok%{*zCpNII%uAlxa7! zsHMC@6{ZEYf1X@<_OMTFXuj#CTrmcR#h^~b3SO^ROD3ejl(DjDdq%8;J!GYke5HAY zsDsGj!f7cLMt@E%;R)L+U2_g|EOXnayZ=#s_dz9+68YXW<PP&=wi5__IG}dTVTu>t zD@fg)0j!$`Tww5JoA65{q60$FMUUv@8*R`+?#`=k+^vV`4}+F42Nx}x^bRYyvOpcI zh;|aVqUcjkJP(2j@t(Z3V~y4aq`%}H_aCwQN#17G29}P7MnB8@$t+>y<~r~(({myK z0AxR#{hsh`935;O9iB4&oQh`UdjV9hf=+!>Qo8Nu@<dxc&bcU}x#WCBOSFf8(nISH zA993mjwf`ew*chAo+oL5L#>V*r|s0MeVz7_xYO}exYYnqL^;02+6cVn24WNKT8Yr8 z;2C`39k@(|h*e<h9j<x@E0A;%t7i`0l?k70&F*M{=V1&4Yhzu@A*WOG3{vpQE;a<p z&=vw-3pP{)de1-DMu*qsmNt<1&RfpytIVQAjZAFY=K%*WbzLz*iAwA3{2{&Rq170$ zfwyE4b28Q}C^Ps`!~@*-zBC*@S|EI91sa3CHeqL<N;dlovG{>7Kf5T2)g0b-Kn2?; zL&ho^J$j%DVKS%Tc68_+)N0T=X=uViQ9b2UV}ZP@Qg)q{8o@JlgrgBR1jGBX7tZTg zDSA?d#FAktA$2jq(n7En5Iiy5jT02-qiDA3<MnLO<5CIUK}^ae0(5d`+=(8sU#i!J zR$`8<LK|lcq5>{(sB+PnqbbOI<j#5yLeQ`!Z$sLqm(we@M^OWABwd$Df;sq<CoUC4 zZx9hnUZ$J9?Ao#nP3#BnPC)yBvp>{zI)yw*i`dW5O;4wM;99a*><EG0MAl@+ABYz1 zZ)b5yh`2Pv7j8nI^&ogf(0u5{pZN_f`fM5E>%`$`lvh*W01);|t0+<D`xG_ZGVHf- z{!=Mp)gSKZzljoS<yzH+H(<1Pl7B1<<aLLvR;Sq1x!$&h%y@1wV7H~DnX`-1vJro5 zHOA^#UN^3o=_+w15e!3cE(P!h+|Hk$E_7Efl<R);tc|Q5fAO6&f;(Ej@bbR;6z@yy zj^1exn39|aI37(sc`jO_r7GAP8o}GeS>Yt^8j@hNm+eu_M0(4`EOuZKtfnlcn?;`a z(U`8&DIQQG&!NeVp$Wbpz8ti@X}srIW}&s1-DV&;cDP<9(tiluhPqB54YHU8BKGMe z;3ws-GzsCP3_^zit9l9iDO9XDPJ^=K{gfdM%1ZQ+{MbyrdEx5SmD<yLB26JX8u8GA zaKWu5D~6vxw|nl+u%|Y%*y;>g6mn7KqISPeI@=)S&f-*1r*0?Si6WX1VBXd8Uctg| zQUjCbT?0Yaclj0HP>fk^>yj9TpubnAw^v4RxqjD9rX;{QvUJOxMl!TD=E<Vy<nu`f zLobC3EF;b0`tBmCaHZH`BAnaIn{|(V%A5Td`TV?~L&gjI$8v`SSh+`OmFHC&<3Rxc zu&4k4$=|PITO0d7R&l%9iuH`ZAIYi+RW?Qn57pDuENcp$8mwI02PQyO(;c0`(bB^C z1A=_0_Zrbx#*Tm`DRgex*8(BHl7pjz<dvo5;oLzH#kyAK<A!zmwl$5k9Ps{0VRq=( zzQS}+%Uf$}u;fLY;cwwYD(c{~*9??Z8L1N{D4d8cjC}WX@M(AB{KN`Lq5TO-V6WZ* zO*>bt!_XYou=`%3;|>tOUS%LhUI#INs~38|S*B}4bu09ovlgRLD}nEdlt2ls(;T?K zE@o=AeFqxgvM9mk>UVb#EqSew5%ay&f1MDY5cv&Z7d@o(Wl~R?Mz`(`R~e29sYtd+ z2A$w4KMqeRxN07qr9wvD3qOTq5n~jbT96U$UgTwM|7Mb%ySM@)0)~lG?3*ljp*6*` zQRLP6P<g$()gW^dG~ct}F+)H4R5^m~bWPY6LLnQLfLrXX5_Er#m)MfIkm*@c+{LwE z>t^s1`H~IV>M0$n5tkDm*&CDEMHKLJjn5+{W@o#=psH42-eKas@(-7z1P&<s5i3QD z!6_8M52+Zz$?BCF2P~OFl{5+#FEh5{lSgKMFb-!$aU<VZVPtLd;jVfkVFEjsYWS|T z5%o=|M)=!;_hBl$&R>9|TLFc>Z^-o1Ah%1$cjtuoVC|Xk8G<Txmb+%WZ>LBzyKW7x zUzRbus7ls|wn}eNWHP=lRjk*ok1CB?ckQm>35$+^EZ8pRs^B*3Ed-Vwa_*T`SWkY` zAlp=(uvpq9IttEWB(SoXd}Fq&)M-Hcebn!h-^nC%0DRvj-<>OF7)#Zqq4Rj*nbw(p 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 literal 0 HcmV?d00001 diff --git a/s1-show-category-description/s1-show-category-description.php b/s1-show-category-description/s1-show-category-description.php new file mode 100644 index 0000000..8353b83 --- /dev/null +++ b/s1-show-category-description/s1-show-category-description.php @@ -0,0 +1,102 @@ +<?php +/* +Plugin Name: S1 show category description +Plugin URI: https://1studi.ru +Description: Выводит описание категории/рубрики на странице архива (по умолчанию WP его скрывает) +Version: 1.1.0 +Author: S1 +Author URI: https://1studi.ru +License: GPL-2.0+ +Text Domain: s1-custom-scroll-to-top +Domain Path: /languages +*/ + +if (!defined('ABSPATH')) { + exit; +} + + +add_action('wp_head', function() { + // Можно добавить стили прямо в head (или подключить отдельный css) + // Раскомментируйте, если нужно + /* + echo '<style> + .category-description { + margin: 1.5em 0 2.5em; + padding: 1.2em 1.8em; + background: #f8f9fa; + border-left: 4px solid #0073aa; + border-radius: 4px; + line-height: 1.6; + } + .category-description p:last-child { + margin-bottom: 0; + } + </style>'; + */ +}, 20); + + +/** + * Основной вывод описания + */ +function scd_show_category_description() { + // Показываем только на страницах архивов категорий (можно расширить) + if (!is_category() && !is_tag() && !is_tax()) { + return; + } + + // Получаем описание + $description = get_the_archive_description(); + + if (empty($description)) { + return; + } + + // Можно обернуть в свой класс или изменить тег + $output = '<div class="category-description entry-content">'; + $output .= $description; + $output .= '</div>'; + + echo $output; +} + + +/** + * Вариант А — после заголовка архива (самый популярный) + */ +add_action('woocommerce_archive_description', 'scd_show_category_description', 15); // для woocommerce +add_action('generate_archive_title', 'scd_show_category_description', 20); // GeneratePress +add_action('astra_archive_header', 'scd_show_category_description', 15); // Astra + +// Универсальный способ — если тема не использует специальные хуки +add_action('wp_head', function() { + if (is_category() || is_tag() || is_tax()) { + add_filter('the_content', 'scd_inject_description_to_content', 9); + } +}); + +function scd_inject_description_to_content($content) { + // Показываем только в начале основного контента архива + if (!is_main_query() || !in_the_loop()) { + return $content; + } + + remove_filter('the_content', 'scd_inject_description_to_content', 9); + + $description = get_the_archive_description(); + if (empty($description)) { + return $content; + } + + return '<div class="category-description">' . $description . '</div>' . $content; +} + + +/** + * Вариант Б — короткий код [category_description] + * Можно вставить в любой блок / виджет / шаблон + */ +add_shortcode('category_description', function() { + return get_the_archive_description(); +}); \ No newline at end of file diff --git a/s1-show-category-description/s1-show-category-description.zip b/s1-show-category-description/s1-show-category-description.zip new file mode 100644 index 0000000000000000000000000000000000000000..093f1205bc93cc7c44986332e82b84561530df4c GIT binary patch literal 1492 zcmajfeKgYx7zgm5W>csbSwc<imUks@37u&fCKe{cVy#`9*yg2FvMo|dIfcT!PI*ZY z@_M?2*quhy;zo1J9B*Mvw;4uP_pkfUJ@@<k@jcIT&hzgFivcNX0|2lYD7)?u>}-jz z-3A7LbaeoLZ(L&zna5D*apojqEIEuGecAjRIffKXWyDhHGz$iWQQ|*TB|NB+(05ur zm7*3)DvQ`;s04M{F#~eJxruD=@eHE^l->GGb+&Zw->nIkf=aMc<}Fzv4CXaUB9BJn z_!y;#O47m{cNgZwh}Lc@8t9pJdKc`%kL=E~I{FL}R-w_bytUf1J<zcUY^zq;CQW|b z()mp^t!2Y*&|n6bW<BMmM3iZxc<(;cLl$uglx)00VRkbL##k^=St5H^@wxhJ{q8fO z2cTv5D&7(!JRohp3$wVdr6_9i)W;+_Q`DtfZ^Cq&xiTdK2rHCcQa`Q@N4oRFoM|nM zhrqrT9S6~lbnM7h+oclK7@8M`Ecba_7FhAngg5F|ceyA`Be8pxzWvJknrxWW9P1gg zwyDx@=xiR@4y-?JAZ-6gtf!pde#zf^wu8`UWWT<0qv807_S~*ag7Ex_UsIEL6SYM9 zll<o|@Iq;JexHGJbSYClD-GrXja!FPMq)di$Et`sOC#+(QyFh3Y4W?ahv!886xV_R zj83Ki{uzESL?aV-uP{okiJbC9ipx|3JEeNaH3P#dUPg(tpspkZ$9;fteZl!S`@4@B z9(F**e%Y@I-XsW3CK4n6SgJ)gjlDUvuHFxh^BE_Ua_C*+qWq3WRQChrF!~_6epo+x z^>~zktg<!RvE;FFs_)4sZ#m8ehbfxt1Mj^Cuc<~n>Pygt?HP^vO=mfRWT1EzHaJ#G zB}9T97iV`xP$X1IB<Jup$7(z_7VrBZ^d?KoMdDx^QSNt<Za29KDV;-c7vByO%;IW# z^ZbLb9xr`nM!*M_&R6N)E*`iT9B-lCB?t~x8!WQYi)G<SxkAWn>&qZwP)T*)K`gX- zb1t&=?z}JIT*z7o@2%TTr`HqmW~65NdQyL_9%AoZ#EdKFLc`I|#3D_|GH1az%}`=u zajZm^^Hixt-kzz7O?-8`+e$Wnsq#DZ5ahC_Q~A+=#)`dpT2<q>nj?pmt@rf|{w3(W z+TrqD*%NgQ-LF|NP4yFF_|SxOe6MesUYY47Akfnqm21-j2rlK5?-hq5sI_U9wDB^S zWsi}7DPD)SqOS_IB^lN-9QoFWDtEOA@4Y9)CPu3UJM~p-!A*Xj4dP=zHE+|oNW2uf zCek4nwCSeCZ$i)1j96)Us8}(2b{aEv?TT<uOeim)9d)>n?%lYD?9!JLU`5kXpmCN@ zjuQVlWd$|Py~w5IG^73Q^AP94b-#{6A1)plJ&^$pUwAefO_O@v1P_Xax%!h6LimMp ziP@}>yy6+(?#J)(H=n3!)1G$j(6o3BOuwL57B~Dk^AQo?6iUn<f|MW*W;lJw??~*~ zzpx$c;MuO8v~3A#!z$!>bMwyV$}2ojE$%xobnrkVsp7pVj$ZCWHTLL&62`7bf$`96 z9_q7PX&4<XZ;V4knLY*LBy1hPso;0k?UC5v_Qb1c{G-$JkEhb&lJRGEac+F^^@>;9 za%x^|G|-!T{+r%`B$lwhHT_ijFDGvd+^ODY&Q_#2tW*>b(XJ-bpD<&AozU{ZQB&lX zXi1-zyPJ<!z59@Z!eKQ;{RhDtCYTnB>LY7p++n0~?iu4#)^&*XAJ5KN+E}{AR*r^b z1qP|v5?;e5-z`9S-A1fJN#9@bd%lAbax(VR%UFKJ_K8;aRKnlE#IROz!ccC&Y3SNM znM#M)Rxmjc>5Qq-t_D9aG*KvHF-n_`f&NQV#D=Fo183J=``Q0ztLi@j1Ar2+6L{nD d{tG`%r1bOe-@#(Qke|EK#zb$l=Z4(?@HbOIuIB&% literal 0 HcmV?d00001 diff --git a/s1-smart-sticky-anything/s1-smart-sticky-anything.php b/s1-smart-sticky-anything/s1-smart-sticky-anything.php new file mode 100644 index 0000000..20b4a06 --- /dev/null +++ b/s1-smart-sticky-anything/s1-smart-sticky-anything.php @@ -0,0 +1,137 @@ +<?php +/* +Plugin Name: Smart Sticky Header with Scroll Interval +Description: Sticky header с интервалом скролла и клонированием (оригинал остаётся на месте) +Version: 1.2.0 +*/ + +add_action('admin_menu', function() { + add_options_page( + 'Sticky Header Settings', + 'Sticky Header', + 'manage_options', + 'smart-sticky-header', + 'ssh_settings_page' + ); +}); + +function ssh_settings_page() { + if (isset($_POST['save_settings'])) { + update_option('ssh_sticky_selector', sanitize_text_field($_POST['sticky_selector'])); + update_option('ssh_start_scroll', intval($_POST['start_scroll'])); + update_option('ssh_end_scroll', intval($_POST['end_scroll'])); + echo '<div class="updated"><p>Настройки сохранены!</p></div>'; + } + + $selector = get_option('ssh_sticky_selector', '#masthead, .site-header, header'); + $start_scroll = get_option('ssh_start_scroll', 100); + $end_scroll = get_option('ssh_end_scroll', 0); + ?> + <div class="wrap"> + <h1>Настройки Sticky Header</h1> + <form method="post"> + <table class="form-table"> + <tr> + <th><label>CSS-селектор хедера/меню</label></th> + <td> + <input type="text" name="sticky_selector" value="<?php echo esc_attr($selector) ?>" style="width:400px;" placeholder="#masthead, .site-header, header"/> + <p class="description">Можно несколько через запятую. Используй инспектор браузера для точного селектора.</p> + </td> + </tr> + <tr> + <th><label>Минимальный скролл (px)</label></th> + <td> + <input type="number" name="start_scroll" value="<?php echo esc_attr($start_scroll) ?>" min="0" style="width:100px;"/> + <p class="description">Sticky появляется после этого значения (default: 100).</p> + </td> + </tr> + <tr> + <th><label>Максимальный скролл (px)</label></th> + <td> + <input type="number" name="end_scroll" value="<?php echo esc_attr($end_scroll) ?>" min="0" style="width:100px;"/> + <p class="description">Sticky скрывается после этого значения. 0 — без лимита (default: 0).</p> + </td> + </tr> + </table> + <?php submit_button('Сохранить настройки', 'primary', 'save_settings'); ?> + </form> + </div> + <?php +} + +add_action('wp_enqueue_scripts', function() { + if (is_admin()) return; + + $selector = get_option('ssh_sticky_selector', '#masthead, .site-header, header'); + $start_scroll = get_option('ssh_start_scroll', 100); + $end_scroll = get_option('ssh_end_scroll', 0); + + // Выводим CSS и JS inline для простоты (можно вынести в файлы) + ?> + <style id="ssh-sticky-css"> + .ssh-sticky-clone { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + width: 100% !important; + z-index: 9998 !important; + background: #ffffff; + box-shadow: 0 3px 8px rgba(0,0,0,0.12); + opacity: 0; + transition: opacity 0.3s ease, transform 0.3s ease; + transform: translateY(-100%); + display: none; /* Initially hidden */ + } + .ssh-sticky-clone.ssh-visible { + opacity: 1; + transform: translateY(0); + display: block !important; + } + body.admin-bar .ssh-sticky-clone { + top: 32px !important; + } + @media (max-width: 782px) { + body.admin-bar .ssh-sticky-clone { + top: 46px !important; + } + } + </style> + + <script id="ssh-sticky-js"> + document.addEventListener('DOMContentLoaded', function() { + const selectors = '<?php echo esc_js($selector); ?>'.split(',').map(s => s.trim()); + const startScroll = <?php echo intval($start_scroll); ?>; + const endScroll = <?php echo intval($end_scroll); ?>; + + let originalHeader = null; + for (let sel of selectors) { + originalHeader = document.querySelector(sel); + if (originalHeader) break; + } + + if (!originalHeader) return; // Не нашли хедер + + // Клонируем + const clone = originalHeader.cloneNode(true); + clone.classList.add('ssh-sticky-clone'); + document.body.appendChild(clone); + + // Функция проверки скролла + function checkScroll() { + const scrollY = window.scrollY; + if (scrollY >= startScroll && (endScroll === 0 || scrollY <= endScroll)) { + clone.classList.add('ssh-visible'); + } else { + clone.classList.remove('ssh-visible'); + } + } + + // Слушатели + window.addEventListener('scroll', checkScroll); + window.addEventListener('resize', checkScroll); // На всякий случай + checkScroll(); // Инициализация + }); + </script> + <?php +}, 999); // Высокий приоритет, чтобы после других скриптов \ No newline at end of file diff --git a/s1-smart-sticky-anything/s1-smart-sticky-header.php.zip b/s1-smart-sticky-anything/s1-smart-sticky-header.php.zip new file mode 100644 index 0000000000000000000000000000000000000000..da16b11056a75364d01db899f430c86094fc5312 GIT binary patch literal 2096 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 P00A+-LjAA5008(GpPIe$ literal 0 HcmV?d00001