Changeset 3749166
- Timestamp:
- 10/15/2026 07:04:36 PM (3 weeks ago)
- Location:
- rootspersona/trunk
- Files:
-
- 7 added
- 27 edited
-
admin/class-rootspersona-svg-admin.php (modified) (1 diff)
-
css/persona-templates.css (added)
-
js/rootsUtilities.js (modified) (4 diffs)
-
js/rootspersona-settings.js (added)
-
js/rp-persons-add-multiselect.js (modified) (1 diff)
-
localization/rootspersona-ru_RU.mo (modified) ( previous)
-
localization/rootspersona-ru_RU.po (modified) (2 diffs)
-
php/Genealogy/Gedcom/Structures/class-RP-Fact.php (modified) (1 diff)
-
php/class-RP-Date.php (added)
-
php/class-RP-Gedcom-Loader.php (modified) (5 diffs)
-
php/class-RP-Log.php (modified) (1 diff)
-
php/class-RP-Persona-Factory.php (modified) (1 diff)
-
php/class-RP-Persona-Helper.php (modified) (5 diffs)
-
php/class-RP-Template-Colors.php (added)
-
php/dao/mysql/class-RP-Persona-Mysql-DAO.php (modified) (15 diffs)
-
php/model/class-RP-Persona.php (modified) (1 diff)
-
php/pages/class-RP-Add-Page-Builder.php (modified) (4 diffs)
-
php/pages/class-RP-Evidence-Page-Builder.php (modified) (1 diff)
-
php/pages/class-RP-Index-Page-Builder.php (modified) (4 diffs)
-
php/pages/class-RP-Option-Page-Builder.php (modified) (1 diff)
-
php/pages/class-RP-Persona-Page-Builder.php (modified) (3 diffs)
-
php/pages/panels/class-RP-Ancestors-Panel-Creator.php (modified) (2 diffs)
-
php/pages/panels/class-RP-Descendancy-Panel-Creator.php (modified) (4 diffs)
-
php/pages/panels/class-RP-Evidence-Panel-Creator.php (modified) (2 diffs)
-
php/pages/panels/class-RP-Facts-Panel-Creator.php (modified) (2 diffs)
-
php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php (modified) (5 diffs)
-
php/pages/panels/class-RP-Header-Panel-Creator.php (modified) (9 diffs)
-
php/pages/panels/class-RP-Picture-Panel-Creator.php (modified) (1 diff)
-
php/templates (added)
-
php/templates/class-RP-Persona-Templates.php (added)
-
php/templates/class-RP-Persona-View.php (added)
-
readme.txt (modified) (3 diffs)
-
rootspersona.php (modified) (30 diffs)
-
uninstall.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
rootspersona/trunk/admin/class-rootspersona-svg-admin.php
r3746101 r3749166 62 62 'nonce' => wp_create_nonce( 'rootspersona_clear_svg_cache' ), 63 63 'loading' => __( 'Clearing...', 'rootspersona' ), 64 'success' => __( ' ?Cache cleared.', 'rootspersona' ),65 'error' => __( ' ?Error clearing cache.', 'rootspersona' ),66 'network' => __( ' ?Network error.', 'rootspersona' ),64 'success' => __( '✓ Cache cleared.', 'rootspersona' ), 65 'error' => __( '✗ Error clearing cache.', 'rootspersona' ), 66 'network' => __( '✗ Network error.', 'rootspersona' ), 67 67 ) 68 68 ) . '; -
rootspersona/trunk/js/rootsUtilities.js
r3716808 r3749166 458 458 jQuery.get(refreshUrl, data, function(response) { 459 459 document.body.style.cursor = "default"; 460 var res = jQuery.parseJSON(response); 461 jQuery.each(res, function(index, p) { 460 // wp_send_json_success() answers {success, data}; jQuery may have parsed it already. 461 var res = (typeof response === 'string') ? jQuery.parseJSON(response) : response; 462 if (res && typeof res.success !== 'undefined') { 463 res = res.data; 464 } 465 jQuery.each(res || [], function(index, p) { 462 466 if(index == 'error') { 463 467 alert(p); 464 468 } else { 465 var label = (p.surname || '') + ", " + (p.given || '');469 var label = p.label || ((p.surname || '') + ", " + (p.given || '')); 466 470 jQuery("#persons").append( 467 471 jQuery("<option/>", { 468 472 id: String(p.id || ''), 473 value: p.value || (String(p.id || '') + '|' + (p.surname || '') + ', ' + (p.given || '')), 469 474 text: label 470 475 }) … … 661 666 ]; 662 667 668 // jQuery UI autocomplete is loaded only on admin screens (edit form). 669 var rpHasAutocomplete = typeof jQuery.fn.autocomplete === 'function'; 670 663 671 jQuery('.claimType').each(function(index) { 672 if (!rpHasAutocomplete) { return; } 664 673 jQuery(this).autocomplete({ 665 674 source: claimTypes … … 717 726 jQuery(this).append(newRow); 718 727 }); 719 jQuery('#newclaim').autocomplete({ 720 source: claimTypes 721 }); 728 if (rpHasAutocomplete) { 729 jQuery('#newclaim').autocomplete({ 730 source: claimTypes 731 }); 732 } 722 733 jQuery('#newclaim').focus(); 723 734 jQuery('#newclassification').blur(addFactsRow); … … 959 970 } 960 971 961 jQuery('#parental_text').autocomplete({ 962 source: rp_autoCompleteParental, 963 select: rp_autoSelectPerson, 964 minLength: 1 965 }); 966 967 jQuery('#spousal_text').autocomplete({ 968 source: rp_autoCompleteSpousal, 969 select: rp_autoSelectPerson, 970 minLength: 1 971 }); 972 if (rpHasAutocomplete) { 973 jQuery('#parental_text').autocomplete({ 974 source: rp_autoCompleteParental, 975 select: rp_autoSelectPerson, 976 minLength: 1 977 }); 978 979 jQuery('#spousal_text').autocomplete({ 980 source: rp_autoCompleteSpousal, 981 select: rp_autoSelectPerson, 982 minLength: 1 983 }); 984 } 972 985 973 986 document.body.style.cursor = "default"; -
rootspersona/trunk/js/rp-persons-add-multiselect.js
r3577169 r3749166 80 80 }); 81 81 })(); 82 83 /** 84 * Add Uploaded Persons: send the selection to the server in chunks. 85 * A plain form post carries one field per person; above ~1000 people PHP's 86 * max_input_vars silently truncates it and no page is created at all. 87 */ 88 (function () { 89 'use strict'; 90 var CHUNK = 100; 91 92 document.addEventListener('DOMContentLoaded', function () { 93 var form = document.getElementById('rp-add-pages-form'); 94 var sel = document.getElementById('persons'); 95 var cfg = window.rootspersonaAddPages; 96 if (!form || !sel || !cfg || !window.fetch) { 97 return; 98 } 99 var msg = document.getElementById('rp-add-pages-msg'); 100 var submit = form.querySelector('input[name="submitAddPageForm"]'); 101 102 function say(text) { 103 if (msg) { 104 msg.textContent = text; 105 } 106 } 107 108 function format(tpl, a, b) { 109 return tpl.replace('%1$s', a).replace('%2$s', b); 110 } 111 112 function postChunk(values) { 113 var body = new FormData(); 114 body.append('action', 'rootspersona_add_pages'); 115 body.append('_wpnonce', form.querySelector('input[name="_wpnonce"]').value); 116 body.append('batch_id', form.querySelector('input[name="batch_id"]').value); 117 values.forEach(function (v) { 118 body.append('persons[]', v); 119 }); 120 return fetch(cfg.ajaxurl, { method: 'POST', body: body, credentials: 'same-origin' }) 121 .then(function (r) { 122 return r.json(); 123 }) 124 .then(function (json) { 125 if (!json || !json.success) { 126 throw new Error(json && json.data && json.data.message ? json.data.message : cfg.i18n.failed); 127 } 128 return json.data; 129 }); 130 } 131 132 form.addEventListener('submit', function (e) { 133 var selected = Array.prototype.filter.call(sel.options, function (o) { 134 return o.selected; 135 }); 136 if (!selected.length) { 137 return; // let the server answer "No people selected." 138 } 139 e.preventDefault(); 140 if (submit) { 141 submit.disabled = true; 142 } 143 document.body.style.cursor = 'wait'; 144 145 var total = selected.length; 146 var done = 0; 147 var created = 0; 148 var errors = 0; 149 var lines = []; 150 151 function next() { 152 if (done >= total) { 153 document.body.style.cursor = 'default'; 154 if (submit) { 155 submit.disabled = false; 156 } 157 say(format(cfg.i18n.finished, String(created), String(errors))); 158 if (msg && lines.length) { 159 lines.forEach(function (line) { 160 msg.appendChild(document.createElement('br')); 161 msg.appendChild(document.createTextNode(line)); 162 }); 163 } 164 return; 165 } 166 var slice = selected.slice(done, done + CHUNK); 167 postChunk(slice.map(function (o) { 168 return o.value; 169 })) 170 .then(function (data) { 171 created += data.created; 172 errors += data.errors; 173 data.lines.forEach(function (line) { 174 // Lines arrive as escaped HTML; show them as plain text. 175 var tmp = document.createElement('div'); 176 tmp.innerHTML = line; 177 lines.push(tmp.textContent); 178 }); 179 slice.forEach(function (o) { 180 o.parentNode.removeChild(o); 181 }); 182 done += slice.length; 183 say(format(cfg.i18n.progress, String(done), String(total))); 184 next(); 185 }) 186 .catch(function (err) { 187 document.body.style.cursor = 'default'; 188 if (submit) { 189 submit.disabled = false; 190 } 191 say(format(cfg.i18n.stopped, String(created), String(total)) + ' ' + err.message); 192 }); 193 } 194 195 say(format(cfg.i18n.progress, '0', String(total))); 196 next(); 197 }); 198 }); 199 })(); -
rootspersona/trunk/localization/rootspersona-ru_RU.po
r3572543 r3749166 1 # Translation of Plugins - Rootspersona - Stable (latest release) in Russian2 # This file is distributed under the same license as the Plugins - Rootspersona - Stable (latest release) package.3 1 msgid "" 4 2 msgstr "" 5 "PO-Revision-Date: 2026-01-24 18:38+0300\n" 3 "Project-Id-Version: Plugins - Rootspersona - Stable (latest release)\n" 4 "Report-Msgid-Bugs-To: \n" 5 "Last-Translator: \n" 6 "Language-Team: \n" 6 7 "MIME-Version: 1.0\n" 7 8 "Content-Type: text/plain; charset=UTF-8\n" 8 9 "Content-Transfer-Encoding: 8bit\n" 10 "POT-Creation-Date: \n" 11 "PO-Revision-Date: 2026-10-15 10:00+0300\n" 12 "Language: ru\n" 9 13 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 10 14 "X-Generator: Poedit 2.0.7\n" 11 "Language: ru\n" 12 "Project-Id-Version: Plugins - Rootspersona - Stable (latest release)\n" 13 "POT-Creation-Date: \n" 14 "Last-Translator: \n" 15 "Language-Team: \n" 16 17 #. Plugin URI of the plugin/theme 18 msgid "https://rootspersona.com" 19 msgstr "https://rootspersona.com" 20 21 #. Plugin Name of the plugin/theme 15 16 #. Plugin Name of the plugin 17 #: rootspersona.php 22 18 msgid "RootsPersona" 23 19 msgstr "RootsPersona" 24 20 25 #. Author URI of the plugin/theme 26 msgid "http://rootspersona.com/" 27 msgstr "http://rootspersona.com/" 28 29 #. Author of the plugin/theme 30 msgid "Ed Thompson" 31 msgstr "Эд Томпсон" 32 33 #: rootspersona.php:485 21 #: rootspersona.php:1031 34 22 msgid "You do not have permission to upload files." 35 23 msgstr "У вас нет прав на загрузку файлов." 36 24 37 #: php/pages/panels/class-RP-Bio-Panel-Creator.php:15 38 #: php/pages/panels/class-RP-Bio-Panel-Creator.php:28 25 #: php/pages/panels/class-RP-Bio-Panel-Creator.php:47 26 #: php/pages/panels/class-RP-Bio-Panel-Creator.php:65 27 #: php/templates/class-RP-Persona-Templates.php:330 28 #: php/templates/class-RP-Persona-Templates.php:574 39 29 msgid "Biography" 40 30 msgstr "Биография" 41 31 42 #: php/class-RP-Persona-Installer.php:2 1932 #: php/class-RP-Persona-Installer.php:285 43 33 msgid "(Picture a front page with 100 or more entries in the menu bar)" 44 34 msgstr "(Представьте себе первую страницу со 100 или более записями в строке меню)" 45 35 46 #: php/pages/class-RP-Add-Page-Builder.php:6 336 #: php/pages/class-RP-Add-Page-Builder.php:68 47 37 msgid "Add" 48 38 msgstr "Добавить" 49 39 50 #: php/pages/class-RP-Tools-Page-Builder.php:1 0440 #: php/pages/class-RP-Tools-Page-Builder.php:125 51 41 msgid "Add Evidence Pages" 52 42 msgstr "Добавить Страницы Данных" 53 43 54 #: php/pages/class-RP-Tools-Page-Builder.php: 8944 #: php/pages/class-RP-Tools-Page-Builder.php:107 55 45 msgid "Add Individual" 56 46 msgstr "Добавить человека" … … 60 50 msgstr "Добавить Загруженные Персоны" 61 51 62 #: php/pages/class-RP-Tools-Page-Builder.php:1 0752 #: php/pages/class-RP-Tools-Page-Builder.php:128 63 53 msgid "Add missing evidence pages." 64 54 msgstr "Добавить недостающие Страницы Данных" 65 55 66 #: php/pages/class-RP-Include-Page-Builder.php:19 56 #. translators: %s: plural label (e.g. personas). 57 #: php/pages/class-RP-Include-Page-Builder.php:23 58 #, php-format 67 59 msgid "All %s have been included" 68 60 msgstr "Все %s включены" 69 61 70 #: php/pages/class-RP-Add-Page-Builder.php:23 71 msgid "All available persons have been added." 72 msgstr "Все персоны добавлены" 73 74 #: php/pages/class-RP-Option-Page-Builder.php:457 62 #: php/pages/class-RP-Option-Page-Builder.php:485 75 63 msgid "Allow Editor" 76 64 msgstr "Разрешать редактирование" 77 65 78 #: php/pages/class-RP-Option-Page-Builder.php:4 7066 #: php/pages/class-RP-Option-Page-Builder.php:498 79 67 msgid "Allow Editor to edit persona pages" 80 68 msgstr "Разрешать редактирование страниц персон" 81 69 82 #: php/pages/panels/class-RP-Ancestors-Panel-Creator.php:2 370 #: php/pages/panels/class-RP-Ancestors-Panel-Creator.php:27 83 71 msgid "Ancestors" 84 72 msgstr "Предки" 85 73 86 #: php/pages/class-RP-Option-Page-Builder.php:3 2474 #: php/pages/class-RP-Option-Page-Builder.php:369 87 75 msgid "Banner Background Color" 88 76 msgstr "Цвет фона баннера" 89 77 90 #: php/pages/class-RP-Option-Page-Builder.php:3 1878 #: php/pages/class-RP-Option-Page-Builder.php:363 91 79 msgid "Banner Foreground Color" 92 80 msgstr "Цвет переднего плана баннера" 93 81 94 #: php/pages/class-RP-Option-Page-Builder.php:3 3082 #: php/pages/class-RP-Option-Page-Builder.php:375 95 83 msgid "Banner Image" 96 84 msgstr "Изображение Баннера" 97 85 98 #: php/pages/class-RP-Option-Page-Builder.php:1 2186 #: php/pages/class-RP-Option-Page-Builder.php:163 99 87 msgid "Bio" 100 88 msgstr "Био" 101 89 102 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php: 93103 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:2 3690 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:107 91 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:260 104 92 msgid "Birth" 105 93 msgstr "Дата рождения" 106 94 107 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:2 1195 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:235 108 96 msgid "CHILDREN" 109 97 msgstr "ДЕТИ" 110 98 111 #: php/pages/class-RP-Option-Page-Builder.php:1 2299 #: php/pages/class-RP-Option-Page-Builder.php:164 112 100 msgid "Choose a style for the header panel" 113 101 msgstr "Выберите стиль для панели заголовка" 114 102 115 #: php/pages/class-RP-Evidence-Page-Builder.php:2 0103 #: php/pages/class-RP-Evidence-Page-Builder.php:23 116 104 msgid "Citations" 117 105 msgstr "Цитирование" 118 106 119 #: php/pages/class-RP-Option-Page-Builder.php:379 120 msgid "Custom Style" 121 msgstr "Свой Стиль" 122 123 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:102 124 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:245 107 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:116 108 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:269 125 109 msgid "Death" 126 110 msgstr "Дата ухода из жизни" 127 111 128 #: php/pages/class-RP-Option-Page-Builder.php: 57112 #: php/pages/class-RP-Option-Page-Builder.php:67 129 113 msgid "Default Privacy Setting" 130 114 msgstr "Настройки приватности по умолчанию" 131 115 132 #: php/class-RP-Persona-Site-Mender.php:3 29116 #: php/class-RP-Persona-Site-Mender.php:351 133 117 msgid "Deleta Data From Database?" 134 118 msgstr "Удалить данные из базы данных?" 135 119 136 #: php/pages/class-RP-Persona-Page-Builder.php:1 00120 #: php/pages/class-RP-Persona-Page-Builder.php:154 137 121 msgid "Delete Person" 138 122 msgstr "Удалить персону" 139 123 140 #: php/pages/class-RP-Tools-Page-Builder.php:1 49124 #: php/pages/class-RP-Tools-Page-Builder.php:181 141 125 msgid "Delete persona Pages" 142 126 msgstr "Удалить страницы персоны" 143 127 144 #: php/class-RP-Persona-Site-Mender.php:70 145 #: php/class-RP-Persona-Site-Mender.php:78 128 #. translators: %s: page or form name. 146 129 #: php/class-RP-Persona-Site-Mender.php:86 147 130 #: php/class-RP-Persona-Site-Mender.php:94 148 131 #: php/class-RP-Persona-Site-Mender.php:102 132 #: php/class-RP-Persona-Site-Mender.php:110 133 #: php/class-RP-Persona-Site-Mender.php:118 134 #, php-format 149 135 msgid "Deleted obsolete %s page." 150 136 msgstr "Удалена устаревшая страница %s." 151 137 152 #: php/pages/class-RP-Option-Page-Builder.php:1 05138 #: php/pages/class-RP-Option-Page-Builder.php:138 153 139 msgid "Display Options" 154 140 msgstr "Настройки Отображения" 155 141 156 #: php/pages/class-RP-Persona-Page-Builder.php: 92142 #: php/pages/class-RP-Persona-Page-Builder.php:137 157 143 msgid "Edit Person" 158 144 msgstr "Редактировать данные персоны" 159 145 160 #: php/pages/class-RP-Option-Page-Builder.php:4 17146 #: php/pages/class-RP-Option-Page-Builder.php:461 161 147 msgid "Entries Per Page" 162 148 msgstr "Записей на Страницу" 163 149 164 #: rootspersona.php: 369150 #: rootspersona.php:787 165 151 msgid "Error creating page for" 166 152 msgstr "Ошибка создания страницы для" 167 153 168 #: php/pages/panels/class-RP-Facts-Panel-Creator.php:1 5154 #: php/pages/panels/class-RP-Facts-Panel-Creator.php:19 169 155 msgid "Facts" 170 156 msgstr "Факты" 171 157 172 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:115 158 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:129 159 #: php/templates/class-RP-Persona-Templates.php:343 160 #: php/templates/class-RP-Persona-Templates.php:436 161 #: php/templates/class-RP-Persona-Templates.php:535 173 162 msgid "Father" 174 163 msgstr "Отец" 175 164 176 #: rootspersona.php: 471165 #: rootspersona.php:1017 177 166 msgid "For internal use only" 178 167 msgstr "Только для внутреннего пользования" 179 168 180 #: php/pages/class-RP-Option-Page-Builder.php:3 37169 #: php/pages/class-RP-Option-Page-Builder.php:382 181 170 msgid "Group Foreground Color" 182 171 msgstr "Цвет переднего плана для группы" 183 172 184 #: php/pages/class-RP-Option-Page-Builder.php:3 49173 #: php/pages/class-RP-Option-Page-Builder.php:394 185 174 msgid "Group Image" 186 175 msgstr "Изображение для группы" 187 176 188 #: php/pages/class-RP-Option-Page-Builder.php:1 09177 #: php/pages/class-RP-Option-Page-Builder.php:153 189 178 msgid "Header Style" 190 179 msgstr "Стиль Заголовка" 191 180 192 #: php/pages/class-RP-Option-Page-Builder.php: 177181 #: php/pages/class-RP-Option-Page-Builder.php:219 193 182 msgid "Hide Ancestors" 194 183 msgstr "Скрыть предков" 195 184 196 #: php/pages/class-RP-Option-Page-Builder.php:1 43185 #: php/pages/class-RP-Option-Page-Builder.php:185 197 186 msgid "Hide Bio" 198 187 msgstr "Скрыть Био" 199 188 200 #: php/pages/class-RP-Option-Page-Builder.php:2 11189 #: php/pages/class-RP-Option-Page-Builder.php:253 201 190 msgid "Hide Child Family Group" 202 191 msgstr "Скрыть Группу Семья Ребенка" 203 192 204 #: php/pages/class-RP-Option-Page-Builder.php: 76193 #: php/pages/class-RP-Option-Page-Builder.php:111 205 194 msgid "Hide Dates" 206 195 msgstr "Скрыть Даты" 207 196 208 #: php/pages/class-RP-Option-Page-Builder.php: 386197 #: php/pages/class-RP-Option-Page-Builder.php:430 209 198 msgid "Hide Donation Button" 210 199 msgstr "Скрыть кнопку пожертвования" 211 200 212 #: php/pages/class-RP-Option-Page-Builder.php: 274201 #: php/pages/class-RP-Option-Page-Builder.php:317 213 202 msgid "Hide Edit Links" 214 203 msgstr "Скрыть ссылку редактирования" 215 204 216 #: php/pages/class-RP-Option-Page-Builder.php: 160205 #: php/pages/class-RP-Option-Page-Builder.php:202 217 206 msgid "Hide Facts" 218 207 msgstr "Скрыть Факты" 219 208 220 #: php/pages/class-RP-Option-Page-Builder.php:1 26209 #: php/pages/class-RP-Option-Page-Builder.php:168 221 210 msgid "Hide Header" 222 211 msgstr "Скрыть Заголовок" 223 212 224 #: php/pages/class-RP-Option-Page-Builder.php: 91213 #: php/pages/class-RP-Option-Page-Builder.php:125 225 214 msgid "Hide Locations" 226 215 msgstr "Скрыть местоположение" 227 216 228 #: php/pages/class-RP-Option-Page-Builder.php:2 43217 #: php/pages/class-RP-Option-Page-Builder.php:285 229 218 msgid "Hide Picture" 230 219 msgstr "Скрыть картинку" 231 220 232 #: php/pages/class-RP-Option-Page-Builder.php: 290221 #: php/pages/class-RP-Option-Page-Builder.php:334 233 222 msgid "Hide Silhouettes" 234 223 msgstr "Скрыть силуэты" 235 224 236 #: php/pages/class-RP-Option-Page-Builder.php:2 28225 #: php/pages/class-RP-Option-Page-Builder.php:270 237 226 msgid "Hide Spousal Family Groups" 238 227 msgstr "Скрыть Семейные группы супругов" 239 228 240 #: php/pages/class-RP-Option-Page-Builder.php: 399229 #: php/pages/class-RP-Option-Page-Builder.php:443 241 230 msgid "Hide the PayPal Donation button" 242 231 msgstr "Скрыть кнопку Пожертвования с помощью PayPal" 243 232 244 #: php/pages/class-RP-Include-Page-Builder.php: 38233 #: php/pages/class-RP-Include-Page-Builder.php:44 245 234 msgid "Include" 246 235 msgstr "Включить" 247 236 248 #: php/pages/class-RP-Option-Page-Builder.php: 361237 #: php/pages/class-RP-Option-Page-Builder.php:406 249 238 msgid "Index Even Row Color" 250 239 msgstr "Цвет четных строк индекса" 251 240 252 #: php/pages/class-RP-Option-Page-Builder.php: 373241 #: php/pages/class-RP-Option-Page-Builder.php:418 253 242 msgid "Index Header Row Color" 254 243 msgstr "Цвет строки заголовка индекса" 255 244 256 #: php/pages/class-RP-Option-Page-Builder.php: 367245 #: php/pages/class-RP-Option-Page-Builder.php:412 257 246 msgid "Index Odd Row Color" 258 247 msgstr "Цвет нечетных строк индекса" 259 248 260 #: rootspersona.php: 117249 #: rootspersona.php:409 261 250 msgid "Invalid person id." 262 251 msgstr "Неверный ID (идентификатор) персоны" 263 252 264 #: php/class-RP-Persona-Site-Mender.php:1 73253 #: php/class-RP-Persona-Site-Mender.php:189 265 254 msgid "Invalid person page assignment in database (no personid)." 266 255 msgstr "Неверное сопоставление страницы персоны в базе данных (отсутствует идентификатор персоны (personid) )" 267 256 268 #: php/class-RP-Persona-Site-Mender.php:2 32257 #: php/class-RP-Persona-Site-Mender.php:251 269 258 msgid "Invalid source page assignment in database (no sourceid)." 270 259 msgstr "Неверное сопоставление страницы источника в базе данных (отсутствует идентификатор источника (sourceid))" 271 260 272 #: php/class-RP-Persona-Site-Mender.php:2 22261 #: php/class-RP-Persona-Site-Mender.php:241 273 262 msgid "Invalid source page assignment in database (page missing)." 274 263 msgstr "Неверное сопоставление страницы источника в базе данных (страница утеряна)" 275 264 276 #: php/class-RP-Persona-Site-Mender.php:2 40265 #: php/class-RP-Persona-Site-Mender.php:259 277 266 msgid "Invalid source page assignment in database (wrong sourceId)." 278 267 msgstr "Неверное сопоставление страницы источника в базе данных (неверный идентификатор источника (sourceid))" 279 268 280 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:1 71269 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:187 281 270 msgid "Marriage" 282 271 msgstr "В браке" 283 272 284 #: php/pages/class-RP-Option-Page-Builder.php: 53285 #: php/pages/class-RP-Option-Page-Builder.php: 72273 #: php/pages/class-RP-Option-Page-Builder.php:63 274 #: php/pages/class-RP-Option-Page-Builder.php:82 286 275 msgid "Members Only" 287 276 msgstr "Только для Участников" 288 277 289 #: php/pages/class-RP-Option-Page-Builder.php: 383278 #: php/pages/class-RP-Option-Page-Builder.php:427 290 279 msgid "Misc Options" 291 280 msgstr "Разное" 292 281 293 #: rootspersona.php:277 294 msgid "Missing" 295 msgstr "Не найдено" 296 297 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:123 282 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:137 283 #: php/templates/class-RP-Persona-Templates.php:343 284 #: php/templates/class-RP-Persona-Templates.php:437 285 #: php/templates/class-RP-Persona-Templates.php:535 298 286 msgid "Mother" 299 287 msgstr "Мать" 300 288 301 #: php/pages/class-RP-Option-Page-Builder.php: 88302 #: php/pages/class-RP-Option-Page-Builder.php:1 03303 #: php/pages/class-RP-Option-Page-Builder.php:1 38304 #: php/pages/class-RP-Option-Page-Builder.php:1 55305 #: php/pages/class-RP-Option-Page-Builder.php: 172306 #: php/pages/class-RP-Option-Page-Builder.php: 189307 #: php/pages/class-RP-Option-Page-Builder.php:2 06308 #: php/pages/class-RP-Option-Page-Builder.php:2 23309 #: php/pages/class-RP-Option-Page-Builder.php:2 40310 #: php/pages/class-RP-Option-Page-Builder.php:2 55311 #: php/pages/class-RP-Option-Page-Builder.php: 270312 #: php/pages/class-RP-Option-Page-Builder.php: 286313 #: php/pages/class-RP-Option-Page-Builder.php:3 02289 #: php/pages/class-RP-Option-Page-Builder.php:121 290 #: php/pages/class-RP-Option-Page-Builder.php:135 291 #: php/pages/class-RP-Option-Page-Builder.php:180 292 #: php/pages/class-RP-Option-Page-Builder.php:197 293 #: php/pages/class-RP-Option-Page-Builder.php:214 294 #: php/pages/class-RP-Option-Page-Builder.php:231 295 #: php/pages/class-RP-Option-Page-Builder.php:248 296 #: php/pages/class-RP-Option-Page-Builder.php:265 297 #: php/pages/class-RP-Option-Page-Builder.php:282 298 #: php/pages/class-RP-Option-Page-Builder.php:297 299 #: php/pages/class-RP-Option-Page-Builder.php:313 300 #: php/pages/class-RP-Option-Page-Builder.php:329 301 #: php/pages/class-RP-Option-Page-Builder.php:346 314 302 msgid "No" 315 303 msgstr "Нет" 316 304 317 #: rootspersona.php:352 rootspersona.php:414 305 #: rootspersona.php:816 306 #: rootspersona.php:855 307 #: rootspersona.php:942 318 308 msgid "No people selected." 319 309 msgstr "Не выбрано Людей" 320 310 321 #: php/class-RP-Persona-Site-Mender.php:2 73311 #: php/class-RP-Persona-Site-Mender.php:295 322 312 msgid "No persona pages found." 323 313 msgstr "Страницы персоны не найдены" 324 314 325 #: php/class-RP-Persona-Site-Mender.php: 74326 #: php/class-RP-Persona-Site-Mender.php: 82327 #: php/class-RP-Persona-Site-Mender.php: 90328 #: php/class-RP-Persona-Site-Mender.php: 98329 #: php/class-RP-Persona-Site-Mender.php:1 06315 #: php/class-RP-Persona-Site-Mender.php:89 316 #: php/class-RP-Persona-Site-Mender.php:97 317 #: php/class-RP-Persona-Site-Mender.php:105 318 #: php/class-RP-Persona-Site-Mender.php:113 319 #: php/class-RP-Persona-Site-Mender.php:121 330 320 msgid "Obsolete" 331 321 msgstr "Устарело" 332 322 333 #: php/pages/class-RP-Option-Page-Builder.php:1 20323 #: php/pages/class-RP-Option-Page-Builder.php:162 334 324 msgid "Original" 335 325 msgstr "Оригинал" 336 326 337 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php: 80327 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:94 338 328 msgid "PARENT" 339 329 msgstr "РОДИТЕЛИ" 340 330 341 #: php/class-RP-Persona-Site-Mender.php:143 342 #: php/class-RP-Persona-Site-Mender.php:204 343 #: php/class-RP-Persona-Site-Mender.php:263 344 #: php/pages/class-RP-Option-Page-Builder.php:312 331 #: php/pages/class-RP-Option-Page-Builder.php:356 345 332 msgid "Page" 346 333 msgstr "Страница" 347 334 348 #: rootspersona.php:363 335 #. translators: %s: page ID created by RootsPersona. 336 #: rootspersona.php:783 337 #, php-format 349 338 msgid "Page %s created for" 350 339 msgstr "Страница %s создана для" 351 340 352 #: php/class-RP-Persona-Site-Mender.php: 191341 #: php/class-RP-Persona-Site-Mender.php:207 353 342 msgid "Page title out of synch with person name" 354 343 msgstr "Заголовок страницы не синхронизирован с именем персоны" 355 344 356 #: php/class-RP-Persona-Site-Mender.php:2 50345 #: php/class-RP-Persona-Site-Mender.php:269 357 346 msgid "Page title out of synch with source name" 358 347 msgstr "Заголовок страницы не синхронизирован с названием источника" 359 348 360 #: php/class-RP-Persona-Site-Mender.php: 185361 #: php/class-RP-Persona-Site-Mender.php:2 44349 #: php/class-RP-Persona-Site-Mender.php:201 350 #: php/class-RP-Persona-Site-Mender.php:263 362 351 msgid "Page title updated." 363 352 msgstr "Заголовок страницы обновлен." 364 353 365 #: php/pages/class-RP-Persona-Page-Builder.php: 82354 #: php/pages/class-RP-Persona-Page-Builder.php:126 366 355 msgid "Page will be removed but supporting data will not be deleted. Proceed?" 367 356 msgstr "Страница будет удалена, но данные - оставлены. Продолжить?" 368 357 369 #: php/pages/class-RP-Evidence-Page-Builder.php: 29358 #: php/pages/class-RP-Evidence-Page-Builder.php:32 370 359 msgid "Person(s) of Interest" 371 360 msgstr "Заинтересованное Лицо (-а)" 372 361 373 #: php/pages/class-RP-Option-Page-Builder.php: 355362 #: php/pages/class-RP-Option-Page-Builder.php:400 374 363 msgid "Persona Frames" 375 364 msgstr "Кадры Персоны" 376 365 377 #: php/pages/class-RP-Include-Page-Builder.php:2 1366 #: php/pages/class-RP-Include-Page-Builder.php:25 378 367 msgid "Previously excluded persons must be re-imported" 379 368 msgstr "Ранее исключенные персоны нужно повторно импортировать" 380 369 381 #: php/pages/class-RP-Option-Page-Builder.php: 31370 #: php/pages/class-RP-Option-Page-Builder.php:41 382 371 msgid "Privacy Options" 383 372 msgstr "Настройки Конфиденциальности" 384 373 385 #: php/pages/class-RP-Option-Page-Builder.php: 74374 #: php/pages/class-RP-Option-Page-Builder.php:84 386 375 msgid "Privacy Setting to use when nothing else applies" 387 376 msgstr "Настройки Конфиденциальности если не применяется никаких дополнительных настроек" 388 377 389 #: php/pages/class-RP-Option-Page-Builder.php: 52390 #: php/pages/class-RP-Option-Page-Builder.php: 71378 #: php/pages/class-RP-Option-Page-Builder.php:62 379 #: php/pages/class-RP-Option-Page-Builder.php:81 391 380 msgid "Private, Admins Only" 392 381 msgstr "Приватно, только для Администраторов" 393 382 394 #: php/pages/class-RP-Option-Page-Builder.php: 54395 #: php/pages/class-RP-Option-Page-Builder.php: 73383 #: php/pages/class-RP-Option-Page-Builder.php:64 384 #: php/pages/class-RP-Option-Page-Builder.php:83 396 385 msgid "Public" 397 386 msgstr "Публично" 398 387 399 #: php/class-RP-Persona-Site-Mender.php: 284388 #: php/class-RP-Persona-Site-Mender.php:306 400 389 msgid "Repair Inconsistencies?" 401 390 msgstr "Исправить Ошибки?" 402 391 403 #: php/pages/class-RP-Add-Page-Builder.php: 65404 #: php/pages/class-RP-Include-Page-Builder.php:4 0405 #: php/pages/class-RP-Upload-Page-Builder.php: 66392 #: php/pages/class-RP-Add-Page-Builder.php:70 393 #: php/pages/class-RP-Include-Page-Builder.php:46 394 #: php/pages/class-RP-Upload-Page-Builder.php:81 406 395 msgid "Reset" 407 396 msgstr "Перезагрузить" 408 397 409 #: php/class-RP-Persona-Site-Mender.php:293 410 #: php/class-RP-Persona-Site-Mender.php:336 411 #: php/class-RP-Persona-Site-Mender.php:359 398 #: php/class-RP-Persona-Site-Mender.php:313 399 #: php/class-RP-Persona-Site-Mender.php:356 412 400 #: php/class-RP-Persona-Site-Mender.php:378 401 #: php/class-RP-Persona-Site-Mender.php:395 413 402 msgid "Return" 414 403 msgstr "Вернуться" 415 404 416 #: php/pages/class-RP-Tools-Page-Builder.php:1 18405 #: php/pages/class-RP-Tools-Page-Builder.php:140 417 406 msgid "Review Excluded Persons" 418 407 msgstr "Обзор Исключенных Персон" 419 408 420 #: php/pages/class-RP-Tools-Page-Builder.php:74 421 msgid "Review the list of people you have uploaded but not created pages for." 422 msgstr "Смотреть список людей которые были загружены, но для которых не были созданы страницы" 423 424 #: php/pages/class-RP-Option-Page-Builder.php:29 425 #: php/pages/class-RP-Option-Page-Builder.php:479 409 #: php/pages/class-RP-Option-Page-Builder.php:39 410 #: php/pages/class-RP-Option-Page-Builder.php:504 426 411 msgid "Save Changes" 427 412 msgstr "Сохранить изменения" 428 413 429 #: php/pages/class-RP-Option-Page-Builder.php:89 414 #. translators: %s: context (e.g. persona). 415 #: php/pages/class-RP-Option-Page-Builder.php:123 416 #, php-format 430 417 msgid "Some people may want to hide dates for privacy purposes. This is a global flag (impacts all %s pages)." 431 418 msgstr "Некоторые люди, возможно захотят скрыть даты по соображениям конфиденциальности. Это глобальная настройка (влияет на все %s страницы)" 432 419 433 #: php/pages/class-RP-Option-Page-Builder.php:287 420 #. translators: %s: context (e.g. persona). 421 #: php/pages/class-RP-Option-Page-Builder.php:331 422 #, php-format 434 423 msgid "Some people may want to hide the edit links at the bottom of the %s page" 435 424 msgstr "Некоторые люди, возможно захотят скрыть ссылку для редактирования внизу %s страницы" 436 425 437 #: php/pages/class-RP-Option-Page-Builder.php:3 03426 #: php/pages/class-RP-Option-Page-Builder.php:347 438 427 msgid "Some people may want to hide the silhouette placeholders in the picture panel" 439 428 msgstr "Если вы хотите скрыть замещающие силуэты на панели фото - выберите Да иначе - Нет" 440 429 441 #: php/pages/class-RP-Evidence-Page-Builder.php:1 5430 #: php/pages/class-RP-Evidence-Page-Builder.php:18 442 431 msgid "Source" 443 432 msgstr "Источник" 444 433 445 #: php/pages/class-RP-Option-Page-Builder.php:3 15434 #: php/pages/class-RP-Option-Page-Builder.php:360 446 435 msgid "Style Options" 447 436 msgstr "Настройки Стилей" 448 437 449 #: php/class-RP-Persona-Site-Mender.php:3 71438 #: php/class-RP-Persona-Site-Mender.php:390 450 439 msgid "Tables emptied." 451 440 msgstr "Таблицы Очищены" 452 441 453 #: php/pages/class-RP-Upload-Page-Builder.php: 70442 #: php/pages/class-RP-Upload-Page-Builder.php:85 454 443 msgid "The following PHP settings have an impact on GEDCOM file processing" 455 444 msgstr "Следующие настройки PHP влияют на обработку GEDCOM-файла" 456 445 457 #: php/pages/class-RP-Upload-Page-Builder.php:2 1446 #: php/pages/class-RP-Upload-Page-Builder.php:24 458 447 msgid "The system will be uploading the file AND parsing each person into the database" 459 448 msgstr "Система загрузит файл и занесет каждую персону в базу данных" 460 449 461 #: php/pages/class-RP-Edit-Page-Builder.php:1 38450 #: php/pages/class-RP-Edit-Page-Builder.php:152 462 451 msgid "This person will be visible based on the privacy setting for living persons or the global default setting." 463 452 msgstr "Видимость данной персоны будет зависеть от настроек конфиденциальности для ныне живущих персон или глобальных настроек по умолчанию" 464 453 465 #: php/pages/class-RP-Edit-Page-Builder.php:1 32454 #: php/pages/class-RP-Edit-Page-Builder.php:146 466 455 msgid "This person will be visible to ANYONE, even if they are living." 467 456 msgstr "Эта персона будет видна кому угодно, даже если это ныне живущая персона" 468 457 469 #: php/pages/class-RP-Edit-Page-Builder.php:1 26458 #: php/pages/class-RP-Edit-Page-Builder.php:140 470 459 msgid "This person will only be visible to users who are logged in." 471 460 msgstr "Эта персона будет видна только зарегистрированному пользователю который выполнил вход на сайте" 472 461 473 #: php/pages/class-RP-Upload-Page-Builder.php: 64462 #: php/pages/class-RP-Upload-Page-Builder.php:79 474 463 msgid "Upload" 475 464 msgstr "Загрузить" 476 465 477 #: php/pages/class-RP-Tools-Page-Builder.php:58 478 msgid "Upload (or re-upload) a GEDCOM file." 479 msgstr "Загрузить (или повторно загрузить) GEDCOM-файл" 480 481 #: php/pages/class-RP-Option-Page-Builder.php:306 466 #: php/pages/class-RP-Option-Page-Builder.php:350 482 467 msgid "Utility Pages" 483 468 msgstr "Страницы Настроек" 484 469 485 #: php/pages/class-RP-Tools-Page-Builder.php:1 33470 #: php/pages/class-RP-Tools-Page-Builder.php:159 486 471 msgid "Validate persona Pages" 487 472 msgstr "Проверить страницы Персоны" 488 473 489 #: php/pages/class-RP-Option-Page-Builder.php: 87490 #: php/pages/class-RP-Option-Page-Builder.php:1 02491 #: php/pages/class-RP-Option-Page-Builder.php:1 37492 #: php/pages/class-RP-Option-Page-Builder.php:1 54493 #: php/pages/class-RP-Option-Page-Builder.php: 171494 #: php/pages/class-RP-Option-Page-Builder.php: 188495 #: php/pages/class-RP-Option-Page-Builder.php:2 05496 #: php/pages/class-RP-Option-Page-Builder.php:2 22497 #: php/pages/class-RP-Option-Page-Builder.php:2 39498 #: php/pages/class-RP-Option-Page-Builder.php:2 54499 #: php/pages/class-RP-Option-Page-Builder.php: 269500 #: php/pages/class-RP-Option-Page-Builder.php: 285501 #: php/pages/class-RP-Option-Page-Builder.php:3 01474 #: php/pages/class-RP-Option-Page-Builder.php:120 475 #: php/pages/class-RP-Option-Page-Builder.php:134 476 #: php/pages/class-RP-Option-Page-Builder.php:179 477 #: php/pages/class-RP-Option-Page-Builder.php:196 478 #: php/pages/class-RP-Option-Page-Builder.php:213 479 #: php/pages/class-RP-Option-Page-Builder.php:230 480 #: php/pages/class-RP-Option-Page-Builder.php:247 481 #: php/pages/class-RP-Option-Page-Builder.php:264 482 #: php/pages/class-RP-Option-Page-Builder.php:281 483 #: php/pages/class-RP-Option-Page-Builder.php:296 484 #: php/pages/class-RP-Option-Page-Builder.php:312 485 #: php/pages/class-RP-Option-Page-Builder.php:328 486 #: php/pages/class-RP-Option-Page-Builder.php:345 502 487 msgid "Yes" 503 488 msgstr "Да" 504 489 505 #: php/pages/class-RP-Upload-Page-Builder.php:1 00490 #: php/pages/class-RP-Upload-Page-Builder.php:117 506 491 msgid "You may change the above values in the php.ini file" 507 492 msgstr "Вы можете изменить данную настройку в файле php.ini" 508 493 509 #: php/class-RP-Persona-Site-Mender.php:277 494 #. translators: %s: plugin or context name. 495 #: php/class-RP-Persona-Site-Mender.php:300 496 #, php-format 510 497 msgid "Your %s setup is VALID." 511 498 msgstr "Ваша %s установка Действительна" 512 499 513 #: php/pages/class-RP-Upload-Page-Builder.php: 84500 #: php/pages/class-RP-Upload-Page-Builder.php:101 514 501 msgid "Your system is configured in " 515 502 msgstr "Ваша система настроена в" 516 503 517 #: rootspersona.php:468 518 msgid "action not supported" 519 msgstr "данное действие не поддерживается" 520 521 #: php/class-RP-Persona-Installer.php:215 504 #: php/class-RP-Persona-Installer.php:279 522 505 msgid "allowing an admin to manage the display of those pages" 523 506 msgstr "позволяет администратору управлять отображением этих страниц" 524 507 525 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:168 508 #. translators: Marriage row: before place name (English "at Boston"). 509 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:184 510 msgctxt "marriage: before place" 526 511 msgid "at" 527 512 msgstr "при" 528 513 529 #: php/pages/class-RP-Upload-Page-Builder.php:1 02514 #: php/pages/class-RP-Upload-Page-Builder.php:119 530 515 msgid "or by override the settings in your .htaccess file as defined below" 531 516 msgstr "или путем перезаписи настроек в файле .htaccess как указано ниже" 532 517 533 #: php/class-RP-Persona-Site-Mender.php:400 518 #. translators: %s: batch id. 519 #: php/class-RP-Persona-Site-Mender.php:418 520 #, php-format 534 521 msgid "source page(s) added for batchId %s" 535 522 msgstr "страница (ы) источника добавленные для batchId %s" 536 523 537 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:157 524 #. translators: Marriage row: short word before the spouse name (English "to Jane Doe"). 525 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:172 526 msgctxt "marriage: before spouse name" 538 527 msgid "to" 539 msgstr " c"540 541 #: php/pages/class-RP-Upload-Page-Builder.php: 71528 msgstr "с" 529 530 #: php/pages/class-RP-Upload-Page-Builder.php:86 542 531 msgid "version" 543 532 msgstr "версия" 544 533 545 #: php/pages/panels/class-RP-Facts-Panel-Creator.php: 48534 #: php/pages/panels/class-RP-Facts-Panel-Creator.php:56 546 535 msgid "with" 547 536 msgstr "с" 548 537 549 #: surname_widget.php:12 3538 #: surname_widget.php:122 550 539 msgid "How Many:" 551 540 msgstr "Сколько" 552 541 553 #: surname_widget.php: 39542 #: surname_widget.php:41 554 543 msgid "Surname Widget" 555 544 msgstr "Виджет Фамилий" 556 545 557 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php: 379546 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:404 558 547 msgid "Link to a Spouse" 559 548 msgstr "Связать с Супругом" 560 549 561 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:3 61550 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:386 562 551 msgid "Unlink from this Spouse" 563 552 msgstr "Удалить связь с Супругом" 564 553 565 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:3 22554 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:347 566 555 msgid "Link to a Father" 567 556 msgstr "Связать с Отцом" 568 557 569 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:3 24558 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:349 570 559 msgid "Link to a Mother" 571 560 msgstr "Связать с Матерью" 572 561 573 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:3 20562 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:345 574 563 msgid "Unlink from Parents" 575 564 msgstr "Удалить связь с Родителями" 576 565 577 #: php/pages/class-RP-Edit-Page-Builder.php: 25566 #: php/pages/class-RP-Edit-Page-Builder.php:42 578 567 msgid "New" 579 568 msgstr "Новый" 580 569 581 #: php/pages/class-RP-Edit-Page-Builder.php:2 3582 #: php/pages/class-RP- Edit-Page-Builder.php:61570 #: php/pages/class-RP-Edit-Page-Builder.php:28 571 #: php/pages/class-RP-Upload-Page-Builder.php:146 583 572 msgid "Cancel" 584 573 msgstr "Отменить" 585 574 586 #: php/pages/class-RP-Edit-Page-Builder.php: 21587 #: php/pages/class-RP-Edit-Page-Builder.php: 59575 #: php/pages/class-RP-Edit-Page-Builder.php:34 576 #: php/pages/class-RP-Edit-Page-Builder.php:73 588 577 msgid "Save" 589 578 msgstr "Сохранить" 590 579 591 #: php/class-RP-Persona-Validator.php: 79580 #: php/class-RP-Persona-Validator.php:86 592 581 msgid "Surname and/or Given name required." 593 582 msgstr "Необходимо указать Фамилию и / или Данное имя." 594 583 595 #: surname_widget.php:11 2584 #: surname_widget.php:111 596 585 msgid "Top Surnames" 597 586 msgstr "Топ Фамилий" 598 587 599 #. Description of the plugin/theme 588 #. Description of the plugin 589 #: rootspersona.php 600 590 msgid "Build one or more family history pages from a Gedcom file." 601 591 msgstr "Создает от одной и более страниц семейной истории из файла GEDCOM" 602 592 603 #: rootspersona.php: 496593 #: rootspersona.php:1064 604 594 msgid "Empty File." 605 595 msgstr "Файл пуст" 606 596 607 #: rootspersona.php: 547597 #: rootspersona.php:1113 608 598 msgid "Content reserved for registered members." 609 599 msgstr "Контент только для зарегистрированных пользователей." 610 600 611 #: rootspersona.php:494 601 #: rootspersona.php:1058 602 #: rootspersona.php:1061 612 603 msgid "Error Uploading File." 613 604 msgstr "Ошибка загрузки файла" 614 605 615 #: surname_widget.php:3 3606 #: surname_widget.php:35 616 607 msgid "An widget that displays the top X surnames in rootspersona." 617 608 msgstr "Виджет, отображающий Топ Фамилий РутсПерсоны" 618 609 619 #: surname_widget.php:11 7610 #: surname_widget.php:116 620 611 msgid "Title:" 621 612 msgstr "Заголовок" 622 613 623 #: php/pages/panels/class-RP-Picture-Panel-Creator.php: 22614 #: php/pages/panels/class-RP-Picture-Panel-Creator.php:95 624 615 msgid "Picture Gallery" 625 616 msgstr "Фото Галерея" 626 617 627 #: php/pages/panels/class-RP-Descendancy-Panel-Creator.php: 16618 #: php/pages/panels/class-RP-Descendancy-Panel-Creator.php:20 628 619 msgid "Descendancy Chart" 629 620 msgstr "Список родственников по нисходящей линии" 630 621 631 #: php/pages/class-RP-Upload-Page-Builder.php: 95622 #: php/pages/class-RP-Upload-Page-Builder.php:112 632 623 msgid "and may need to split your GEDCOM file accordingly" 633 624 msgstr "и, возможно, потребуется соответствующим образом разделить файл GEDCOM" 634 625 635 #: php/pages/class-RP-Upload-Page-Builder.php: 93626 #: php/pages/class-RP-Upload-Page-Builder.php:110 636 627 msgid "However you may still run into an issue with server time limit settings (usually 300 seconds)" 637 628 msgstr "Тем не менее, вы можете столкнуться с проблемой ограничения выполнения скрипта на сервере (обычно оно составляет около 300 секунд)" 638 629 639 #: php/pages/class-RP-Upload-Page-Builder.php: 90630 #: php/pages/class-RP-Upload-Page-Builder.php:107 640 631 msgid "will try to override your system settings as needed to allow processing of a large GEDCOM file" 641 632 msgstr "будет предпринята попытка перезаписи ваших системных настроек - это необходимо для обработки большого GEDCOM-файла" 642 633 643 #: php/pages/class-RP-Upload-Page-Builder.php: 86634 #: php/pages/class-RP-Upload-Page-Builder.php:103 644 635 msgid "will NOT be able to override your system settings to allow processing of a large GEDCOM file." 645 636 msgstr "не удалось перезаписать настройки вашей системы для обработки большого GEDCOM-файла" 646 637 647 #: php/pages/class-RP-Upload-Page-Builder.php:2 3638 #: php/pages/class-RP-Upload-Page-Builder.php:26 648 639 msgid "resulting in a white screen with no status message" 649 640 msgstr "в итоге - белый экран без каких-либо сообщений на нем" 650 641 651 #: php/pages/class-RP-Upload-Page-Builder.php:2 2642 #: php/pages/class-RP-Upload-Page-Builder.php:25 652 643 msgid "Many users have reported hitting PHP time limit constraints when processing large files" 653 644 msgstr "Многие пользователи жаловались на проблемы связанные с лимитом времени работы скриптов php при обработке больших файлов" 654 645 655 #: php/pages/class-RP-Upload-Page-Builder.php:2 0646 #: php/pages/class-RP-Upload-Page-Builder.php:23 656 647 msgid "Please note that GEDCOM processing is the most system taxing processing this plugin performs" 657 648 msgstr "Не забывайте, что обработка файла GEDCOM - это самый сильно нагружающий систему процесс из тех, которые выполняет плагин." 658 649 659 #: php/pages/class-RP-Tools-Page-Builder.php:153 660 msgid "Perform a bulk deletion of all %s and evidence pages. You will be prompted before the data is deleted." 661 msgstr "Выполняет массовое удаление всех страниц %s и страниц данных. Перед удалением Вы получите запрос на подтверждение удаления." 662 663 #: php/pages/class-RP-Tools-Page-Builder.php:137 664 msgid "Identify orphaned %s pages. Includes all pages with %s shortcode and no reference in the database, or reference in the database with no corresponding page." 665 msgstr "Идентифицировать подвисшие страницы %s, включая все страницы с шоткодами %s и страницы не имеющие ссылок в базе данных или, напротив, ссылки, присутствующие в базе данных, но без соответствующих им страниц." 666 667 #: php/pages/class-RP-Tools-Page-Builder.php:138 650 #: php/pages/class-RP-Tools-Page-Builder.php:166 668 651 msgid "Will also identify/sync pages with the wrong parent page assigned." 669 652 msgstr "При этом также будут идентифицированы либо синхронизированы страницы которым была присвоена неверная родительская страница " 670 653 671 #: php/pages/class-RP-Tools-Page-Builder.php: 92654 #: php/pages/class-RP-Tools-Page-Builder.php:110 672 655 msgid "If rootspersona is set as the System of record, this screenallows you to add a new individual from scratch." 673 656 msgstr "Если на сайте для РутсПерсоны разрешена Система Записи (в настройках РутсПерсоны), то с помощью этой кнопки можно добавить нового человека с нуля." 674 657 675 #: php/pages/class-RP-Tools-Page-Builder.php:5 6658 #: php/pages/class-RP-Tools-Page-Builder.php:58 676 659 msgid "Upload GEDCOM" 677 660 msgstr "Загрузить GEDCOM-файл" 678 661 679 #: php/pages/class-RP-Tools-Page-Builder.php:1 22662 #: php/pages/class-RP-Tools-Page-Builder.php:144 680 663 msgid "Review people you have previous excluded, and include the ones you select." 681 664 msgstr "Можно просмотреть тех людей, которых вы ранее исключили, и включить их вновь по вашему выбору." 682 665 683 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php: 21666 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:33 684 667 msgid "Family Group Sheet - Child" 685 668 msgstr "Семья персоны (Персона здесь - Ребёнок)" 686 669 687 #: php/class-RP-Persona-Installer.php:5 1670 #: php/class-RP-Persona-Installer.php:52 688 671 msgid "rootspersona Tree" 689 672 msgstr "Дерево РутсПерсоны" 690 673 691 #: php/class-RP-Persona-Installer.php:2 12674 #: php/class-RP-Persona-Installer.php:276 692 675 msgid "The default WordPress themes display all first level pages by default" 693 676 msgstr "Тема Вордпресс установленная по Умолчанию отображает все страницы первого уровня вложенности" 694 677 695 #: php/class-RP-Persona-Installer.php:2 13678 #: php/class-RP-Persona-Installer.php:277 696 679 msgid "When importing a GEDCOM file, hundreds of pages can be created, all first level pages" 697 680 msgstr "В результате импорта GEDCOM-файла могут быть созданы сотни страниц - все первого уровня вложенности." 698 681 699 #: php/class-RP-Persona-Installer.php:4 6682 #: php/class-RP-Persona-Installer.php:47 700 683 msgid "Evidence Index" 701 684 msgstr "Индекс Данных" 702 685 703 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:2 69686 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:292 704 687 msgid "Family Group Sheet - Spouse" 705 688 msgstr "Семья Персоны (Персона здесь - Родитель)" 706 689 707 #: php/pages/panels/class-RP-Evidence-Panel-Creator.php:1 5690 #: php/pages/panels/class-RP-Evidence-Panel-Creator.php:19 708 691 msgid "Evidence" 709 692 msgstr "Данные" 710 693 711 #: php/pages/panels/class-RP-Evidence-Panel-Creator.php: 27694 #: php/pages/panels/class-RP-Evidence-Panel-Creator.php:30 712 695 msgid "Evidence page has not yet been created for this source" 713 696 msgstr "Страница данных не была создана для данного источника" 714 697 715 #: php/pages/class-RP-Upload-Page-Builder.php:45 698 #: php/pages/class-RP-Upload-Page-Builder.php:50 699 #, php-format 716 700 msgid "Please note that %s cannot link persons in different batches" 717 701 msgstr "Внимание! Имейте ввиду, что плагин %s не может связывать персон из разных групп!" 718 702 719 #: php/pages/class-RP-Option-Page-Builder.php:476 720 msgid "Advanced feature. Data will be used in place of the RootsPersona shortcode when generating new pages.<br>{%personid%} will be replaced by the actual person id when the page is created.<br>{%batchid%} will be replaced by the current batch id when the page is created.<br>{%picfile1%} will be replaced by the current picfile1 when page is created.<br>The rest is still rendered when the page is requested." 721 msgstr "Настройка для продвинутых пользователей. Данные будут использоваться при генерации новых страниц в месте расположения шоткодов РутсПерсоны. <br> при создании страницы {%personid%} будет заменен текущим идентификатором (id) персоны, <br>{%batchid%} - текущим идентификатором группы, <br>{%picfile1%} - текущим идентификатором картинки. <br> Все остальное при запросе страницы будет отображаться по-прежнему." 722 723 #: php/pages/class-RP-Persona-Page-Builder.php:83 703 #: php/pages/class-RP-Persona-Page-Builder.php:127 724 704 msgid "Page will be removed and supporting data will be deleted. Proceed?" 725 705 msgstr "Страница будет удалена, также как и данные. Продолжить?" 726 706 727 #: php/pages/class-RP-Option-Page-Builder.php:473 728 msgid "Custom Page Shortcodes" 729 msgstr "Шоткоды страницы пользователя" 730 731 #: php/pages/class-RP-Option-Page-Builder.php:437 707 #: php/pages/class-RP-Option-Page-Builder.php:481 732 708 msgid "Yes indicates data may be entered from this website. Editing data from this website AND importing GEDCOM data is NOT recommended." 733 709 msgstr " \"Да\" - разрешает редактирование данных на сайте. Однако одновременное редактирование данных на сайте и импорт GEDCOM - данных не рекомендуются. На Ваш выбор, не забывайте о бэкапах. " 734 710 735 #: php/pages/class-RP-Option-Page-Builder.php:4 23711 #: php/pages/class-RP-Option-Page-Builder.php:467 736 712 msgid "System Of Record" 737 713 msgstr "Использовать Систему Записи на сайте." 738 714 739 #: php/pages/class-RP-Option-Page-Builder.php:4 20715 #: php/pages/class-RP-Option-Page-Builder.php:464 740 716 msgid "Indicates the number of rows on an index page. Default is 25." 741 717 msgstr "Количество строк на индексной странице РутсПерсоны. По умолчанию 25." 742 718 743 #: php/pages/class-RP-Option-Page-Builder.php:3 43719 #: php/pages/class-RP-Option-Page-Builder.php:388 744 720 msgid "Group Background Color" 745 721 msgstr "Цвет фона для группы" 746 722 747 #: php/pages/class-RP-Option-Page-Builder.php:313 723 #. translators: %s: context (e.g. persona). 724 #: php/pages/class-RP-Option-Page-Builder.php:358 725 #, php-format 748 726 msgid "you want %s pages to be organized under in a menu structure. 0 indicates no parent page" 749 727 msgstr "Если вы хотите, чтобы страницы %s были организованы в виде меню. всех персон. Номер идентификатора страницы, где расположено это меню (обычно ее адрес - /rootspersona-tree/). 0 означает отсутствие данной страницы." 750 728 751 #: php/pages/class-RP-Option-Page-Builder.php:3 09729 #: php/pages/class-RP-Option-Page-Builder.php:353 752 730 msgid "Parent Page Id" 753 731 msgstr "Идентификатор (ID) родительской страницы" 754 732 755 #: php/pages/class-RP-Option-Page-Builder.php:2 24733 #: php/pages/class-RP-Option-Page-Builder.php:266 756 734 msgid "Skip the Family Group panel where the person is a Child on persona pages" 757 735 msgstr "Не отображать панель Семейной группы (Персона в качестве Ребенка) на страницах Персон" 758 736 759 #: php/pages/class-RP-Option-Page-Builder.php:2 41737 #: php/pages/class-RP-Option-Page-Builder.php:283 760 738 msgid "Skip the Family Group panels where the person is a Spouse on persona pages" 761 739 msgstr "Не отображать панель Семейная группа (Персона в качестве Супруга) на страницах Персон" 762 740 763 #: php/pages/class-RP-Option-Page-Builder.php:256 741 #. translators: %s: context (e.g. persona). 742 #: php/pages/class-RP-Option-Page-Builder.php:299 743 #, php-format 764 744 msgid "Skip the pictures panel on %s pages. Still displays the image in the Header panel" 765 745 msgstr "Не отображать панель Фото на страницах %s. При этом изображение на панели Заголовка будет по-прежнему отображаться." 766 746 767 #: php/pages/class-RP-Option-Page-Builder.php: 258747 #: php/pages/class-RP-Option-Page-Builder.php:301 768 748 msgid "Hide Evidence" 769 749 msgstr "Скрыть Данные" 770 750 771 #: php/pages/class-RP-Option-Page-Builder.php: 271751 #: php/pages/class-RP-Option-Page-Builder.php:314 772 752 msgid "Skip the evidence panel in persona pages." 773 753 msgstr "Не отображать панель Данных на страницах Персоны" 774 754 775 #: php/pages/class-RP-Option-Page-Builder.php:2 07755 #: php/pages/class-RP-Option-Page-Builder.php:249 776 756 msgid "Skip the Descendancy panel on persona pages" 777 757 msgstr "Не отображать панель Родство По Нисходящей линии на страницах Персоны" 778 758 779 #: php/pages/class-RP-Option-Page-Builder.php: 194759 #: php/pages/class-RP-Option-Page-Builder.php:236 780 760 msgid "Hide Descendancy" 781 761 msgstr "Скрыть область \"Родство По нисходящей\"" 782 762 783 #: php/pages/class-RP-Option-Page-Builder.php:1 56763 #: php/pages/class-RP-Option-Page-Builder.php:198 784 764 msgid "Skip the Biography panel on persona pages" 785 765 msgstr "Не отображать панель Биографии на страницах Персоны" 786 766 787 #: php/pages/class-RP-Option-Page-Builder.php: 173767 #: php/pages/class-RP-Option-Page-Builder.php:215 788 768 msgid "Skip the Facts panel on persona pages" 789 769 msgstr "Не отображать панель Фактов на страницах Персоны" 790 770 791 #: php/pages/class-RP-Option-Page-Builder.php: 190771 #: php/pages/class-RP-Option-Page-Builder.php:232 792 772 msgid "Skip the Ancestors panel on persona pages" 793 773 msgstr "Не отображать панель Предков на страницах Персоны" 794 774 795 #: php/pages/class-RP-Option-Page-Builder.php:1 39775 #: php/pages/class-RP-Option-Page-Builder.php:181 796 776 msgid "Skip the Header Panel on persona pages." 797 777 msgstr "Не отображать панель Заголовка на страницах Персоны" 798 778 799 #: php/pages/class-RP-Option-Page-Builder.php:104 779 #. translators: %s: context (e.g. persona). 780 #: php/pages/class-RP-Option-Page-Builder.php:137 781 #, php-format 800 782 msgid "Some people may want to hide locations for privacy purposes. This is a global flag (impacts all %s pages)." 801 783 msgstr "Некоторые люди, возможно захотят скрыть свое местонахождение по личным причинам. Это глобальная настройка (влияющая на все страницы %s)" 802 784 803 #: php/pages/class-RP-Option-Page-Builder.php: 55785 #: php/pages/class-RP-Option-Page-Builder.php:65 804 786 msgid "Privacy Setting to use for living people (no death date and birth date greater than 110 years ago)" 805 787 msgstr "Настройки Конфиденциальности для Ныне Живущих Людей (дата рождения или смерти не более 110 лет назад)" 806 788 807 #: php/pages/class-RP-Option-Page-Builder.php: 33789 #: php/pages/class-RP-Option-Page-Builder.php:43 808 790 msgid "Living Privacy Setting" 809 msgstr "Текущие настройки Конфиденциальности" 810 811 #: php/pages/class-RP-Option-Page-Builder.php:13 791 msgstr "Приватность живых людей" 792 793 #. translators: %s: context (e.g. persona). 794 #: php/pages/class-RP-Option-Page-Builder.php:17 795 #, php-format 812 796 msgid "Edit page is meant to be accessed using the Edit button on a %s page" 813 797 msgstr "Переход на страницу редактирования производится с помощью кнопки \"Редактировать\" на странице %s" 814 798 815 #: php/pages/class-RP-Evidence-Page-Builder.php:4 6799 #: php/pages/class-RP-Evidence-Page-Builder.php:49 816 800 msgid "Notes" 817 801 msgstr "Заметки" 818 802 819 #: php/pages/class-RP-Edit-Page-Builder.php:1 13803 #: php/pages/class-RP-Edit-Page-Builder.php:127 820 804 msgid "Delete page and exclude this person FROM future uploads." 821 805 msgstr "Удалить страницу и исключить эту персону при будущих загрузках" 822 806 823 #: php/pages/class-RP-Edit-Page-Builder.php:1 20807 #: php/pages/class-RP-Edit-Page-Builder.php:134 824 808 msgid "This person will only be visible to the admin role." 825 809 msgstr "Эта персона будет видна только пользователю с правами Администратора." 826 810 827 #: php/class-RP-Persona-Validator.php: 27811 #: php/class-RP-Persona-Validator.php:30 828 812 msgid "Batch Id required." 829 813 msgstr "Укажите идентификатор (Id) группы" 830 814 831 #: php/class-RP-Persona-Site-Mender.php:351 815 #. translators: %s: batch id. 816 #: php/class-RP-Persona-Site-Mender.php:372 817 #, php-format 832 818 msgid "Data deleted for batchId %s." 833 819 msgstr "Данные удалены для группы %s" 834 820 835 #: php/class-RP-Persona-Site-Mender.php:324 821 #. translators: %s: batch id. 822 #: php/class-RP-Persona-Site-Mender.php:347 823 #, php-format 836 824 msgid "pages deleted from batchId %s" 837 825 msgstr "страницы будут удалены из группы %s" 838 826 839 #: php/class-RP-Persona-Site-Mender.php:1 81827 #: php/class-RP-Persona-Site-Mender.php:197 840 828 msgid "Invalid person page assignment in database (wrong personId)." 841 829 msgstr "Неверное сопоставление страницы персоны в базе данных (неверный идентификатор персоны (personid))" 842 830 843 #: php/class-RP-Persona-Site-Mender.php:1 69844 #: php/class-RP-Persona-Site-Mender.php:1 77845 #: php/class-RP-Persona-Site-Mender.php:2 28846 #: php/class-RP-Persona-Site-Mender.php:2 36831 #: php/class-RP-Persona-Site-Mender.php:185 832 #: php/class-RP-Persona-Site-Mender.php:193 833 #: php/class-RP-Persona-Site-Mender.php:247 834 #: php/class-RP-Persona-Site-Mender.php:255 847 835 msgid "Database updated to remove reference to invalid page." 848 836 msgstr "База данных обновлена, для удаления связи с неверной страницей." 849 837 850 #: php/class-RP-Persona-Site-Mender.php:1 63838 #: php/class-RP-Persona-Site-Mender.php:179 851 839 msgid "Invalid person page assignment in database (page missing)." 852 840 msgstr "Неверное сопоставление страницы персоны в базе данных (страница отсутствует)" 853 841 854 #: php/class-RP-Persona-Site-Mender.php:1 59855 #: php/class-RP-Persona-Site-Mender.php:2 18842 #: php/class-RP-Persona-Site-Mender.php:175 843 #: php/class-RP-Persona-Site-Mender.php:237 856 844 msgid "Database updated to remove reference to missing page." 857 845 msgstr "База данных обновлена, для удаления связи с отсутствующей страницей." 858 846 859 #: php/class-RP-Persona-Site-Mender.php:129 847 #. translators: %s: parent page info. 848 #: php/class-RP-Persona-Site-Mender.php:143 849 #, php-format 860 850 msgid "Parent page out of synch %s" 861 851 msgstr "Родительская страница не синхронизирована с %s" 862 852 863 #: php/class-RP-Persona-Site-Mender.php:139 864 #: php/class-RP-Persona-Site-Mender.php:200 865 #: php/class-RP-Persona-Site-Mender.php:259 853 #. translators: %s: plugin or context name. 854 #: php/class-RP-Persona-Site-Mender.php:153 855 #: php/class-RP-Persona-Site-Mender.php:217 856 #: php/class-RP-Persona-Site-Mender.php:279 857 #, php-format 866 858 msgid "Issues found with your %s pages." 867 859 msgstr "Проблемы, обнаруженные с вашими страницами %s" 868 860 869 #: php/class-RP-Persona-Site-Mender.php:122 861 #. translators: %s: parent page. 862 #: php/class-RP-Persona-Site-Mender.php:136 863 #, php-format 870 864 msgid "Updated parent page to %s." 871 865 msgstr "Обновляет родительскую страницу для %s." 872 866 873 #: php/class-RP-Persona-Site-Mender.php:118 874 msgid "No reference in rp_source." 875 msgstr "Нет связи в rp_source." 876 877 #: php/class-RP-Persona-Site-Mender.php:114 878 msgid "Deleted orphaned page with no reference in rp_source." 879 msgstr "Удалена потерянная страница без связи в rp_source." 880 881 #: php/class-RP-Persona-Site-Mender.php:47 867 #: php/class-RP-Persona-Site-Mender.php:62 882 868 msgid "Deleted orphaned page with no reference in" 883 869 msgstr "Удаленная потерянная страница с отсутствовавшими связями" 884 870 885 #: php/class-RP-Persona-Installer.php:2 22871 #: php/class-RP-Persona-Installer.php:288 886 872 msgid "The parent page is NOT meant for display, as the menu is very large on most sites." 887 873 msgstr "Родительская страница не создается с целью отображения всей развертки Древа, ввиду того, что на большинстве сайтов меню будет гигантским." 888 874 889 #: php/class-RP-Persona-Installer.php:2 21875 #: php/class-RP-Persona-Installer.php:287 890 876 msgid "will now create and assign a Parent Page by default" 891 877 msgstr "создаст и привяжет Родительскую Страницу по умолчанию" 892 878 893 #: php/class-RP-Persona-Installer.php:218 879 #. translators: %s: plugin name. 880 #: php/class-RP-Persona-Installer.php:284 881 #, php-format 894 882 msgid "then emailed me to say that %s has destroyed their site" 895 883 msgstr "а после этого маякните мне на мейл, что %s \"положила\" ваш сайт" 896 884 897 #: php/class-RP-Persona-Installer.php:2 17885 #: php/class-RP-Persona-Installer.php:281 898 886 msgid "I can not tell you how many new users have skipped the step of assigning the parent page" 899 887 msgstr "Даже не могу сказать сколько новых пользователей пропустили шаг на котором назначается родительская страница" 900 888 901 #: php/class-RP-Persona-Installer.php:2 14889 #: php/class-RP-Persona-Installer.php:278 902 890 msgid "The intent of assigning a Parent Page is to push persona pages down to a second level" 903 891 msgstr "Целью назначения Родительской страницы является перемещение страниц Персон на второй уровень вложенности" 904 892 905 #: php/class-RP-Persona-Installer.php:4 3893 #: php/class-RP-Persona-Installer.php:44 906 894 msgid "Person Index" 907 895 msgstr "Индекс Персон" 908 896 909 #: php/class-RP-Persona-Site-Mender.php: 51897 #: php/class-RP-Persona-Site-Mender.php:66 910 898 msgid "No reference in" 911 899 msgstr "Нет связи" 912 900 913 #: php/pages/class-RP-Option-Page-Builder.php:15 901 #. translators: %s: context (e.g. persona). 902 #: php/pages/class-RP-Option-Page-Builder.php:19 903 #, php-format 914 904 msgid "Accessing outside of a %s page will result in an invalid personId error." 915 905 msgstr "Доступ за пределами страницы %s приведет к ошибке неверного идентифмкатора персоны." 906 907 #. Author of the plugin 908 #: rootspersona.php 909 msgid "Yuriy Fisenko" 910 msgstr "Юрий Фисенко" 911 912 #. Author URI of the plugin 913 #: rootspersona.php 914 msgid "https://urryes.ru/" 915 msgstr "https://urryes.ru/" 916 917 #: admin/class-rootspersona-svg-admin.php:35 918 msgid "SVG Tree Export" 919 msgstr "Экспорт дерева в SVG" 920 921 #: admin/class-rootspersona-svg-admin.php:36 922 msgid "SVG Export" 923 msgstr "Экспорт SVG" 924 925 #: admin/class-rootspersona-svg-admin.php:63 926 msgid "Clearing..." 927 msgstr "Очистка..." 928 929 #: admin/class-rootspersona-svg-admin.php:64 930 msgid "✓ Cache cleared." 931 msgstr "✓ Кэш очищен." 932 933 #: admin/class-rootspersona-svg-admin.php:65 934 msgid "✗ Error clearing cache." 935 msgstr "✗ Ошибка очистки кэша." 936 937 #: admin/class-rootspersona-svg-admin.php:66 938 msgid "✗ Network error." 939 msgstr "✗ Ошибка сети." 940 941 #: admin/class-rootspersona-svg-admin.php:98 942 #: admin/class-rootspersona-svg-settings.php:76 943 msgid "You do not have sufficient permissions." 944 msgstr "У вас недостаточно прав." 945 946 #: admin/class-rootspersona-svg-admin.php:118 947 msgid "Rootspersona SVG Tree Export" 948 msgstr "Экспорт дерева РутсПерсоны в SVG" 949 950 #: admin/class-rootspersona-svg-admin.php:127 951 msgid "Batch" 952 msgstr "Группа (Batch)" 953 954 #: admin/class-rootspersona-svg-admin.php:138 955 msgid "GEDCOM batches — choose the tree you want to export." 956 msgstr "Группы GEDCOM — выберите дерево для экспорта." 957 958 #: admin/class-rootspersona-svg-admin.php:145 959 msgid "Root Person" 960 msgstr "Корневая персона" 961 962 #: admin/class-rootspersona-svg-admin.php:149 963 msgid "— Select a person —" 964 msgstr "— Выберите персону —" 965 966 #. translators: %s: batch id. 967 #: admin/class-rootspersona-svg-admin.php:162 968 #, php-format 969 msgid "No persons found for batch \"%s\". Please upload a GEDCOM first." 970 msgstr "В группе «%s» персоны не найдены. Сначала загрузите GEDCOM-файл." 971 972 #: admin/class-rootspersona-svg-admin.php:172 973 msgid "Options" 974 msgstr "Настройки" 975 976 #: admin/class-rootspersona-svg-admin.php:177 977 msgid "Include photos (fallback to gendered avatar if missing)" 978 msgstr "Включать фотографии (если фото нет, используется аватар по полу)" 979 980 #: admin/class-rootspersona-svg-admin.php:182 981 msgid "Vertical tree (ancestor > person > descendant)" 982 msgstr "Вертикальное дерево (предок > персона > потомок)" 983 984 #: admin/class-rootspersona-svg-admin.php:186 985 msgid "Large trees (>300 people) may take time. Results are cached for 24 hours." 986 msgstr "Большие деревья (более 300 человек) могут строиться долго. Результаты кэшируются на 24 часа." 987 988 #: admin/class-rootspersona-svg-admin.php:193 989 msgid "Generate & Download SVG" 990 msgstr "Создать и скачать SVG" 991 992 #: admin/class-rootspersona-svg-admin.php:198 993 msgid "Preview" 994 msgstr "Предпросмотр" 995 996 #: admin/class-rootspersona-svg-admin.php:200 997 msgid "Select a person above and click \"Generate & Download SVG\" to see the tree. For preview, use the shortcode on a page." 998 msgstr "Выберите персону выше и нажмите «Создать и скачать SVG», чтобы увидеть дерево. Для предпросмотра используйте шоткод на странице." 999 1000 #: admin/class-rootspersona-svg-admin.php:206 1001 msgid "Cache Management" 1002 msgstr "Управление кэшем" 1003 1004 #: admin/class-rootspersona-svg-admin.php:209 1005 msgid "Clear SVG Cache" 1006 msgstr "Очистить кэш SVG" 1007 1008 #: admin/class-rootspersona-svg-admin.php:316 1009 msgid "Access denied. Insufficient permissions." 1010 msgstr "Доступ запрещён. Недостаточно прав." 1011 1012 #: admin/class-rootspersona-svg-admin.php:321 1013 msgid "Access denied. Security check failed." 1014 msgstr "Доступ запрещён. Проверка безопасности не пройдена." 1015 1016 #: admin/class-rootspersona-svg-admin.php:337 1017 msgid "Please select a person." 1018 msgstr "Выберите персону." 1019 1020 #: admin/class-rootspersona-svg-admin.php:343 1021 msgid "SVG generator file not found." 1022 msgstr "Файл генератора SVG не найден." 1023 1024 #: admin/class-rootspersona-svg-admin.php:361 1025 msgid "Failed to generate SVG. Check person ID and batch data." 1026 msgstr "Не удалось создать SVG. Проверьте ID персоны и данные группы." 1027 1028 #: admin/class-rootspersona-svg-admin.php:380 1029 msgid "An error occurred while generating SVG. Please check the error logs." 1030 msgstr "При создании SVG произошла ошибка. Проверьте журналы ошибок." 1031 1032 #: admin/class-rootspersona-svg-admin.php:381 1033 msgid "SVG Generation Error" 1034 msgstr "Ошибка создания SVG" 1035 1036 #: admin/class-rootspersona-svg-admin.php:410 1037 msgid "Failed to create SVG directory." 1038 msgstr "Не удалось создать каталог для SVG." 1039 1040 #: admin/class-rootspersona-svg-admin.php:431 1041 msgid "Failed to write SVG file." 1042 msgstr "Не удалось записать файл SVG." 1043 1044 #: admin/class-rootspersona-svg-admin.php:541 1045 #: rootspersona.php:2021 1046 msgid "Insufficient permissions." 1047 msgstr "Недостаточно прав." 1048 1049 #. translators: %d: number of deleted transients. 1050 #: admin/class-rootspersona-svg-admin.php:553 1051 #, php-format 1052 msgid "%d transient deleted." 1053 msgid_plural "%d transients deleted." 1054 msgstr[0] "Удалена %d временная запись." 1055 msgstr[1] "Удалено %d временные записи." 1056 msgstr[2] "Удалено %d временных записей." 1057 1058 #: admin/class-rootspersona-svg-settings.php:27 1059 msgid "SVG Tree Settings" 1060 msgstr "Настройки SVG-дерева" 1061 1062 #: admin/class-rootspersona-svg-settings.php:28 1063 msgid "SVG Settings" 1064 msgstr "Настройки SVG" 1065 1066 #: admin/class-rootspersona-svg-settings.php:98 1067 msgid "Rootspersona SVG Tree Settings" 1068 msgstr "Настройки SVG-дерева РутсПерсоны" 1069 1070 #: admin/class-rootspersona-svg-settings.php:106 1071 msgid "Male Color" 1072 msgstr "Цвет мужчин" 1073 1074 #: admin/class-rootspersona-svg-settings.php:111 1075 msgid "Background color for male persons" 1076 msgstr "Цвет фона для персон мужского пола" 1077 1078 #: admin/class-rootspersona-svg-settings.php:117 1079 msgid "Female Color" 1080 msgstr "Цвет женщин" 1081 1082 #: admin/class-rootspersona-svg-settings.php:122 1083 msgid "Background color for female persons" 1084 msgstr "Цвет фона для персон женского пола" 1085 1086 #: admin/class-rootspersona-svg-settings.php:128 1087 msgid "Line Color" 1088 msgstr "Цвет линий" 1089 1090 #: admin/class-rootspersona-svg-settings.php:133 1091 msgid "Color for connection lines" 1092 msgstr "Цвет соединительных линий" 1093 1094 #: admin/class-rootspersona-svg-settings.php:139 1095 msgid "Node Width" 1096 msgstr "Ширина блока" 1097 1098 #: admin/class-rootspersona-svg-settings.php:144 1099 msgid "Width of person nodes in pixels" 1100 msgstr "Ширина блока персоны в пикселях" 1101 1102 #: admin/class-rootspersona-svg-settings.php:150 1103 msgid "Node Height" 1104 msgstr "Высота блока" 1105 1106 #: admin/class-rootspersona-svg-settings.php:155 1107 msgid "Height of person nodes in pixels" 1108 msgstr "Высота блока персоны в пикселях" 1109 1110 #: admin/class-rootspersona-svg-settings.php:161 1111 msgid "Generation Spacing" 1112 msgstr "Расстояние между поколениями" 1113 1114 #: admin/class-rootspersona-svg-settings.php:166 1115 msgid "Distance between generations in pixels" 1116 msgstr "Расстояние между поколениями в пикселях" 1117 1118 #: admin/class-rootspersona-svg-settings.php:172 1119 msgid "Sibling Spacing" 1120 msgstr "Расстояние между братьями и сёстрами" 1121 1122 #: admin/class-rootspersona-svg-settings.php:177 1123 msgid "Distance between siblings in pixels" 1124 msgstr "Расстояние между братьями и сёстрами в пикселях" 1125 1126 #: admin/class-rootspersona-svg-settings.php:183 1127 msgid "Spouse Spacing" 1128 msgstr "Расстояние между супругами" 1129 1130 #: admin/class-rootspersona-svg-settings.php:188 1131 msgid "Distance between spouses in pixels" 1132 msgstr "Расстояние между супругами в пикселях" 1133 1134 #: admin/class-rootspersona-svg-settings.php:194 1135 msgid "Font Family" 1136 msgstr "Шрифт" 1137 1138 #: admin/class-rootspersona-svg-settings.php:199 1139 msgid "Font family for text (e.g., Tahoma, Arial, sans-serif)" 1140 msgstr "Шрифт текста (например, Tahoma, Arial, sans-serif)" 1141 1142 #: admin/class-rootspersona-svg-settings.php:205 1143 msgid "Font Size" 1144 msgstr "Размер шрифта" 1145 1146 #: admin/class-rootspersona-svg-settings.php:210 1147 msgid "Font size for person names in pixels" 1148 msgstr "Размер шрифта имён персон в пикселях" 1149 1150 #: admin/class-rootspersona-svg-settings.php:216 1151 msgid "Title Font Size" 1152 msgstr "Размер шрифта заголовка" 1153 1154 #: admin/class-rootspersona-svg-settings.php:221 1155 msgid "Font size for tree title in pixels" 1156 msgstr "Размер шрифта заголовка дерева в пикселях" 1157 1158 #: admin/class-rootspersona-svg-settings.php:227 1159 msgid "Line Width" 1160 msgstr "Толщина линий" 1161 1162 #: admin/class-rootspersona-svg-settings.php:232 1163 msgid "Width of connection lines in pixels" 1164 msgstr "Толщина соединительных линий в пикселях" 1165 1166 #: includes/class-rootspersona-svg-export.php:25 1167 #: includes/class-rootspersona-svg-export.php:87 1168 msgid "Imagick extension is not available." 1169 msgstr "Расширение Imagick недоступно." 1170 1171 #: includes/class-rootspersona-svg-export.php:71 1172 msgid "Failed to export PNG: " 1173 msgstr "Не удалось экспортировать PNG: " 1174 1175 #: includes/class-rootspersona-svg-export.php:142 1176 msgid "Failed to export PDF: " 1177 msgstr "Не удалось экспортировать PDF: " 1178 1179 #: includes/class-rootspersona-svg-shortcode.php:70 1180 msgid "Please specify person_id parameter." 1181 msgstr "Укажите параметр person_id." 1182 1183 #: includes/class-rootspersona-svg-shortcode.php:119 1184 msgid "Loading family tree..." 1185 msgstr "Загрузка родословного дерева..." 1186 1187 #: includes/class-rootspersona-svg-shortcode.php:138 1188 msgid "SVG generator not found." 1189 msgstr "Генератор SVG не найден." 1190 1191 #: includes/class-rootspersona-svg-shortcode.php:147 1192 #: rootspersona.php:2060 1193 msgid "Failed to generate SVG tree." 1194 msgstr "Не удалось создать SVG-дерево." 1195 1196 #: includes/class-rootspersona-svg-shortcode.php:172 1197 #: rootspersona.php:2071 1198 msgid "An error occurred while generating the tree." 1199 msgstr "При построении дерева произошла ошибка." 1200 1201 #: php/class-RP-Gedcom-Import-Session.php:88 1202 msgid "Could not store the uploaded GEDCOM file for processing." 1203 msgstr "Не удалось сохранить загруженный GEDCOM-файл для обработки." 1204 1205 #: php/class-RP-Gedcom-Import-Session.php:130 1206 msgid "Import session expired or not found. Please upload the file again." 1207 msgstr "Сеанс импорта истёк или не найден. Загрузите файл заново." 1208 1209 #: php/class-RP-Gedcom-Import-Session.php:155 1210 msgid "Details were saved to the RootsPersona log: open the Upload GEDCOM File page and use \"Download log\"." 1211 msgstr "Подробности сохранены в журнале РутсПерсоны: откройте страницу «Загрузить GEDCOM-файл» и нажмите «Скачать журнал»." 1212 1213 #: php/class-RP-Log.php:229 1214 msgid "Import log" 1215 msgstr "Журнал импорта" 1216 1217 #: php/class-RP-Log.php:230 1218 msgid "If an import fails, download this file and send it to the plugin developer. It records each step and the exact database error." 1219 msgstr "Если импорт завершился ошибкой, скачайте этот файл и отправьте его разработчику плагина. В нём записан каждый шаг и точная ошибка базы данных." 1220 1221 #: php/class-RP-Log.php:231 1222 msgid "File:" 1223 msgstr "Файл:" 1224 1225 #: php/class-RP-Log.php:234 1226 msgid "Download log" 1227 msgstr "Скачать журнал" 1228 1229 #: php/class-RP-Log.php:235 1230 msgid "Clear log" 1231 msgstr "Очистить журнал" 1232 1233 #: php/class-RP-Log.php:237 1234 msgid "The log is empty — nothing has gone wrong yet." 1235 msgstr "Журнал пуст — ничего плохого пока не случилось." 1236 1237 #: php/class-RP-Log.php:240 1238 msgid "The log may contain short fragments of your family data (names, notes). Send it only to the developer." 1239 msgstr "Журнал может содержать короткие фрагменты данных вашей семьи (имена, заметки). Отправляйте его только разработчику." 1240 1241 #: php/class-RP-Log.php:250 1242 #: php/class-RP-Log.php:275 1243 #: rootspersona.php:512 1244 #: rootspersona.php:644 1245 #: rootspersona.php:805 1246 #: rootspersona.php:831 1247 #: rootspersona.php:914 1248 #: rootspersona.php:970 1249 msgid "You do not have permission to perform this action." 1250 msgstr "У вас нет прав на это действие." 1251 1252 #: php/class-RP-Log.php:255 1253 msgid "The log is empty." 1254 msgstr "Журнал пуст." 1255 1256 #: php/class-RP-Persona-Site-Mender.php:128 1257 msgid "Deleted orphaned page with no reference in rootspersona_source." 1258 msgstr "Удалена потерянная страница без связи в rootspersona_source." 1259 1260 #: php/class-RP-Persona-Site-Mender.php:131 1261 msgid "No reference in rootspersona_source." 1262 msgstr "Нет связи в rootspersona_source." 1263 1264 #. translators: 1: page id, 2: issue description. 1265 #: php/class-RP-Persona-Site-Mender.php:158 1266 #: php/class-RP-Persona-Site-Mender.php:222 1267 #: php/class-RP-Persona-Site-Mender.php:284 1268 #, php-format 1269 msgid "Page %1$s: %2$s" 1270 msgstr "Страница %1$s: %2$s" 1271 1272 #: php/class-RP-Persona-Site-Mender.php:435 1273 #: php/pages/class-RP-Tools-Page-Builder.php:90 1274 #: rootspersona.php:886 1275 msgid "Sync page titles from database" 1276 msgstr "Обновить заголовки страниц из базы данных" 1277 1278 #: php/class-RP-Persona-Site-Mender.php:436 1279 msgid "This updates each persona page title and the expected_name meta field to match the primary name currently stored in the genealogy database (for example after re-uploading a GEDCOM with the same Batch Id). It does not change shortcode attributes (e.g. picfile links)." 1280 msgstr "Обновляет заголовок каждой страницы персоны и мета-поле expected_name в соответствии с основным именем, сохранённым сейчас в генеалогической базе данных (например, после повторной загрузки GEDCOM с тем же Batch Id). Атрибуты шоткода (например, ссылки picfile) не изменяются." 1281 1282 #: php/class-RP-Persona-Site-Mender.php:439 1283 msgid "No persons with linked pages found for this batch." 1284 msgstr "Для этой группы персоны со связанными страницами не найдены." 1285 1286 #: php/class-RP-Persona-Site-Mender.php:440 1287 #: php/class-RP-Persona-Site-Mender.php:489 1288 msgid "Return to Tools" 1289 msgstr "Вернуться в «Инструменты»" 1290 1291 #. translators: 1: page ID, 2: old title, 3: new title. 1292 #: php/class-RP-Persona-Site-Mender.php:474 1293 #, php-format 1294 msgid "Updated page %1$d: \"%2$s\" → \"%3$s\"." 1295 msgstr "Обновлена страница %1$d: «%2$s» → «%3$s»." 1296 1297 #. translators: 1: number of pages updated, 2: number unchanged. 1298 #: php/class-RP-Persona-Site-Mender.php:485 1299 #, php-format 1300 msgid "Finished. Updated: %1$d. Unchanged: %2$d." 1301 msgstr "Готово. Обновлено: %1$d. Без изменений: %2$d." 1302 1303 #: php/dao/sql/class-RP-Connection-Factory.php:19 1304 msgid "WordPress database layer is not available." 1305 msgstr "Слой базы данных WordPress недоступен." 1306 1307 #: php/pages/class-RP-Add-Page-Builder.php:27 1308 msgid "No one is waiting for a new page." 1309 msgstr "Нет персон, ожидающих новой страницы." 1310 1311 #: php/pages/class-RP-Add-Page-Builder.php:28 1312 msgid "This list only shows people without a linked WordPress page. If you re-uploaded a GEDCOM, the database was still updated for the same Batch Id — use Tools → Sync page titles from database to refresh page titles." 1313 msgstr "В этом списке только персоны без связанной страницы WordPress. Если вы повторно загрузили GEDCOM, база данных для того же Batch Id всё равно обновлена — чтобы обновить заголовки страниц, используйте Инструменты → «Обновить заголовки страниц из базы данных»." 1314 1315 #: php/pages/class-RP-Add-Page-Builder.php:29 1316 msgid "Open sync now" 1317 msgstr "Открыть обновление" 1318 1319 #: php/pages/class-RP-Edit-Page-Builder.php:27 1320 msgid "View" 1321 msgstr "Просмотр" 1322 1323 #: php/pages/class-RP-Option-Page-Builder.php:32 1324 #: php/pages/class-RP-Tools-Page-Builder.php:28 1325 msgid "Donate" 1326 msgstr "Поддержать" 1327 1328 #: php/pages/class-RP-Tools-Page-Builder.php:60 1329 msgid "Upload or re-upload a GEDCOM file. Use the same Batch Id as your existing tree so records are merged into the database (duplicates update). After a re-upload, open “Sync page titles from database” if page titles are out of date." 1330 msgstr "Загрузите или повторно загрузите GEDCOM-файл. Используйте тот же Batch Id, что и у существующего дерева, — тогда записи будут объединены в базе данных (дубликаты обновляются). После повторной загрузки откройте «Обновить заголовки страниц из базы данных», если заголовки страниц устарели." 1331 1332 #: php/pages/class-RP-Tools-Page-Builder.php:74 1333 msgid "Lists only people who do not have a WordPress page yet. If everyone already has a page, the list is empty — that is normal. Re-uploading GEDCOM still updates the database; use “Sync page titles from database” to refresh titles." 1334 msgstr "Показывает только персон, у которых ещё нет страницы WordPress. Если у всех уже есть страницы, список пуст — это нормально. Повторная загрузка GEDCOM по-прежнему обновляет базу данных; чтобы обновить заголовки, используйте «Обновить заголовки страниц из базы данных»." 1335 1336 #: php/pages/class-RP-Tools-Page-Builder.php:93 1337 msgid "Run after re-uploading a GEDCOM (same Batch Id): updates each persona page title and expected_name meta from the genealogy database. Choose batch via “Process” if not 1." 1338 msgstr "Запускайте после повторной загрузки GEDCOM (тот же Batch Id): обновляет заголовок каждой страницы персоны и мета-поле expected_name из генеалогической базы данных. Если группа не 1, выберите её через «Process»." 1339 1340 #. translators: 1: context (e.g. persona), 2: shortcode name. Keep {{1}} and {{2}} as-is in translations. 1341 #: php/pages/class-RP-Tools-Page-Builder.php:164 1342 msgid "Identify orphaned {{1}} pages. Includes all pages with {{2}} shortcode and no reference in the database, or reference in the database with no corresponding page." 1343 msgstr "Найти потерянные страницы {{1}}. Включает все страницы с шоткодом {{2}} без связи в базе данных, а также связи в базе данных без соответствующей страницы." 1344 1345 #. translators: Keep {{1}} in translation; it is replaced with plugin name. 1346 #: php/pages/class-RP-Tools-Page-Builder.php:186 1347 msgid "Perform a bulk deletion of all {{1}} and evidence pages. You will be prompted before the data is deleted." 1348 msgstr "Массовое удаление всех страниц {{1}} и страниц данных. Перед удалением будет запрошено подтверждение." 1349 1350 #: php/pages/class-RP-Upload-Page-Builder.php:66 1351 msgid "Step time (sec):" 1352 msgstr "Время шага (сек):" 1353 1354 #: php/pages/class-RP-Upload-Page-Builder.php:72 1355 msgid "The file is processed in short bursts of this many seconds each, so large files no longer time out. Lower it on slower hosting, raise it to finish faster." 1356 msgstr "Файл обрабатывается короткими порциями по указанному числу секунд, поэтому большие файлы больше не вызывают тайм-аут. Уменьшите значение на медленном хостинге, увеличьте — чтобы закончить быстрее." 1357 1358 #: php/pages/class-RP-Upload-Page-Builder.php:140 1359 msgid "Uploading and processing your GEDCOM file in short steps so the page never times out." 1360 msgstr "Загрузка и обработка GEDCOM-файла выполняется короткими шагами, чтобы страница не прерывалась по тайм-ауту." 1361 1362 #: php/pages/class-RP-Upload-Page-Builder.php:145 1363 msgid "Starting…" 1364 msgstr "Запуск…" 1365 1366 #. translators: %d: seconds spent server-side per processing step. 1367 #: php/pages/class-RP-Upload-Page-Builder.php:149 1368 #, php-format 1369 msgid "Step time: %d sec." 1370 msgstr "Время шага: %d сек." 1371 1372 #: php/pages/panels/class-RP-Header-Panel-Creator.php:310 1373 msgid "Caption (native description)" 1374 msgstr "Подпись (нативное описание)" 1375 1376 #: php/pages/panels/class-RP-Header-Panel-Creator.php:313 1377 #: php/pages/panels/class-RP-Picture-Panel-Creator.php:227 1378 msgid "Description" 1379 msgstr "Описание" 1380 1381 #: php/pages/panels/class-RP-Header-Panel-Creator.php:314 1382 #: php/pages/panels/class-RP-Picture-Panel-Creator.php:228 1383 msgid "Caption" 1384 msgstr "Подпись" 1385 1386 #: rootspersona.php:51 1387 msgid "This RootsPersona file was not loaded: another copy of RootsPersona already loaded the same PHP code. Deactivate the other plugin, then enable only one." 1388 msgstr "Этот файл РутсПерсоны не загружен: другая копия РутсПерсоны уже загрузила тот же PHP-код. Отключите другой плагин и оставьте включённым только один." 1389 1390 #: rootspersona.php:77 1391 msgid "This RootsPersona file was not loaded: another copy already loaded the same PHP code. Deactivate the other plugin." 1392 msgstr "Этот файл РутсПерсоны не загружен: другая копия уже загрузила тот же PHP-код. Отключите другой плагин." 1393 1394 #: rootspersona.php:547 1395 #: rootspersona.php:587 1396 #: rootspersona.php:659 1397 #: rootspersona.php:841 1398 #: rootspersona.php:926 1399 #: rootspersona.php:1048 1400 msgid "Security check failed. Please refresh and try again." 1401 msgstr "Проверка безопасности не пройдена. Обновите страницу и повторите попытку." 1402 1403 #. translators: %s: missing personId. 1404 #: rootspersona.php:628 1405 #, php-format 1406 msgid "Missing personId: %s" 1407 msgstr "Отсутствует personId: %s" 1408 1409 #: rootspersona.php:743 1410 msgid "Unknown or unsupported action. Nothing was saved." 1411 msgstr "Неизвестное или неподдерживаемое действие. Ничего не сохранено." 1412 1413 #: rootspersona.php:881 1414 msgid "GEDCOM uploaded: new or changed records were merged into the database for this batch." 1415 msgstr "GEDCOM-файл загружен: новые и изменённые записи объединены с базой данных для этой группы." 1416 1417 #: rootspersona.php:883 1418 msgid "The list below only adds WordPress pages for people who do not have one yet; it may be empty if everyone already has a page." 1419 msgstr "Список ниже добавляет страницы WordPress только для персон, у которых их ещё нет; он может быть пустым, если у всех уже есть страницы." 1420 1421 #: rootspersona.php:885 1422 msgid "To refresh page titles from the updated database, use" 1423 msgstr "Чтобы обновить заголовки страниц из обновлённой базы данных, используйте" 1424 1425 #: rootspersona.php:887 1426 msgid "Tools menu" 1427 msgstr "меню «Инструменты»" 1428 1429 #: rootspersona.php:976 1430 msgid "Security check failed. Please refresh the Tools page and try again." 1431 msgstr "Проверка безопасности не пройдена. Обновите страницу «Инструменты» и повторите попытку." 1432 1433 #. translators: %s: unsupported action name. 1434 #: rootspersona.php:1014 1435 #, php-format 1436 msgid "Action %s not supported." 1437 msgstr "Действие %s не поддерживается." 1438 1439 #: rootspersona.php:2018 1440 msgid "You must be logged in to view this content." 1441 msgstr "Чтобы просмотреть этот материал, нужно войти в систему." 1442 1443 #: rootspersona.php:2048 1444 msgid "Person ID is required." 1445 msgstr "Требуется ID персоны." 1446 1447 #: rootspersona.php:1974 1448 msgid "The RootsPersona SVG tree is disabled (experimental feature). To enable it, set the ROOTSPERSONA_ENABLE_SVG constant to true in wp-config.php." 1449 msgstr "SVG-дерево РутсПерсоны отключено (экспериментальная функция). Чтобы включить его, задайте константе ROOTSPERSONA_ENABLE_SVG значение true в wp-config.php." 1450 1451 #: php/pages/class-RP-Option-Page-Builder.php:142 1452 msgid "Persona page template" 1453 msgstr "Шаблон страницы персоны" 1454 1455 #: php/pages/class-RP-Option-Page-Builder.php:149 1456 msgid "Layout of the persona page. Archive card and Chronicle take their colours and fonts from the theme (--rp-* CSS variables) and hide sections that have no data. A page can override it with the template attribute of the shortcode." 1457 msgstr "Раскладка страницы персоны. «Карточка из архива» и «Летопись» берут цвета и шрифты из темы (CSS-переменные --rp-*) и скрывают разделы, для которых нет данных. Для отдельной страницы шаблон можно задать атрибутом template шорткода." 1458 1459 #: php/templates/class-RP-Persona-Templates.php:29 1460 msgid "Classic (panels)" 1461 msgstr "Классический (панели)" 1462 1463 #: php/templates/class-RP-Persona-Templates.php:30 1464 msgid "Archive card" 1465 msgstr "Карточка из архива" 1466 1467 #: php/templates/class-RP-Persona-Templates.php:31 1468 msgid "Chronicle (line of generations)" 1469 msgstr "Летопись (линия поколений)" 1470 1471 #: php/templates/class-RP-Persona-Templates.php:111 1472 msgid "Information about this person is private." 1473 msgstr "Сведения об этом человеке скрыты." 1474 1475 #: php/templates/class-RP-Persona-Templates.php:167 1476 msgid "Unknown" 1477 msgstr "Неизвестно" 1478 1479 #: php/templates/class-RP-Persona-Templates.php:198 1480 msgid "Wife" 1481 msgstr "Жена" 1482 1483 #: php/templates/class-RP-Persona-Templates.php:201 1484 msgid "Husband" 1485 msgstr "Муж" 1486 1487 #: php/templates/class-RP-Persona-Templates.php:203 1488 #: php/templates/class-RP-Persona-Templates.php:356 1489 msgid "Spouse" 1490 msgstr "Супруг(а)" 1491 1492 #: php/templates/class-RP-Persona-Templates.php:233 1493 msgid "Born" 1494 msgstr "Родился(-ась)" 1495 1496 #: php/templates/class-RP-Persona-Templates.php:241 1497 msgid "Died" 1498 msgstr "Ушёл(-ла) из жизни" 1499 1500 #: php/templates/class-RP-Persona-Templates.php:258 1501 msgid "Occupation" 1502 msgstr "Род занятий" 1503 1504 #: php/templates/class-RP-Persona-Templates.php:261 1505 msgid "Places of residence" 1506 msgstr "Места проживания" 1507 1508 #: php/templates/class-RP-Persona-Templates.php:321 1509 #: php/templates/class-RP-Persona-Templates.php:363 1510 #: php/templates/class-RP-Persona-Templates.php:467 1511 #: php/templates/class-RP-Persona-Templates.php:619 1512 msgid "Children" 1513 msgstr "Дети" 1514 1515 #: php/templates/class-RP-Persona-Templates.php:346 1516 #: php/templates/class-RP-Persona-Templates.php:538 1517 msgid "Parents" 1518 msgstr "Родители" 1519 1520 #. translators: %s: marriage date and/or place. 1521 #: php/templates/class-RP-Persona-Templates.php:671 1522 #, php-format 1523 msgid "Married: %s" 1524 msgstr "Брак: %s" 1525 1526 #: php/templates/class-RP-Persona-Templates.php:356 1527 msgid "Spouses" 1528 msgstr "Супруги" 1529 1530 #: php/templates/class-RP-Persona-Templates.php:370 1531 #: php/templates/class-RP-Persona-Templates.php:570 1532 msgid "Brothers and sisters" 1533 msgstr "Братья и сёстры" 1534 1535 #: php/templates/class-RP-Persona-Templates.php:373 1536 msgid "Family" 1537 msgstr "Семья" 1538 1539 #: php/templates/class-RP-Persona-Templates.php:377 1540 msgid "Place in the family tree" 1541 msgstr "Место в родословной" 1542 1543 #: php/templates/class-RP-Persona-Templates.php:380 1544 #: php/templates/class-RP-Persona-Templates.php:634 1545 msgid "Descendants" 1546 msgstr "Потомки" 1547 1548 #: php/templates/class-RP-Persona-Templates.php:385 1549 #: php/templates/class-RP-Persona-Templates.php:585 1550 msgid "Photos" 1551 msgstr "Фотографии" 1552 1553 #: php/templates/class-RP-Persona-Templates.php:424 1554 #: php/templates/class-RP-Persona-Templates.php:426 1555 #: php/templates/class-RP-Persona-Templates.php:525 1556 msgid "Grandfather" 1557 msgstr "Дедушка" 1558 1559 #: php/templates/class-RP-Persona-Templates.php:425 1560 #: php/templates/class-RP-Persona-Templates.php:427 1561 #: php/templates/class-RP-Persona-Templates.php:525 1562 msgid "Grandmother" 1563 msgstr "Бабушка" 1564 1565 #: php/templates/class-RP-Persona-Templates.php:453 1566 msgid "Child" 1567 msgstr "Ребёнок" 1568 1569 #: php/templates/class-RP-Persona-Templates.php:468 1570 msgid "Grandchildren" 1571 msgstr "Внуки" 1572 1573 #: php/templates/class-RP-Persona-Templates.php:469 1574 msgid "Great-grandchildren" 1575 msgstr "Правнуки" 1576 1577 #: php/templates/class-RP-Persona-Templates.php:529 1578 msgid "Grandparents" 1579 msgstr "Бабушки и дедушки" 1580 1581 #: php/templates/class-RP-Persona-Templates.php:592 1582 msgid "No further details are recorded yet." 1583 msgstr "Других сведений пока нет." 1584 1585 #. translators: %s: name of the other parent. 1586 #: php/templates/class-RP-Persona-Templates.php:613 1587 #, php-format 1588 msgid "With %s" 1589 msgstr "С %s" 1590 1591 #: php/templates/class-RP-Persona-View.php:201 1592 #: rootspersona.php:1148 1593 #: rootspersona.php:1173 1594 msgid "Private" 1595 msgstr "Скрыто" 1596 1597 #. translators: date range of uncertainty, e.g. "between 1852 and 1854". 1598 #: php/class-RP-Date.php:82 1599 #, php-format 1600 msgid "between %1$s and %2$s" 1601 msgstr "между %1$s и %2$s" 1602 1603 #. translators: period, e.g. "from 1846 to 1890". 1604 #: php/class-RP-Date.php:86 1605 #, php-format 1606 msgid "from %1$s to %2$s" 1607 msgstr "с %1$s по %2$s" 1608 1609 #. translators: approximate date, e.g. "about 1890". 1610 #: php/class-RP-Date.php:90 1611 #, php-format 1612 msgid "about %s" 1613 msgstr "около %s" 1614 1615 #. translators: e.g. "before 1900". 1616 #: php/class-RP-Date.php:96 1617 #, php-format 1618 msgid "before %s" 1619 msgstr "до %s" 1620 1621 #. translators: e.g. "after 1900". 1622 #: php/class-RP-Date.php:98 1623 #, php-format 1624 msgid "after %s" 1625 msgstr "после %s" 1626 1627 #: php/templates/class-RP-Persona-View.php:265 1628 #: php/templates/class-RP-Persona-View.php:266 1629 msgid "Burial" 1630 msgstr "Погребение" 1631 1632 #: php/templates/class-RP-Persona-View.php:267 1633 #: php/templates/class-RP-Persona-View.php:268 1634 msgid "Baptism" 1635 msgstr "Крещение" 1636 1637 #: php/templates/class-RP-Persona-View.php:269 1638 msgid "Education" 1639 msgstr "Образование" 1640 1641 #: php/templates/class-RP-Persona-View.php:270 1642 msgid "Adoption" 1643 msgstr "Усыновление" 1644 1645 #: php/templates/class-RP-Persona-View.php:271 1646 msgid "Emigration" 1647 msgstr "Эмиграция" 1648 1649 #: php/templates/class-RP-Persona-View.php:272 1650 msgid "Immigration" 1651 msgstr "Иммиграция" 1652 1653 #: php/templates/class-RP-Persona-View.php:273 1654 msgid "Military service" 1655 msgstr "Военная служба" 1656 1657 #: php/templates/class-RP-Persona-View.php:274 1658 msgid "Religion" 1659 msgstr "Вероисповедание" 1660 1661 #. translators: %d: number of grandchildren. 1662 #: php/templates/class-RP-Persona-Templates.php:628 1663 #, php-format 1664 msgid "%d grandchild" 1665 msgid_plural "%d grandchildren" 1666 msgstr[0] "%d внук" 1667 msgstr[1] "%d внука" 1668 msgstr[2] "%d внуков" 1669 1670 #. translators: %d: number of great-grandchildren. 1671 #: php/templates/class-RP-Persona-Templates.php:632 1672 #, php-format 1673 msgid "%d great-grandchild" 1674 msgid_plural "%d great-grandchildren" 1675 msgstr[0] "%d правнук" 1676 msgstr[1] "%d правнука" 1677 msgstr[2] "%d правнуков" 1678 1679 #: php/class-RP-Persona-Helper.php:784 1680 msgid "« first" 1681 msgstr "« первая" 1682 1683 #: php/class-RP-Persona-Helper.php:785 1684 msgid "‹ prev" 1685 msgstr "‹ назад" 1686 1687 #: php/class-RP-Persona-Helper.php:786 1688 msgid "next ›" 1689 msgstr "вперёд ›" 1690 1691 #: php/class-RP-Persona-Helper.php:787 1692 msgid "last »" 1693 msgstr "последняя »" 1694 1695 #: php/pages/class-RP-Option-Page-Builder.php:89 1696 msgid "People without dates" 1697 msgstr "Люди без дат" 1698 1699 #: php/pages/class-RP-Option-Page-Builder.php:91 1700 msgid "Treat as living" 1701 msgstr "Считать живыми" 1702 1703 #: php/pages/class-RP-Option-Page-Builder.php:92 1704 msgid "Treat as deceased" 1705 msgstr "Считать умершими" 1706 1707 #: php/pages/class-RP-Option-Page-Builder.php:93 1708 msgid "When neither the person nor the parents, spouses or children have dates that settle it. \"Treat as living\" is the safe choice: living relatives are often entered without dates." 1709 msgstr "Когда ни у самого человека, ни у его родителей, супругов и детей нет дат, по которым это можно определить. «Считать живыми» — безопасный выбор: живых родственников часто вносят без дат." 1710 1711 #: php/pages/class-RP-Option-Page-Builder.php:97 1712 msgid "Page for private people" 1713 msgstr "Страница для скрытых персон" 1714 1715 #: php/pages/class-RP-Option-Page-Builder.php:103 1716 msgid "— none: show \"Private\" on the person's page —" 1717 msgstr "— нет: показывать «Скрыто» на странице человека —" 1718 1719 #: php/pages/class-RP-Option-Page-Builder.php:109 1720 msgid "Visitors who may not see a person are sent to this page, for example a page saying \"Closed. Private access.\"" 1721 msgstr "Посетители, которым человек скрыт, будут перенаправлены на эту страницу — например, с текстом «Закрыто. Приватный доступ»." 1722 1723 #. translators: 1: pages processed so far, 2: people selected. 1724 #: rootspersona.php:1476 1725 #, php-format 1726 msgid "Creating pages: %1$s of %2$s…" 1727 msgstr "Создание страниц: %1$s из %2$s…" 1728 1729 #. translators: 1: pages created, 2: errors. 1730 #: rootspersona.php:1478 1731 #, php-format 1732 msgid "Done. Pages created: %1$s, errors: %2$s." 1733 msgstr "Готово. Создано страниц: %1$s, ошибок: %2$s." 1734 1735 #. translators: 1: pages created, 2: people selected. 1736 #: rootspersona.php:1480 1737 #, php-format 1738 msgid "Stopped after %1$s of %2$s pages." 1739 msgstr "Остановлено: создано %1$s из %2$s страниц." 1740 1741 #: rootspersona.php:1481 1742 msgid "The server did not answer. Check the import log." 1743 msgstr "Сервер не ответил. Проверьте журнал импорта." 1744 1745 #: php/templates/class-RP-Persona-Templates.php:228 1746 msgctxt "fact label, female person" 1747 msgid "Born" 1748 msgstr "Родилась" 1749 1750 #: php/templates/class-RP-Persona-Templates.php:231 1751 msgctxt "fact label, male person" 1752 msgid "Born" 1753 msgstr "Родился" 1754 1755 #: php/templates/class-RP-Persona-Templates.php:236 1756 msgctxt "fact label, female person" 1757 msgid "Died" 1758 msgstr "Ушла из жизни" 1759 1760 #: php/templates/class-RP-Persona-Templates.php:239 1761 msgctxt "fact label, male person" 1762 msgid "Died" 1763 msgstr "Ушёл из жизни" 1764 1765 #. translators: date calculated from other data, e.g. "calculated 1890". 1766 #: php/class-RP-Date.php:92 1767 #, php-format 1768 msgid "calculated %s" 1769 msgstr "по расчёту %s" 1770 1771 #. translators: estimated date, e.g. "estimated 1890". 1772 #: php/class-RP-Date.php:94 1773 #, php-format 1774 msgid "estimated %s" 1775 msgstr "предположительно %s" 1776 1777 #. translators: open period start, e.g. "from 1846". 1778 #: php/class-RP-Date.php:100 1779 #, php-format 1780 msgid "from %s" 1781 msgstr "с %s" 1782 1783 #. translators: open period end, e.g. "until 1890". 1784 #: php/class-RP-Date.php:102 1785 #, php-format 1786 msgid "until %s" 1787 msgstr "по %s" 1788 1789 #. translators: "Old Style" — a date in the Julian calendar. 1790 #: php/class-RP-Date.php:142 1791 #: php/class-RP-Date.php:164 1792 msgid "O.S." 1793 msgstr "ст. ст." 1794 1795 #: php/class-RP-Date.php:155 1796 msgid "BC" 1797 msgstr "до н. э." 1798 1799 #: php/pages/panels/class-RP-Descendancy-Panel-Creator.php:52 1800 #: php/pages/panels/class-RP-Descendancy-Panel-Creator.php:98 1801 #: php/pages/panels/class-RP-Header-Panel-Creator.php:194 1802 #: php/pages/panels/class-RP-Header-Panel-Creator.php:252 1803 msgctxt "born: short label before a date" 1804 msgid "b:" 1805 msgstr "р." 1806 1807 #: php/pages/panels/class-RP-Descendancy-Panel-Creator.php:63 1808 #: php/pages/panels/class-RP-Descendancy-Panel-Creator.php:109 1809 #: php/pages/panels/class-RP-Header-Panel-Creator.php:204 1810 #: php/pages/panels/class-RP-Header-Panel-Creator.php:262 1811 msgctxt "died: short label before a date" 1812 msgid "d:" 1813 msgstr "ум." 1814 1815 #. translators: %s: surname or full name taken at marriage. 1816 #: php/class-RP-Persona-Helper.php:393 1817 #, php-format 1818 msgid "married name %s" 1819 msgstr "в браке %s" 1820 1821 #. translators: %s: surname or full name at birth. 1822 #: php/class-RP-Persona-Helper.php:398 1823 #, php-format 1824 msgid "née %s" 1825 msgstr "урожд. %s" 1826 1827 #. translators: %s: another name of the person. 1828 #: php/class-RP-Persona-Helper.php:402 1829 #, php-format 1830 msgid "also known as %s" 1831 msgstr "также %s" 1832 1833 #. translators: %s: nickname. 1834 #: php/class-RP-Persona-Helper.php:408 1835 #, php-format 1836 msgid "nickname “%s”" 1837 msgstr "прозвище «%s»" 1838 1839 #: php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php:195 1840 msgid "Divorce" 1841 msgstr "Развод" 1842 1843 #. translators: %s: divorce date. 1844 #: php/templates/class-RP-Persona-Templates.php:676 1845 #, php-format 1846 msgid "divorced %s" 1847 msgstr "развод %s" 1848 1849 #: php/templates/class-RP-Persona-Templates.php:677 1850 msgid "divorced" 1851 msgstr "в разводе" 1852 1853 #: settings 1854 msgid "Accent (links, markers, years)" 1855 msgstr "Акцент (ссылки, маркеры, годы)" 1856 1857 #: settings 1858 msgid "Text" 1859 msgstr "Текст" 1860 1861 #: settings 1862 msgid "Secondary text" 1863 msgstr "Второстепенный текст" 1864 1865 #: settings 1866 msgid "Card background" 1867 msgstr "Фон карточки" 1868 1869 #: settings 1870 msgid "Section background" 1871 msgstr "Фон раздела" 1872 1873 #: settings 1874 msgid "Lines and borders" 1875 msgstr "Линии и рамки" 1876 1877 #: settings 1878 msgid "Avatar of men" 1879 msgstr "Аватар мужчин" 1880 1881 #: settings 1882 msgid "Avatar of women" 1883 msgstr "Аватар женщин" 1884 1885 #: settings 1886 msgid "Do not set — the theme provides the colours" 1887 msgstr "Не задавать — цвета берёт тема" 1888 1889 #: settings 1890 msgid "Sepia" 1891 msgstr "Сепия" 1892 1893 #: settings 1894 msgid "Neutral" 1895 msgstr "Нейтральный" 1896 1897 #: settings 1898 msgid "Dark" 1899 msgstr "Тёмный" 1900 1901 #: settings 1902 msgid "My own colours" 1903 msgstr "Свои цвета" 1904 1905 #: settings 1906 msgid "Colour set" 1907 msgstr "Набор цветов" 1908 1909 #: settings 1910 msgid "Colours of the Archive card and Chronicle templates. \"Do not set\" leaves them to the theme (recommended when the theme defines --rp-* variables, e.g. with a dark mode). Choosing a set fills the colours below; changing any colour switches to \"My own colours\"." 1911 msgstr "Цвета шаблонов «Карточка из архива» и «Летопись». «Не задавать» оставляет их теме (рекомендуется, если тема сама задаёт переменные --rp-*, например с тёмным режимом). Выбор набора заполняет цвета ниже; изменение любого цвета переключает на «Свои цвета»." 1912 1913 #: settings 1914 msgid "Colours" 1915 msgstr "Цвета" 1916 1917 #: settings 1918 msgid "Preview (without the theme's own colours)" 1919 msgstr "Предпросмотр (без собственных цветов темы)" 1920 1921 #: settings 1922 msgid "Peter Smith" 1923 msgstr "Пётр Смирнов" 1924 1925 #: settings 1926 msgid "Mary Smith" 1927 msgstr "Мария Смирнова" 1928 1929 #: settings 1930 msgid "The colours below apply to the Classic template only." 1931 msgstr "Цвета ниже действуют только для классического шаблона." 1932 1933 #: settings 1934 msgid "Choose from Media Library" 1935 msgstr "Выбрать из медиатеки" 1936 1937 #: settings 1938 msgid "Remove" 1939 msgstr "Убрать" 1940 1941 #: settings 1942 msgid "Create Page or Post" 1943 msgstr "Создавать страницу или запись" 1944 1945 #: settings 1946 msgid "Post" 1947 msgstr "Запись" 1948 1949 #: settings 1950 msgid "Rootspersona can create a Page or Post" 1951 msgstr "Rootspersona может создавать страницу или запись" 1952 1953 #: settings 1954 msgid "Choose image" 1955 msgstr "Выбор изображения" 1956 1957 #: settings 1958 msgid "Use this image" 1959 msgstr "Использовать это изображение" 1960 1961 #: settings 1962 msgid "Privacy" 1963 msgstr "Приватность" 1964 1965 #: settings 1966 msgid "Persona page" 1967 msgstr "Страница персоны" 1968 1969 #: settings 1970 msgid "Pages and other" 1971 msgstr "Страницы и прочее" 1972 1973 #: settings 1974 msgid "Settings sections" 1975 msgstr "Разделы настроек" 1976 1977 #: settings 1978 msgid "Version %s" 1979 msgstr "Версия %s" 1980 1981 #: settings 1982 msgid "A fan of RootsPersona? Consider making a donation!" 1983 msgstr "Нравится RootsPersona? Поддержите разработку!" 1984 1985 #: settings 1986 msgid "Exclude from site" 1987 msgstr "Исключить с сайта" 1988 1989 #: settings 1990 msgid "Dates and places" 1991 msgstr "Даты и места" 1992 1993 #: settings 1994 msgid "Show dates" 1995 msgstr "Показывать даты" 1996 1997 #: settings 1998 msgid "Show places" 1999 msgstr "Показывать места" 2000 2001 #: settings 2002 msgid "Untick to hide them on all persona pages, for example for privacy." 2003 msgstr "Снимите галочку, чтобы скрыть их на всех страницах персон, например ради приватности." 2004 2005 #: settings 2006 msgid "Show on the page" 2007 msgstr "Показывать на странице" 2008 2009 #: settings 2010 msgid "the classic header keeps its picture" 2011 msgstr "в классической шапке фото остаётся" 2012 2013 #: settings 2014 msgid "Classic page panels" 2015 msgstr "Панели классической страницы" 2016 2017 #: settings 2018 msgid "Header" 2019 msgstr "Шапка" 2020 2021 #: settings 2022 msgid "Family where the person is a child" 2023 msgstr "Семья, где человек — ребёнок" 2024 2025 #: settings 2026 msgid "Families where the person is a spouse" 2027 msgstr "Семьи, где человек — супруг(а)" 2028 2029 #: settings 2030 msgid "Evidence (sources)" 2031 msgstr "Свидетельства (источники)" 2032 2033 #: settings 2034 msgid "Silhouettes in place of missing photos" 2035 msgstr "Силуэты вместо отсутствующих фото" 2036 2037 #: settings 2038 msgid "Edit links at the bottom of the page" 2039 msgstr "Ссылки «Редактировать» внизу страницы" 2040 2041 #: settings 2042 msgid "These panels exist only in the Classic template." 2043 msgstr "Эти панели есть только в классическом шаблоне." 2044 2045 #: settings 2046 msgid "Parent page" 2047 msgstr "Родительская страница" 2048 2049 #: settings 2050 msgid "— none —" 2051 msgstr "— нет —" 2052 2053 #: settings 2054 msgid "View page" 2055 msgstr "Открыть страницу" 2056 2057 #: settings 2058 msgid "New persona pages are created as sub-pages of this page. They are kept out of menus and page lists." 2059 msgstr "Новые страницы персон создаются как дочерние этой страницы. В меню и списки страниц они не попадают." 2060 2061 #: settings 2062 msgid "Editing on the site" 2063 msgstr "Редактирование на сайте" 2064 2065 #: settings 2066 msgid "Data is entered on this website (system of record)" 2067 msgstr "Данные вводятся на этом сайте (основная база)" 2068 2069 #: settings 2070 msgid "Donation button" 2071 msgstr "Кнопка пожертвования" 2072 2073 #: settings 2074 msgid "Show the PayPal donation button on this page" 2075 msgstr "Показывать кнопку пожертвования PayPal на этой странице" -
rootspersona/trunk/php/Genealogy/Gedcom/Structures/class-RP-Fact.php
r3572543 r3749166 29 29 class Rootspersona_Fact extends Rootspersona_Fact_Detail 30 30 { 31 var $_TYPES = array('CAST' => 'Caste', 'EDUC' => 'Education', 'NATI' => 'Nationality', 'OCCU' => 'Occupation', 'PROP' => 'Possessions', 'RELI' => 'Religion', 'RESI' => 'Residence', 'TITL' => 'Nobility Title', 'SSN' => 'Social Security Nbr', 'FACT' => 'Fact'); 31 // RESI is parsed as an event (Rootspersona_Event): listing it here too stored every residence twice. 32 var $_TYPES = array('CAST' => 'Caste', 'EDUC' => 'Education', 'NATI' => 'Nationality', 'OCCU' => 'Occupation', 'PROP' => 'Possessions', 'RELI' => 'Religion', 'TITL' => 'Nobility Title', 'SSN' => 'Social Security Nbr', 'FACT' => 'Fact'); 32 33 33 34 /** -
rootspersona/trunk/php/class-RP-Gedcom-Loader.php
r3730341 r3749166 239 239 $event = new Rootspersona_Event_Detail(); 240 240 $event->event_type = 241 substr(( $p_event->tag === 'EVEN' ? $p_event->type : $p_event->_TYPES[$p_event->tag]),0,22);242 $event->classification = substr( $p_event->descr,0,89);241 substr((string) ($p_event->tag === 'EVEN' ? $p_event->type : $p_event->_TYPES[$p_event->tag]),0,22); 242 $event->classification = substr((string) $p_event->descr,0,89); 243 243 $event->event_date = $p_event->date; 244 244 $event->place = $p_event->place->name; … … 279 279 $cite->source_batch_id = $batch_id; 280 280 $cite->source_page = $citation->page; 281 $cite->event_type = substr( $citation->event_type,0,22);281 $cite->event_type = substr((string) $citation->event_type,0,22); 282 282 $cite->event_role = $citation->role_in_event; 283 283 $cite->quay = $citation->quay; … … 369 369 foreach ($person->names as $p_name) { 370 370 $name = new Rootspersona_Name_Personal(); 371 $name->personal_name = substr( $p_name->get_name(),0,119);371 $name->personal_name = substr((string) $p_name->get_name(),0,119); 372 372 $name->name_type = $p_name->rp_name->type; 373 373 $name->prefix = $p_name->rp_name->pieces->prefix; … … 427 427 $event = new Rootspersona_Event_Detail(); 428 428 $event->event_type = 429 substr(( $p_event->tag === 'EVEN' ? $p_event->type : $p_event->_TYPES[$p_event->tag]),0,22);430 $event->classification = substr( $p_event->descr,0,89);429 substr((string) ($p_event->tag === 'EVEN' ? $p_event->type : $p_event->_TYPES[$p_event->tag]),0,22); 430 $event->classification = substr((string) $p_event->descr,0,89); 431 431 $event->event_date = $p_event->date; 432 432 $event->place = $p_event->place->name; … … 457 457 $event = new Rootspersona_Event_Detail(); 458 458 $event->event_type = 459 substr(( $p_event->tag === 'FACT' ? $p_event->type : $p_event->_TYPES[$p_event->tag]),0,22);460 $event->classification = substr( $p_event->descr,0,89);459 substr((string) ($p_event->tag === 'FACT' ? $p_event->type : $p_event->_TYPES[$p_event->tag]),0,22); 460 $event->classification = substr((string) $p_event->descr,0,89); 461 461 $event->event_date = $p_event->date; 462 462 $event->place = $p_event->place->name; -
rootspersona/trunk/php/class-RP-Log.php
r3730341 r3749166 289 289 $dir = self::dir(); 290 290 if ( is_dir( $dir ) ) { 291 $files = glob( $dir . '/{,.}*', GLOB_BRACE ); 292 if ( is_array( $files ) ) { 293 foreach ( $files as $file ) { 294 if ( is_file( $file ) ) { 295 wp_delete_file( $file ); 296 } 297 } 291 global $wp_filesystem; 292 if ( empty( $wp_filesystem ) ) { 293 require_once ABSPATH . 'wp-admin/includes/file.php'; 294 WP_Filesystem(); 298 295 } 299 // phpcs:ignore WordPress.WP.AlternativeFunctions.rmdir_rmdir -- removing the plugin's own empty log directory. 300 rmdir( $dir ); 296 if ( ! empty( $wp_filesystem ) ) { 297 $wp_filesystem->delete( $dir, true ); 298 } 301 299 } 302 300 delete_option( self::KEY_OPTION ); -
rootspersona/trunk/php/class-RP-Persona-Factory.php
r3572543 r3749166 65 65 } 66 66 67 // Married name, birth name, also known as. 68 $persona->other_names = Rootspersona_Dao_Factory::get_rp_persona_dao() 69 ->get_other_names($id, $batch_id); 70 67 71 // do I need fact data? 68 72 if ($options['hide_facts'] == 0) { -
rootspersona/trunk/php/class-RP-Persona-Helper.php
r3577245 r3749166 109 109 public static function is_living($persona) 110 110 { 111 $is_living = false;112 111 if (isset($persona->is_living)) { 113 $is_living = $persona->is_living; 112 return $persona->is_living; 113 } 114 if (!empty($persona->death_date)) { 115 return false; 116 } 117 $birth_year = self::year_of(isset($persona->birth_date) ? $persona->birth_date : ''); 118 if ($birth_year) { 119 if ($birth_year + self::MAX_AGE < (int) gmdate('Y')) { 120 return false; 121 } 122 // Young enough to be alive, unless a burial or similar says otherwise. 123 return self::family_says_living($persona, true); 124 } 125 // No birth year: the person's other events and close family decide. 126 return self::family_says_living($persona, false); 127 } 128 129 /** People older than this are taken as deceased (years). */ 130 const MAX_AGE = 110; 131 132 /** 133 * Last four-digit year in a GEDCOM date ("ABT 1890", "BET 1852 AND 1854" -> 1854). 134 * 135 * @param string $date 136 * @return int 0 when there is no year. 137 */ 138 public static function year_of($date) 139 { 140 if (!is_string($date) || $date === '') { 141 return 0; 142 } 143 $cnt = preg_match_all('/([1-2][0-9][0-9][0-9])/', $date, $matches); 144 return $cnt > 0 ? (int) $matches[1][$cnt - 1] : 0; 145 } 146 147 /** 148 * Living status from the person's own events and close family, for people 149 * without a birth year (or to catch a burial recorded without a death). 150 * 151 * Deceased when: an own death/burial/cremation/probate event; an own event more 152 * than MAX_AGE years ago; a marriage or a child's birth more than MAX_AGE-15 153 * years ago; a spouse born more than MAX_AGE+20 years ago; a parent born more 154 * than MAX_AGE+50 years ago. Living when recent dates point that way. With no 155 * clue at all, the "People without dates" setting decides (living by default). 156 * 157 * @param Rootspersona_Persona $persona 158 * @param bool $has_birth_year Birth year already says "alive". 159 * @return bool 160 */ 161 protected static function family_says_living($persona, $has_birth_year) 162 { 163 static $cache = array(); 164 165 if (empty($persona->id) || $persona->id == '0') { 166 return $has_birth_year || self::unknown_dates_are_living(); 167 } 168 $batch_id = isset($persona->batch_id) && $persona->batch_id !== '' ? $persona->batch_id : '1'; 169 $key = $batch_id . '|' . $persona->id; 170 if (isset($cache[$key])) { 171 return $cache[$key]; 172 } 173 174 $now = (int) gmdate('Y'); 175 $dead = false; 176 $alive = false; 177 $rows = Rootspersona_Dao_Factory::get_rp_persona_dao()->get_living_clues($persona->id, $batch_id); 178 foreach ($rows as $row) { 179 $type = strtoupper((string) $row['event_type']); 180 $year = self::year_of((string) $row['event_date']); 181 $died = (bool) preg_match('/^(DEAT|BURI|CREM|PROB)/', $type); 182 $born = strpos($type, 'BIRT') === 0; 183 switch ($row['rel']) { 184 case 'self': 185 if ($died) { 186 $dead = true; 187 } elseif ($year && $year < $now - self::MAX_AGE) { 188 $dead = true; 189 } elseif ($year && $year >= $now - 90) { 190 $alive = true; 191 } 192 break; 193 case 'fam': 194 if ($year && $year < $now - self::MAX_AGE + 15) { 195 $dead = true; 196 } elseif ($year && $year >= $now - 60) { 197 $alive = true; 198 } 199 break; 200 case 'child': 201 if ($born && $year && $year < $now - self::MAX_AGE + 15) { 202 $dead = true; 203 } elseif ($born && $year && $year >= $now - 60) { 204 $alive = true; 205 } 206 break; 207 case 'spouse': 208 if ($born && $year && $year < $now - self::MAX_AGE - 20) { 209 $dead = true; 210 } elseif ($born && $year && $year >= $now - 80) { 211 $alive = true; 212 } 213 break; 214 case 'parent': 215 if ($born && $year && $year < $now - self::MAX_AGE - 50) { 216 $dead = true; 217 } elseif ($born && $year && $year >= $now - 80) { 218 $alive = true; 219 } 220 break; 221 } 222 if ($dead) { 223 break; 224 } 225 } 226 227 if ($has_birth_year) { 228 // Only an explicit death-type event overrides a young birth year. 229 $living = true; 230 foreach ($rows as $row) { 231 if ($row['rel'] === 'self' && preg_match('/^(DEAT|BURI|CREM|PROB)/', strtoupper((string) $row['event_type']))) { 232 $living = false; 233 break; 234 } 235 } 236 } elseif ($dead) { 237 $living = false; 238 } elseif ($alive) { 239 $living = true; 114 240 } else { 115 if (!isset($persona->death_date) 116 || empty($persona->death_date) 117 ) { 118 if (isset($persona->birth_date) 119 && !empty($persona->birth_date) 120 ) { 121 $matches = array(); 122 $cnt = preg_match_all('/.*?([1-2][0-9][0-9][0-9]).*?/Us', $persona->birth_date, $matches); // Modified by Horacio (lower case s) 123 if ($cnt > 0) { 124 $birth_year = $matches[1][$cnt - 1]; 125 $test_year = ((int)$birth_year) + 110; 126 $curr_year = gmdate( 'Y' ); 127 if ($test_year >= $curr_year) { 128 $is_living = true; 129 } 130 } 131 } 132 } 133 } 134 return $is_living; 241 $living = self::unknown_dates_are_living(); 242 } 243 244 $cache[$key] = $living; 245 return $living; 246 } 247 248 /** 249 * "People without dates" setting: true = treat as living (default, safer for privacy). 250 * 251 * @return bool 252 */ 253 public static function unknown_dates_are_living() 254 { 255 $options = get_option('rootspersona_plugin'); 256 return !(is_array($options) && isset($options['living_unknown']) && $options['living_unknown'] === 'dead'); 135 257 } 136 258 … … 199 321 } 200 322 return ''; 323 } 324 325 /** 326 * Whether the current visitor may NOT see the person embedded in this persona page. 327 * 328 * Uses the same scoring as the persona panels, so a page shown as "Private" 329 * also gets a private title (page title, document title, menus, lists). 330 * 331 * @param WP_Post|int $post Post object or ID. 332 * @return bool 333 */ 334 public static function is_persona_post_restricted( $post ) { 335 static $cache = array(); 336 337 $post = get_post( $post ); 338 if ( ! $post || ! is_string( $post->post_content ) || stripos( $post->post_content, '[rootsPersona' ) === false ) { 339 return false; 340 } 341 if ( isset( $cache[ $post->ID ] ) ) { 342 return $cache[ $post->ID ]; 343 } 344 345 $restricted = false; 346 if ( preg_match( '/\[rootsPersona\s[^\]]*personId\s*=\s*["\']([^"\']+)["\'][^\]]*\]/i', $post->post_content, $m ) ) { 347 $batch_id = '1'; 348 if ( preg_match( '/batchId\s*=\s*["\']\D*(\d+)/i', $m[0], $bm ) ) { 349 $batch_id = $bm[1]; 350 } 351 $persona = Rootspersona_Dao_Factory::get_rp_persona_dao()->get_persona( $m[1], $batch_id ); 352 if ( $persona && isset( $persona->id ) && $persona->id != '0' ) { 353 $options = get_option( 'rootspersona_plugin' ); 354 $options = is_array( $options ) ? $options : array(); 355 $options += array( 356 'privacy_living' => false, 357 'privacy_default' => false, 358 ); 359 $pscore = isset( $persona->pscore ) ? $persona->pscore : self::score_persona( $persona, $options ); 360 $restricted = self::is_restricted( self::score_user(), $pscore ); 361 } 362 } 363 364 $cache[ $post->ID ] = $restricted; 365 return $restricted; 366 } 367 368 /** 369 * Other names of a person as short phrases: "née Sokolova", "married name Petrova", 370 * "also known as ...", "nickname «Petrusha»". Shared by the classic header and templates. 371 * 372 * @param Rootspersona_Persona $persona 373 * @return string[] Plain text. 374 */ 375 public static function other_names_text($persona) 376 { 377 $out = array(); 378 $given = isset($persona->given) ? trim((string) $persona->given) : ''; 379 $names = isset($persona->other_names) && is_array($persona->other_names) ? $persona->other_names : array(); 380 foreach ($names as $n) { 381 $n_given = trim((string) $n['given']); 382 $n_surname = trim((string) $n['surname']); 383 // Same given name: the surname alone says it ("née Sokolova"). 384 $text = ($n_given === $given && $n_surname !== '') 385 ? $n_surname 386 : trim(preg_replace('/\s+/', ' ', str_replace('/', ' ', (string) $n['personal_name']))); 387 if ($text === '') { 388 continue; 389 } 390 switch (strtolower(trim((string) $n['name_type']))) { 391 case 'married': 392 /* translators: %s: surname or full name taken at marriage. */ 393 $out[] = Rootspersona_I18n::sprintf(__('married name %s', 'rootspersona'), $text); 394 break; 395 case 'birth': 396 case 'maiden': 397 /* translators: %s: surname or full name at birth. */ 398 $out[] = Rootspersona_I18n::sprintf(__('née %s', 'rootspersona'), $text); 399 break; 400 default: 401 /* translators: %s: another name of the person. */ 402 $out[] = Rootspersona_I18n::sprintf(__('also known as %s', 'rootspersona'), $text); 403 } 404 } 405 $nick = isset($persona->nickname) ? trim((string) $persona->nickname) : ''; 406 if ($nick !== '') { 407 /* translators: %s: nickname. */ 408 $out[] = Rootspersona_I18n::sprintf(__('nickname “%s”', 'rootspersona'), $nick); 409 } 410 return $out; 411 } 412 413 /** Transient with the IDs of persona pages an anonymous visitor may not see. */ 414 const GUEST_PRIVATE_PAGES = 'rootspersona_guest_private_pages'; 415 416 /** 417 * IDs of persona pages an anonymous visitor may not see (living or private people). 418 * Used to keep those pages, and the names in their URLs, out of XML sitemaps. 419 * Cached for a few hours; cleared when pages are created or settings change. 420 * 421 * @return int[] 422 */ 423 public static function get_guest_private_page_ids() 424 { 425 $ids = get_transient(self::GUEST_PRIVATE_PAGES); 426 if (is_array($ids)) { 427 return $ids; 428 } 429 430 $options = get_option('rootspersona_plugin'); 431 $options = is_array($options) ? $options : array(); 432 $options += array( 433 'privacy_living' => false, 434 'privacy_default' => false, 435 ); 436 $ids = array(); 437 try { 438 foreach (Rootspersona_Dao_Factory::get_rp_persona_dao()->get_paged_personas_for_privacy() as $persona) { 439 $restricted = $persona->privacy === self::EXC 440 || self::is_restricted(self::GUEST, self::score_persona($persona, $options)); 441 if ($restricted && (int) $persona->page > 0) { 442 $ids[] = (int) $persona->page; 443 } 444 } 445 } catch (Exception $e) { 446 return array(); 447 } 448 set_transient(self::GUEST_PRIVATE_PAGES, $ids, 6 * HOUR_IN_SECONDS); 449 return $ids; 201 450 } 202 451 … … 457 706 && !empty($options['banner_fcolor']) 458 707 ) { 459 $ban_style = ' style="color:' . $options['banner_fcolor']708 $ban_style = ' style="color:' . esc_attr( $options['banner_fcolor'] ) 460 709 . ';background-image:none;background-color:' 461 . $options['banner_bcolor']. ';" ';710 . esc_attr( $options['banner_bcolor'] ) . ';" '; 462 711 } else { 463 712 $ban_style = ' style="background-image:none;background-color:' 464 . $options['banner_bcolor']. ';" ';713 . esc_attr( $options['banner_bcolor'] ) . ';" '; 465 714 } 466 715 } else if (isset($options['banner_image']) … … 470 719 && !empty($options['banner_fcolor']) 471 720 ) { 472 $ban_style = ' style="color:' . $options['banner_fcolor']473 . ';background-image:url(\'' . $options['banner_image']721 $ban_style = ' style="color:' . esc_attr( $options['banner_fcolor'] ) 722 . ';background-image:url(\'' . esc_url( $options['banner_image'] ) 474 723 . '\');" '; 475 724 } else { 476 725 $ban_style = ' style="background-image:none;background-color:' 477 . $options['banner_bcolor']. ';" ';726 . esc_attr( $options['banner_bcolor'] ) . ';" '; 478 727 } 479 728 } elseif (isset($options['banner_fcolor']) 480 729 && !empty($options['banner_fcolor']) 481 730 ) { 482 $ban_style = ' style="color:' . $options['banner_fcolor']. ';" ';731 $ban_style = ' style="color:' . esc_attr( $options['banner_fcolor'] ) . ';" '; 483 732 } 484 733 … … 515 764 } 516 765 766 // Link to the page's permalink (not "?page_id=", which redirects and may drop the page 767 // number) and build every URL with add_query_arg() so it is always well formed. 768 $base = (string) $targetpage; 769 if ( preg_match( '/[?&]page_id=(\d+)/', $base, $m ) ) { 770 $permalink = get_permalink( (int) $m[1] ); 771 if ( $permalink ) { 772 $base = $permalink; 773 } 774 } 775 $base = remove_query_arg( 'rootsvar', $base ); 776 $link = function ( $page, $label ) use ( $base ) { 777 $url = ( $page > 1 ) ? add_query_arg( 'rootsvar', (int) $page, $base ) : $base; 778 return "<a href='" . esc_url( $url ) . "'>" . esc_html( $label ) . '</a>'; 779 }; 780 $span = function ( $class, $label ) { 781 return "<span class='" . esc_attr( $class ) . "'>" . esc_html( $label ) . '</span>'; 782 }; 783 784 $first_label = __( '« first', 'rootspersona' ); 785 $prev_label = __( '‹ prev', 'rootspersona' ); 786 $next_label = __( 'next ›', 'rootspersona' ); 787 $last_label = __( 'last »', 'rootspersona' ); 788 517 789 $pagination = "<div class='pagination'>"; 518 519 if ($curr_page != 1) { 520 $pagination .= "<a href='" 521 . $targetpage . "page=1'><< first</a>"; 522 } else { 523 $pagination .= "<span class='disabled'><< first</span>"; 524 } 525 526 if ($curr_page > 1) { 527 $pagination .= "<a href='" 528 . $targetpage . "&rootsvar=$prev'>< prev</a>"; 529 } else { 530 $pagination .= "<span class='disabled'>< prev</span>"; 531 } 532 533 for ($p = $start; $p <= $finish; $p++) { 534 if ($p == $curr_page) { 535 $pagination .= "<span class='current'>$p</span>"; 536 } else { 537 $pagination .= "<a href='" . $targetpage 538 . "&rootsvar=" . $p . "'>" . $p . "</a>"; 539 } 540 } 541 if ($curr_page != $finish) { 542 $pagination .= "<a href='" . $targetpage 543 . "&rootsvar=" . $next . "'>next ></a>"; 544 } else { 545 $pagination .= "<span class='disabled'>next ></span>"; 546 } 547 if ($curr_page != $lastpage) { 548 $pagination .= "<a href='" . $targetpage 549 . "&rootsvar=" . $lastpage . "'>last >></a>"; 550 } else { 551 $pagination .= "<span class='disabled'>last >></span>"; 552 } 790 $pagination .= ( $curr_page > 1 ) ? $link( 1, $first_label ) : $span( 'disabled', $first_label ); 791 $pagination .= ( $curr_page > 1 ) ? $link( $prev, $prev_label ) : $span( 'disabled', $prev_label ); 792 for ( $p = $start; $p <= $finish; $p++ ) { 793 $pagination .= ( $p == $curr_page ) ? $span( 'current', (string) $p ) : $link( $p, (string) $p ); 794 } 795 $pagination .= ( $curr_page < $lastpage ) ? $link( $next, $next_label ) : $span( 'disabled', $next_label ); 796 $pagination .= ( $curr_page < $lastpage ) ? $link( $lastpage, $last_label ) : $span( 'disabled', $last_label ); 553 797 $pagination .= "</div>"; 554 798 return $pagination; -
rootspersona/trunk/php/dao/mysql/class-RP-Persona-Mysql-DAO.php
r3577245 r3749166 275 275 $sql_query->set($privacy); 276 276 $sql_query->set($privacy == 'Exc' ? $name : ''); 277 return $this->execute_update($sql_query); 277 $result = $this->execute_update($sql_query); 278 delete_transient(Rootspersona_Persona_Helper::GUEST_PRIVATE_PAGES); 279 return $result; 278 280 } 279 281 … … 538 540 $sql_query->set_number($batch_id); 539 541 $rows = Rootspersona_Query_Executor::execute($sql_query); 542 543 // Years of birth and death for the whole batch in one query, so people with 544 // the same name can be told apart in the list. 545 $sql = "SELECT rie.indi_id AS id, red.event_type AS event_type, red.event_date AS event_date" 546 . " FROM rootspersona_indi_event rie" 547 . " JOIN rootspersona_event_detail red ON red.id = rie.event_id" 548 . " WHERE rie.indi_batch_id = ?" 549 . " AND (UPPER(red.event_type) LIKE 'BIRT%' OR UPPER(red.event_type) LIKE 'DEAT%')"; 550 $years_query = new Rootspersona_Sql_Query($sql); 551 $years_query->set_number($batch_id); 552 $years = array(); 553 foreach (Rootspersona_Query_Executor::execute($years_query) as $y) { 554 $kind = strpos(strtoupper($y['event_type']), 'DEAT') === 0 ? 'death_year' : 'birth_year'; 555 $year = Rootspersona_Persona_Helper::year_of((string) $y['event_date']); 556 if ($year && empty($years[$y['id']][$kind])) { 557 $years[$y['id']][$kind] = $year; 558 } 559 } 560 540 561 $persons = array(); 541 562 if ($rows > 0) { … … 549 570 $person['given'] = $rows[$idx]['given']; 550 571 $person['surname'] = $rows[$idx]['surname']; 572 $person['birth_year'] = isset($years[$person['id']]['birth_year']) ? $years[$person['id']]['birth_year'] : 0; 573 $person['death_year'] = isset($years[$person['id']]['death_year']) ? $years[$person['id']]['death_year'] : 0; 551 574 $persons[$idx] = $person; 552 575 } … … 671 694 672 695 /** 696 * Dated events that tell whether a person without own dates can still be alive: 697 * the person's own events, events of the families they head (marriage...), 698 * and the events of spouses, children and parents. 699 * 700 * @param string $id 701 * @param int|string $batch_id 702 * @return array[] rows: [rel (self|fam|spouse|child|parent), event_type, event_date] 703 */ 704 public function get_living_clues($id, $batch_id) 705 { 706 $sql = "SELECT 'self' AS rel, red.event_type AS event_type, red.event_date AS event_date" 707 . " FROM rootspersona_indi_event rie" 708 . " JOIN rootspersona_event_detail red ON red.id = rie.event_id" 709 . " WHERE rie.indi_id = ? AND rie.indi_batch_id = ?" 710 . " UNION ALL" 711 . " SELECT 'fam', red.event_type, red.event_date" 712 . " FROM rootspersona_fam f" 713 . " JOIN rootspersona_fam_event fe ON fe.fam_id = f.id AND fe.fam_batch_id = f.batch_id" 714 . " JOIN rootspersona_event_detail red ON red.id = fe.event_id" 715 . " WHERE f.batch_id = ? AND (f.spouse1 = ? OR f.spouse2 = ?)" 716 . " UNION ALL" 717 . " SELECT 'spouse', red.event_type, red.event_date" 718 . " FROM rootspersona_fam f" 719 . " JOIN rootspersona_indi_event rie ON rie.indi_batch_id = f.batch_id" 720 . " AND rie.indi_id = CASE WHEN f.spouse1 = ? THEN f.spouse2 ELSE f.spouse1 END" 721 . " JOIN rootspersona_event_detail red ON red.id = rie.event_id" 722 . " WHERE f.batch_id = ? AND (f.spouse1 = ? OR f.spouse2 = ?)" 723 . " UNION ALL" 724 . " SELECT 'child', red.event_type, red.event_date" 725 . " FROM rootspersona_fam f" 726 . " JOIN rootspersona_fam_child fc ON fc.fam_id = f.id AND fc.fam_batch_id = f.batch_id" 727 . " JOIN rootspersona_indi_event rie ON rie.indi_id = fc.child_id AND rie.indi_batch_id = fc.indi_batch_id" 728 . " JOIN rootspersona_event_detail red ON red.id = rie.event_id" 729 . " WHERE f.batch_id = ? AND (f.spouse1 = ? OR f.spouse2 = ?)" 730 . " UNION ALL" 731 . " SELECT 'parent', red.event_type, red.event_date" 732 . " FROM rootspersona_fam_child fc" 733 . " JOIN rootspersona_fam f ON f.id = fc.fam_id AND f.batch_id = fc.fam_batch_id" 734 . " JOIN rootspersona_indi_event rie ON rie.indi_batch_id = f.batch_id" 735 . " AND (rie.indi_id = f.spouse1 OR rie.indi_id = f.spouse2)" 736 . " JOIN rootspersona_event_detail red ON red.id = rie.event_id" 737 . " WHERE fc.child_id = ? AND fc.indi_batch_id = ?"; 738 $sql_query = new Rootspersona_Sql_Query($sql); 739 // self 740 $sql_query->set($id); 741 $sql_query->set_number($batch_id); 742 // fam 743 $sql_query->set_number($batch_id); 744 $sql_query->set($id); 745 $sql_query->set($id); 746 // spouse 747 $sql_query->set($id); 748 $sql_query->set_number($batch_id); 749 $sql_query->set($id); 750 $sql_query->set($id); 751 // child 752 $sql_query->set_number($batch_id); 753 $sql_query->set($id); 754 $sql_query->set($id); 755 // parent 756 $sql_query->set($id); 757 $sql_query->set_number($batch_id); 758 return Rootspersona_Query_Executor::execute($sql_query); 759 } 760 761 /** 762 * Every person that has a WordPress page, with what privacy scoring needs 763 * (privacy code, birth and death dates) — in two queries instead of one per person. 764 * 765 * @return Rootspersona_Persona[] 766 */ 767 public function get_paged_personas_for_privacy() 768 { 769 $sql = "SELECT ri.id AS id, ri.batch_id AS batch_id, ri.wp_page_id AS page," 770 . " IFNULL(rio.privacy_code,'Def') AS privacy" 771 . " FROM rootspersona_indi ri" 772 . " LEFT OUTER JOIN rootspersona_indi_option rio ON rio.indi_id = ri.id AND rio.indi_batch_id = ri.batch_id" 773 . " WHERE ri.wp_page_id IS NOT NULL AND ri.wp_page_id <> '' AND ri.wp_page_id <> '0'"; 774 $rows = Rootspersona_Query_Executor::execute(new Rootspersona_Sql_Query($sql)); 775 776 $sql = "SELECT rie.indi_id AS id, rie.indi_batch_id AS batch_id, red.event_type AS event_type, red.event_date AS event_date" 777 . " FROM rootspersona_indi_event rie" 778 . " JOIN rootspersona_event_detail red ON red.id = rie.event_id" 779 . " WHERE UPPER(red.event_type) LIKE 'BIRT%' OR UPPER(red.event_type) LIKE 'DEAT%'"; 780 $dates = array(); 781 foreach (Rootspersona_Query_Executor::execute(new Rootspersona_Sql_Query($sql)) as $d) { 782 $key = $d['batch_id'] . '|' . $d['id']; 783 $kind = strpos(strtoupper($d['event_type']), 'DEAT') === 0 ? 'death_date' : 'birth_date'; 784 if (empty($dates[$key][$kind])) { 785 $dates[$key][$kind] = (string) $d['event_date']; 786 } 787 } 788 789 $personas = array(); 790 foreach ($rows as $r) { 791 $p = new Rootspersona_Persona(); 792 $p->id = $r['id']; 793 $p->batch_id = $r['batch_id']; 794 $p->page = $r['page']; 795 $p->privacy = $r['privacy']; 796 $key = $r['batch_id'] . '|' . $r['id']; 797 $p->birth_date = isset($dates[$key]['birth_date']) ? $dates[$key]['birth_date'] : ''; 798 $p->death_date = isset($dates[$key]['death_date']) ? $dates[$key]['death_date'] : ''; 799 $personas[] = $p; 800 } 801 return $personas; 802 } 803 804 /** 673 805 * @todo Description of function getPersonaEvents 674 806 * @param $id … … 690 822 if ($rows > 0) { 691 823 $events = array(); 824 $seen = array(); 692 825 for ($idx = 0; 693 826 $idx < $cnt; 694 827 $idx++) { 828 // Imports before 4.2.4 stored each residence twice; show identical facts once. 829 $key = implode("\x1f", array($rows[$idx]['event_type'], $rows[$idx]['event_date'], $rows[$idx]['place'], $rows[$idx]['classification'], $rows[$idx]['cause'])); 830 if (isset($seen[$key])) { 831 continue; 832 } 833 $seen[$key] = true; 695 834 $event = array(); 696 835 $event['type'] = $rows[$idx]['event_type']; … … 700 839 $event['cause'] = $rows[$idx]['cause']; 701 840 $event['associated_person'] = null; //array('name'=>'', 'isPrivate'=> false); 702 $events[ $idx] = $event;841 $events[] = $event; 703 842 } 704 843 } … … 721 860 $families = &self::$_fcache[$batch_id][$id]; 722 861 } else { 723 $sql = "SELECT rf.id as id, rf.spouse1 AS s1, rf.spouse2 AS s2," 724 . "red.event_date AS event_date, red.place AS place" 862 // Every family of the person. (Joining the events here and keeping only 863 // "Marriage" rows dropped families whose only event was e.g. a divorce.) 864 $sql = "SELECT rf.id as id, rf.spouse1 AS s1, rf.spouse2 AS s2" 725 865 . " FROM rootspersona_fam rf" 726 . " LEFT OUTER JOIN rootspersona_fam_event rfe ON rf.id = rfe.fam_id"727 . " AND rf.batch_id = rfe.fam_batch_id"728 . " LEFT OUTER JOIN rootspersona_event_detail red ON red.id = rfe.event_id"729 866 . " WHERE ((rf.spouse1 = ? AND rf.indi_batch_id_1 = ?)" 730 . " OR (rf.spouse2 = ? AND rf.indi_batch_id_2 = ?))" 731 . " AND (red.event_type = ? OR red.event_type IS null)"; 867 . " OR (rf.spouse2 = ? AND rf.indi_batch_id_2 = ?))"; 732 868 $sql_query = new Rootspersona_Sql_Query($sql); 733 869 $sql_query->set($id); … … 735 871 $sql_query->set($id); 736 872 $sql_query->set_number($batch_id); 737 $sql_query->set('Marriage');738 873 $rows = Rootspersona_Query_Executor::execute($sql_query); 739 874 $cnt = count($rows); … … 747 882 $event['fams'] = $rows[$idx]['id']; 748 883 $event['type'] = 'Marriage'; 749 $event['date'] = $rows[$idx]['event_date']; 750 $event['place'] = $rows[$idx]['place']; 884 $event['date'] = null; 885 $event['place'] = null; 886 $event['divorced'] = false; 887 $event['divorce_date'] = ''; 888 foreach ($this->get_family_events($rows[$idx]['id'], $batch_id) as $fe) { 889 $type = strtoupper((string) $fe['event_type']); 890 if ($type === 'MARRIAGE' && $event['date'] === null) { 891 $event['date'] = $fe['event_date']; 892 $event['place'] = $fe['place']; 893 } elseif (strpos($type, 'DIVORCE') === 0 || $type === 'ANNULMENT') { 894 $event['divorced'] = true; 895 if ('' === $event['divorce_date']) { 896 $event['divorce_date'] = (string) $fe['event_date']; 897 } 898 } 899 } 751 900 $id1 = $rows[$idx]['s1']; 752 901 $id2 = $rows[$idx]['s2']; … … 769 918 770 919 /** 920 * Marriage / divorce events of one family. 921 * 922 * @param string $fam_id 923 * @param int|string $batch_id 924 * @return array[] rows: [event_type, event_date, place] 925 */ 926 public function get_family_events($fam_id, $batch_id) 927 { 928 $sql = "SELECT red.event_type AS event_type, red.event_date AS event_date, red.place AS place" 929 . " FROM rootspersona_fam_event rfe" 930 . " JOIN rootspersona_event_detail red ON red.id = rfe.event_id" 931 . " WHERE rfe.fam_id = ? AND rfe.fam_batch_id = ?"; 932 $sql_query = new Rootspersona_Sql_Query($sql); 933 $sql_query->set($fam_id); 934 $sql_query->set_number($batch_id); 935 return Rootspersona_Query_Executor::execute($sql_query); 936 } 937 938 /** 939 * Names after the first one (married name, birth name, also known as). 940 * 941 * @param string $id 942 * @param int|string $batch_id 943 * @return array[] rows: [name_type, given, surname, personal_name] 944 */ 945 public function get_other_names($id, $batch_id) 946 { 947 $sql = "SELECT rnp.name_type AS name_type, rnp.given AS given, rnp.surname AS surname," 948 . " rnp.personal_name AS personal_name" 949 . " FROM rootspersona_indi_name rip" 950 . " JOIN rootspersona_name_personal rnp ON rip.name_id = rnp.id" 951 . " WHERE rip.indi_id = ? AND rip.indi_batch_id = ? AND rip.seq_nbr > 1" 952 . " ORDER BY rip.seq_nbr"; 953 $sql_query = new Rootspersona_Sql_Query($sql); 954 $sql_query->set($id); 955 $sql_query->set_number($batch_id); 956 return Rootspersona_Query_Executor::execute($sql_query); 957 } 958 959 /** 771 960 * @todo Description of function getChildren 772 961 * @param $id … … 804 993 public function get_persona_sources($id, $batch_id) 805 994 { 806 $sql = 'SELECT DISTINCT id,page,title,abbr FROM'807 . ' (SELECT rs.id AS id, rs.wp_page_id AS page,rs.source_title AS title,rs.abbr AS abbr '995 $sql = 'SELECT DISTINCT id,page,title,abbr,cite_page FROM' 996 . ' (SELECT rs.id AS id, rs.wp_page_id AS page,rs.source_title AS title,rs.abbr AS abbr,rsc.source_page AS cite_page' 808 997 . ' FROM rootspersona_indi_cite ric' 809 998 . ' JOIN rootspersona_source_cite rsc ON ric.cite_id = rsc.id' … … 811 1000 . ' WHERE ric.indi_id = ? AND ric.indi_batch_id = ?' 812 1001 . ' UNION' 813 . ' SELECT rs.id AS id, rs.wp_page_id AS page,rs.source_title AS title,rs.abbr AS abbr '1002 . ' SELECT rs.id AS id, rs.wp_page_id AS page,rs.source_title AS title,rs.abbr AS abbr,rsc.source_page AS cite_page' 814 1003 . ' FROM rootspersona_indi_event rie' 815 1004 . ' JOIN rootspersona_event_cite rec ON rie.event_id = rec.event_id' … … 818 1007 . ' WHERE rie.indi_id = ? AND rie.indi_batch_id = ?' 819 1008 . ' UNION' 820 . ' SELECT rs.id AS id, rs.wp_page_id AS page,rs.source_title AS title,rs.abbr AS abbr '1009 . ' SELECT rs.id AS id, rs.wp_page_id AS page,rs.source_title AS title,rs.abbr AS abbr,rsc.source_page AS cite_page' 821 1010 . ' FROM rootspersona_indi_fam rif' 822 1011 . ' JOIN rootspersona_fam_cite rfc ON rfc.fam_id = rif.fam_id AND rfc.fam_batch_id = rif.fam_batch_id' … … 825 1014 . ' WHERE rif.indi_id = ? AND rif.indi_batch_id = ?' 826 1015 . ' UNION' 827 . ' SELECT rs.id AS id, rs.wp_page_id AS page,rs.source_title AS title,rs.abbr AS abbr '1016 . ' SELECT rs.id AS id, rs.wp_page_id AS page,rs.source_title AS title,rs.abbr AS abbr,rsc.source_page AS cite_page' 828 1017 . ' FROM rootspersona_indi_name rin' 829 1018 . ' JOIN rootspersona_name_cite rnc ON rnc.name_id = rin.name_id' … … 844 1033 $sources = null; 845 1034 if ($cnt > 0) { 846 $sources = array(); 1035 // One entry per source; the cited pages ("Folio 12, entry 34") collected under it. 1036 $by_id = array(); 847 1037 for ($idx = 0; 848 1038 $idx < $cnt; 849 1039 $idx++) { 850 $src = array(); 851 $src['src_id'] = $rows[$idx]['id']; 852 $src['src_title'] = $rows[$idx]['title']; 853 $src['src_abbr'] = $rows[$idx]['abbr']; 854 $src['page_id'] = $rows[$idx]['page']; 855 $sources[$idx] = $src; 856 } 1040 $sid = $rows[$idx]['id']; 1041 if (!isset($by_id[$sid])) { 1042 $by_id[$sid] = array( 1043 'src_id' => $sid, 1044 'src_title' => $rows[$idx]['title'], 1045 'src_abbr' => $rows[$idx]['abbr'], 1046 'page_id' => $rows[$idx]['page'], 1047 'src_pages' => array(), 1048 ); 1049 } 1050 $cite_page = trim((string) $rows[$idx]['cite_page']); 1051 if ($cite_page !== '' && !in_array($cite_page, $by_id[$sid]['src_pages'], true)) { 1052 $by_id[$sid]['src_pages'][] = $cite_page; 1053 } 1054 } 1055 $sources = array_values($by_id); 857 1056 } 858 1057 return $sources; -
rootspersona/trunk/php/model/class-RP-Persona.php
r3572543 r3749166 21 21 var $prefix = ""; 22 22 var $nickname = ""; 23 var $other_names = array(); 23 24 var $suffix = ""; 24 25 var $facts; -
rootspersona/trunk/php/pages/class-RP-Add-Page-Builder.php
r3572543 r3749166 22 22 23 23 $block = "<div style='overflow:hidden;width:60%;margin:40px;'><div class='personBanner'> </div>"; 24 if (count($persons) == 0 ) {24 if (count($persons) == 0 && $msg === '') { 25 25 $sync_url = esc_url( admin_url( 'tools.php?page=rootsPersona_tools&rootspage=util&utilityAction=syncPersonaDbToPages&batch_id=' . rawurlencode( (string) $default ) ) ); 26 26 $block .= "<br/><div style='text-align:left;padding:12px;border:1px solid #c3c4c7;background:#f6f7f7;max-width:42em;margin:0 auto;'>" … … 29 29 . ' <a href="' . $sync_url . '">' . esc_html__( 'Open sync now', 'rootspersona' ) . '</a>.</div>'; 30 30 } 31 $block .= "<form action='" . esc_url( $action ) . "' method='POST'>"31 $block .= "<form id='rp-add-pages-form' action='" . esc_url( $action ) . "' method='POST'>" 32 32 . wp_nonce_field('rootspersona_add_page_nonce', '_wpnonce', true, false) 33 33 . "<div style='overflow:hidden;margin:10px;'>" … … 57 57 $cnt = count($persons); 58 58 for ($i = 0; $i < $cnt; $i++) { 59 $opt_value = esc_attr( $persons[$i]['id'] . '|' . $persons[$i]['surname'] . ', ' . $persons[$i]['given']);60 $opt_text = esc_html( $persons[$i]['surname'] . ', ' . $persons[$i]['given']);59 $opt_value = esc_attr( self::option_value( $persons[$i] ) ); 60 $opt_text = esc_html( self::option_label( $persons[$i] ) ); 61 61 $block .= "<option value='" . $opt_value . "'>" . $opt_text . "</option>"; 62 62 } 63 63 $block .= "</select></td>" 64 64 . "</tr><tr><td>" 65 . "<div style='text-align:center;color:red;font-weight:bold'>"66 . esc_html( $msg) . "</div>"65 . "<div id='rp-add-pages-msg' style='text-align:left;max-height:20em;overflow:auto;'>" 66 . wp_kses( $msg, array( 'br' => array() ) ) . "</div>" 67 67 . "<br/><input type='submit' class='js-rp-submit-loading' name='submitAddPageForm' value='" 68 68 . __('Add', 'rootspersona') . "'/>" … … 74 74 return $block; 75 75 } 76 /** 77 * Submitted value: "ID|Surname, Given" (the name becomes the page title). 78 * 79 * @param array $p Row from get_persons_no_page(). 80 * @return string 81 */ 82 public static function option_value( $p ) 83 { 84 return $p['id'] . '|' . $p['surname'] . ', ' . $p['given']; 85 } 86 87 /** 88 * Visible text: "Surname, Given (1850–1920) [I123]", so namesakes can be told apart. 89 * 90 * @param array $p Row from get_persons_no_page(). 91 * @return string 92 */ 93 public static function option_label( $p ) 94 { 95 $label = $p['surname'] . ', ' . $p['given']; 96 $born = ! empty( $p['birth_year'] ) ? (string) $p['birth_year'] : ''; 97 $died = ! empty( $p['death_year'] ) ? (string) $p['death_year'] : ''; 98 if ( '' !== $born || '' !== $died ) { 99 $label .= ' (' . $born . '–' . $died . ')'; 100 } 101 return $label . ' [' . $p['id'] . ']'; 102 } 76 103 } -
rootspersona/trunk/php/pages/class-RP-Evidence-Page-Builder.php
r3572543 r3749166 39 39 . esc_html( $p->surname . ', ' . $p->given ) 40 40 . ' <span style="font-size:smaller">' 41 . ($options['hide_dates'] == 1 ? ' ' : esc_html( '(' . $p->birth_date. ' - '42 . $p->death_date. ')' ))41 . ($options['hide_dates'] == 1 ? ' ' : esc_html( '(' . Rootspersona_Date::format( $p->birth_date ) . ' - ' 42 . Rootspersona_Date::format( $p->death_date ) . ')' )) 43 43 . '</span></a></li>'; 44 44 } -
rootspersona/trunk/php/pages/class-RP-Index-Page-Builder.php
r3572543 r3749166 37 37 . esc_html( $entry->surname ) . ', ' . esc_html( $entry->given ) 38 38 . ($hide_dates == 1 ? ' ' : ('        (' 39 . esc_html( $entry->birth_date) . ' - '40 . esc_html( $entry->death_date)) . ')')39 . esc_html( Rootspersona_Date::format( $entry->birth_date ) ) . ' - ' 40 . esc_html( Rootspersona_Date::format( $entry->death_date ) )) . ')') 41 41 . "</option>"; 42 42 } … … 59 59 $hide_dates = $options['hide_dates']; 60 60 $hdrcolor = ((isset($options['index_hdr_color']) && !empty($options['index_hdr_color'])) 61 ? $options['index_hdr_color']: '#CCCCCC');61 ? esc_attr( $options['index_hdr_color'] ) : '#CCCCCC'); 62 62 $block = $pagination . $xofy; 63 63 $block .= "<table id='personaIndexTable' cellpadding='0' cellspacing='0'>" … … 68 68 $evenodd = 'even'; 69 69 $evencolor = ((isset($options['index_even_color']) && !empty($options['index_even_color'])) 70 ? $options['index_even_color']: 'white');70 ? esc_attr( $options['index_even_color'] ) : 'white'); 71 71 $oddcolor = ((isset($options['index_odd_color']) && !empty($options['index_odd_color'])) 72 ? $options['index_odd_color']: '#DDDDDD');72 ? esc_attr( $options['index_odd_color'] ) : '#DDDDDD'); 73 73 $evenoddcolor = $evencolor; 74 74 foreach ($index AS $persona) { … … 76 76 . esc_html( $persona->surname ) . "</td><td style='background-color:" . esc_attr( $evenoddcolor ) . "' class='given'>" 77 77 . esc_html( $persona->given ) . "</td><td style='background-color:" . esc_attr( $evenoddcolor ) . "' class='rp_dates'>" 78 . ($hide_dates == 1 ? ' ' : (esc_html( $persona->birth_date) . ' - '79 . esc_html( $persona->death_date)))78 . ($hide_dates == 1 ? ' ' : (esc_html( Rootspersona_Date::format( $persona->birth_date ) ) . ' - ' 79 . esc_html( Rootspersona_Date::format( $persona->death_date ) ))) 80 80 . "</td><td style='background-color:" . esc_attr( $evenoddcolor ) . "' class='page'><a href='" . esc_url( $home_url . "?page_id=" 81 81 . $persona->page ) . "'>" . esc_html( $persona->page ) . "</a>" -
rootspersona/trunk/php/pages/class-RP-Option-Page-Builder.php
r3746101 r3749166 4 4 } 5 5 6 /** 7 * Settings page (Settings → RootsPersona): one form split into tabs, WordPress form-table markup. 8 * 9 * The "hide_*" options are stored as before, but shown as "Show …" checkboxes: a hidden field 10 * sends the "hidden" value and the checkbox, when ticked, overrides it with the "shown" value 11 * (PHP keeps the last field of the same name). 12 */ 6 13 class Rootspersona_Option_Page_Builder 7 14 { 8 9 /** 10 * @param $options 11 * 12 * 13 */ 14 function build($options) 15 { 16 /* translators: %s: context (e.g. persona). */ 17 $win1 = Rootspersona_I18n::sprintf( __( 'Edit page is meant to be accessed using the Edit button on a %s page', 'rootspersona' ), 'persona' ); 18 /* translators: %s: context (e.g. persona). */ 19 $win1 .= '. ' . Rootspersona_I18n::sprintf( __( 'Accessing outside of a %s page will result in an invalid personId error.', 'rootspersona' ), 'persona' ); 20 $plugin_images = isset( $options['plugin_url'] ) 21 ? trailingslashit( $options['plugin_url'] ) . 'images/bannerstartwithwp.png' 22 : ( defined( 'ROOTSPERSONA_PLUGIN_URL' ) 23 ? trailingslashit( ROOTSPERSONA_PLUGIN_URL ) . 'images/bannerstartwithwp.png' 24 : plugins_url( 'images/bannerstartwithwp.png', ROOTSPERSONA_PLUGIN_FILE ) ); 25 $banner = esc_url( $plugin_images ); 26 $block = "<div class='wrap'><a href='https://rootspersona.com'><img src='" . $banner . "' alt=''/></a>" 27 . "<div style='padding-left:10px;padding-top:5px;font-size:larger;'>Version " . esc_html( isset( $options['version'] ) ? $options['version'] : '' ); 28 29 if ( empty( $options['hide_donation'] ) ) { 30 $block .= " <span style='display:inline-block;float:right;padding-right:1em;'>" 31 . '<a href="' . esc_url( 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ANLEXXNXKFN9N' ) . '">' 32 . esc_html__( 'Donate', 'rootspersona' ) . '</a>' 33 . "<span style='display:inline-block;float:right;width:15em;padding-right:20em;padding-left:2em;font-size:smaller;'>A Fan of <i>RootsPersona?</i><br>Consider making a donation!</span>"; 34 } 35 echo wp_kses_post( $block . "</div>" ); 36 echo '<form method="post" action="' . esc_url( admin_url( 'options.php' ) ) . '">'; 37 // Nonce + hidden option_page must be inside the form before any submit (broken markup below used to break saves). 38 settings_fields( 'rootspersona_plugin' ); 39 echo "<p class='submit'><input type='submit' name='Submit' value=' " . esc_attr__( 'Save Changes', 'rootspersona' ) . " '/></p>"; 40 echo "<table class='form-table'>"; 41 echo "<tr><td colspan='3'><span class='optionsHdr'>" . esc_html__( 'Privacy Options', 'rootspersona' ) . "</span><hr class='optionsHdr'></span></td></tr>"; 42 echo "<tr valign='top'>"; 43 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[privacy_living]'>" . esc_html__( 'Living Privacy Setting', 'rootspersona' ) . "</label></td>"; 44 $opt = $options['privacy_living']; 45 $exc = ''; 46 $pvt = 'selected'; 47 $mbr = ''; 48 $pub = ''; 49 if ( isset( $opt ) ) { 50 if ( $opt == 'Pub' ) { 51 $pub = 'selected'; 52 $pvt = ''; 53 } elseif ( $opt == 'Mbr' ) { 54 $mbr = 'selected'; 55 $pvt = ''; 56 } elseif ( $opt == 'Exc' ) { 57 $exc = 'selected'; 58 $pvt = ''; 59 } 60 } 61 echo "<td><select id='rootspersona_plugin[privacy_living]' name='rootspersona_plugin[privacy_living]'><option value='Exc' " . esc_attr( $exc ) . ">Exclude FROM site</option>"; 62 echo "<option value='Pvt' " . esc_attr( $pvt ) . ">" . esc_html__( 'Private, Admins Only', 'rootspersona' ) . " </option>"; 63 echo "<option value='Mbr' " . esc_attr( $mbr ) . ">" . esc_html__( 'Members Only', 'rootspersona' ) . "</option>"; 64 echo "<option value='Pub' " . esc_attr( $pub ) . ">" . esc_html__( 'Public', 'rootspersona' ) . "</option></select></td>"; 65 echo "<td>" . esc_html__( 'Privacy Setting to use for living people (no death date and birth date greater than 110 years ago)', 'rootspersona' ) . ".</td></tr>"; 66 echo "<tr valign='top'>"; 67 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[privacy_default]'>" . esc_html__( 'Default Privacy Setting', 'rootspersona' ) . "</label></td>"; 68 $opt = $options['privacy_default']; 69 $pub = 'selected'; 70 $mbr = ''; 71 $pvt = ''; 72 if ( isset( $opt ) ) { 73 if ( $opt == 'Pvt' ) { 74 $pvt = 'selected'; 75 $pub = ''; 76 } elseif ( $opt == 'Mbr' ) { 77 $mbr = 'selected'; 78 $pub = ''; 79 } 80 } 81 echo "<td><select id='rootspersona_plugin[privacy_default]' name='rootspersona_plugin[privacy_default]'><option value='Pvt' " . esc_attr( $pvt ) . ">" . esc_html__( 'Private, Admins Only', 'rootspersona' ) . " </option>"; 82 echo "<option value='Mbr' " . esc_attr( $mbr ) . ">" . esc_html__( 'Members Only', 'rootspersona' ) . "</option>"; 83 echo "<option value='Pub' " . esc_attr( $pub ) . ">" . esc_html__( 'Public', 'rootspersona' ) . "</option></select></td>"; 84 echo "<td>" . esc_html__( 'Privacy Setting to use when nothing else applies', 'rootspersona' ) . ".</td></tr>"; 85 echo "<tr valign='top' class='left-label'>"; 86 echo "<td scope='row' class='left=label' ><label for='rootspersona_plugin[hide_dates]'>" . esc_html__( 'Hide Dates', 'rootspersona' ) . "?</label></td>"; 87 $yes = $options['hide_dates']; 88 if ( isset( $yes ) && $yes == '1' ) { 89 $yes = 'checked'; 90 $no = ''; 91 } else { 92 $yes = ''; 93 $no = 'checked'; 94 } 95 echo "<td><input type='radio' id='rootspersona_plugin[hide_dates]' name='rootspersona_plugin[hide_dates]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona' ); 96 echo " <input type='radio' name='rootspersona_plugin[hide_dates]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona' ) . "</td>"; 97 /* translators: %s: context (e.g. persona). */ 98 echo "<td>" . Rootspersona_I18n::sprintf( esc_html__( 'Some people may want to hide dates for privacy purposes. This is a global flag (impacts all %s pages).', 'rootspersona' ), 'persona' ) . "</td></tr>"; 99 echo "<tr valign='top' class='left-label'>"; 100 echo "<td scope='row' class='left=label' ><label for='rootspersona_plugin[hide_places]'>" . esc_html__( 'Hide Locations', 'rootspersona' ) . "?</label></td>"; 101 $yes = $options['hide_places']; 102 if ( isset( $yes ) && $yes == '1' ) { 103 $yes = 'checked'; 104 $no = ''; 105 } else { 106 $yes = ''; 107 $no = 'checked'; 108 } 109 echo "<td><input type='radio' id='rootspersona_plugin[hide_places]' name='rootspersona_plugin[hide_places]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona' ); 110 echo " <input type='radio' name='rootspersona_plugin[hide_places]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona' ) . "</td>"; 111 /* translators: %s: context (e.g. persona). */ 112 echo "<td>" . Rootspersona_I18n::sprintf( esc_html__( 'Some people may want to hide locations for privacy purposes. This is a global flag (impacts all %s pages).', 'rootspersona' ), 'persona' ) . "</td></tr>"; 113 echo "<tr><td colspan='3'><span class='optionsHdr'>" . esc_html__( 'Display Options', 'rootspersona' ) . "</span><hr class='optionsHdr'></span></td></tr>"; 114 115 // Header Style 116 echo "<tr valign='top' class='left-label'>"; 117 echo "<td scope='row' class='left=label' ><label for='rootspersona_plugin[header_style]'>" . esc_html__( 'Header Style', 'rootspersona' ) . "?</label></td>"; 118 $opt1 = $options['header_style']; 119 if ( isset( $opt1 ) && $opt1 == '1' ) { 120 $opt1 = 'checked'; 121 $opt2 = ''; 122 } else { 123 $opt1 = ''; 124 $opt2 = 'checked'; 125 } 126 echo "<td><input type='radio' id='rootspersona_plugin[header_style]' name='rootspersona_plugin[header_style]' value='1' " . esc_attr( $opt1 ) . ">" . esc_html__( 'Original', 'rootspersona' ); 127 echo " <input type='radio' name='rootspersona_plugin[header_style]' value='2' " . esc_attr( $opt2 ) . ">" . esc_html__( 'Bio', 'rootspersona' ) . "</td>"; 128 echo "<td>" . esc_html__( 'Choose a style for the header panel', 'rootspersona' ) . ".</td></tr>"; 129 130 // Header 131 echo "<tr valign='top'>"; 132 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_header]'>" . esc_html__( 'Hide Header', 'rootspersona') . "?</label></td>"; 133 $yes = $options['hide_header']; 134 if (isset($yes) 135 && $yes == '1' 136 ) { 137 $yes = 'checked'; 138 $no = ''; 139 } else { 140 $yes = ''; 141 $no = 'checked'; 142 } 143 echo "<td><input type='radio' id='rootspersona_plugin[hide_header]' name='rootspersona_plugin[hide_header]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 144 echo " <input type='radio' name='rootspersona_plugin[hide_header]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 145 echo "<td>" . esc_html__( 'Skip the Header Panel on persona pages.', 'rootspersona') . "</td></tr>"; 146 147 // Bio 148 echo "<tr valign='top'>"; 149 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_bio]'>" . esc_html__( 'Hide Bio', 'rootspersona') . "?</label></td>"; 150 $yes = $options['hide_bio']; 151 if (isset($yes) 152 && $yes == '1' 153 ) { 154 $yes = 'checked'; 155 $no = ''; 156 } else { 157 $yes = ''; 158 $no = 'checked'; 159 } 160 echo "<td><input type='radio' id='rootspersona_plugin[hide_bio]' name='rootspersona_plugin[hide_bio]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 161 echo " <input type='radio' name='rootspersona_plugin[hide_bio]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 162 echo "<td>" . esc_html__( 'Skip the Biography panel on persona pages', 'rootspersona') . ".</td></tr>"; 163 164 // Facts 165 echo "<tr valign='top'>"; 166 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_facts]'>" . esc_html__( 'Hide Facts', 'rootspersona') . "?</label></td>"; 167 $yes = $options['hide_facts']; 168 if (isset($yes) 169 && $yes == '1' 170 ) { 171 $yes = 'checked'; 172 $no = ''; 173 } else { 174 $yes = ''; 175 $no = 'checked'; 176 } 177 echo "<td><input type='radio' id='rootspersona_plugin[hide_facts]' name='rootspersona_plugin[hide_facts]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 178 echo " <input type='radio' name='rootspersona_plugin[hide_facts]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 179 echo "<td>" . esc_html__( 'Skip the Facts panel on persona pages', 'rootspersona') . ".</td></tr>"; 180 181 // Ancestors 182 echo "<tr valign='top'>"; 183 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_ancestors]'>" . esc_html__( 'Hide Ancestors', 'rootspersona') . "?</label></td>"; 184 $yes = $options['hide_ancestors']; 185 if (isset($yes) 186 && $yes == '1' 187 ) { 188 $yes = 'checked'; 189 $no = ''; 190 } else { 191 $yes = ''; 192 $no = 'checked'; 193 } 194 echo "<td><input type='radio' id='rootspersona_plugin[hide_ancestors]' name='rootspersona_plugin[hide_ancestors]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 195 echo " <input type='radio' name='rootspersona_plugin[hide_ancestors]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 196 echo "<td>" . esc_html__( 'Skip the Ancestors panel on persona pages', 'rootspersona') . ".</td></tr>"; 197 198 // Descendancy 199 echo "<tr valign='top'>"; 200 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_descendancy]'>" . esc_html__( 'Hide Descendancy', 'rootspersona') . "?</label></td>"; 201 $yes = $options['hide_descendancy']; 202 if (isset($yes) 203 && $yes == '1' 204 ) { 205 $yes = 'checked'; 206 $no = ''; 207 } else { 208 $yes = ''; 209 $no = 'checked'; 210 } 211 echo "<td><input type='radio' id='rootspersona_plugin[hide_descendancy]' name='rootspersona_plugin[hide_descendancy]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 212 echo " <input type='radio' name='rootspersona_plugin[hide_descendancy]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 213 echo "<td>" . esc_html__( 'Skip the Descendancy panel on persona pages', 'rootspersona') . ".</td></tr>"; 214 215 // Family - Child 216 echo "<tr valign='top'>"; 217 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_family_c]'>" . esc_html__( 'Hide Child Family Group', 'rootspersona') . "?</label></td>"; 218 $yes = $options['hide_family_c']; 219 if (isset($yes) 220 && $yes == '1' 221 ) { 222 $yes = 'checked'; 223 $no = ''; 224 } else { 225 $yes = ''; 226 $no = 'checked'; 227 } 228 echo "<td><input type='radio' id='rootspersona_plugin[hide_family_c]' name='rootspersona_plugin[hide_family_c]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 229 echo " <input type='radio' name='rootspersona_plugin[hide_family_c]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 230 echo "<td>" . esc_html__( 'Skip the Family Group panel where the person is a Child on persona pages', 'rootspersona') . ".</td></tr>"; 231 232 // Family - Spouse 233 echo "<tr valign='top'>"; 234 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_family_s]'>" . esc_html__( 'Hide Spousal Family Groups', 'rootspersona') . "?</label></td>"; 235 $yes = $options['hide_family_s']; 236 if (isset($yes) 237 && $yes == '1' 238 ) { 239 $yes = 'checked'; 240 $no = ''; 241 } else { 242 $yes = ''; 243 $no = 'checked'; 244 } 245 echo "<td><input type='radio' id='rootspersona_plugin[hide_family_s]' name='rootspersona_plugin[hide_family_s]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 246 echo " <input type='radio' name='rootspersona_plugin[hide_family_s]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 247 echo "<td>" . esc_html__( 'Skip the Family Group panels where the person is a Spouse on persona pages', 'rootspersona') . ".</td></tr>"; 248 echo "<tr valign='top'>"; 249 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_pictures]'>" . esc_html__( 'Hide Picture', 'rootspersona') . "?</label></td>"; 250 $yes = $options['hide_pictures']; 251 if (isset($yes) 252 && $yes == '1' 253 ) { 254 $yes = 'checked'; 255 $no = ''; 256 } else { 257 $yes = ''; 258 $no = 'checked'; 259 } 260 echo "<td><input type='radio' id='rootspersona_plugin[hide_pictures]' name='rootspersona_plugin[hide_pictures]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 261 echo " <input type='radio' name='rootspersona_plugin[hide_pictures]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 262 /* translators: %s: context (e.g. persona). */ 263 echo "<td>" . Rootspersona_I18n::sprintf( esc_html__( 'Skip the pictures panel on %s pages. Still displays the image in the Header panel', 'rootspersona' ), 'persona' ) . ".</td></tr>"; 264 echo "<tr valign='top'>"; 265 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_evidence]'>" . esc_html__( 'Hide Evidence', 'rootspersona') . "?</label></td>"; 266 $yes = $options['hide_evidence']; 267 if (isset($yes) 268 && $yes == '1' 269 ) { 270 $yes = 'checked'; 271 $no = ''; 272 } else { 273 $yes = ''; 274 $no = 'checked'; 275 } 276 echo "<td><input type='radio' id='rootspersona_plugin[hide_evidence]' name='rootspersona_plugin[hide_evidence]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 277 echo " <input type='radio' name='rootspersona_plugin[hide_evidence]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 278 echo "<td>" . esc_html__( 'Skip the evidence panel in persona pages.', 'rootspersona') . "</td></tr>"; 279 280 echo "<tr valign='top'>"; 281 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_edit_links]'>" . esc_html__( 'Hide Edit Links', 'rootspersona') . "?</label></td>"; 282 $yes = $options['hide_edit_links']; 283 if (isset($yes) 284 && $yes == '1' 285 ) { 286 $yes = 'checked'; 287 $no = ''; 288 } else { 289 $yes = ''; 290 $no = 'checked'; 291 } 292 echo "<td><input type='radio' id='rootspersona_plugin[hide_edit_links]' name='rootspersona_plugin[hide_edit_links]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 293 echo " <input type='radio' name='rootspersona_plugin[hide_edit_links]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 294 /* translators: %s: context (e.g. persona). */ 295 echo "<td>" . Rootspersona_I18n::sprintf( esc_html__( 'Some people may want to hide the edit links at the bottom of the %s page', 'rootspersona' ), 'persona' ) . ".</td></tr>"; 296 297 echo "<tr valign='top'>"; 298 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[hide_undef_pics]'>" . esc_html__( 'Hide Silhouettes', 'rootspersona') . "?</label></td>"; 299 $yes = $options['hide_undef_pics']; 300 if (isset($yes) 301 && $yes == '1' 302 ) { 303 $yes = 'checked'; 304 $no = ''; 305 } else { 306 $yes = ''; 307 $no = 'checked'; 308 } 309 echo "<td><input type='radio' id='rootspersona_plugin[hide_undef_pics]' name='rootspersona_plugin[hide_undef_pics]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Yes', 'rootspersona'); 310 echo " <input type='radio' name='rootspersona_plugin[hide_undef_pics]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'No', 'rootspersona') . "</td>"; 311 echo "<td>" . esc_html__( 'Some people may want to hide the silhouette placeholders in the picture panel', 'rootspersona') . ".</td></tr>"; 312 313 // Misc 314 echo "<tr><td colspan='3'><span class='optionsHdr'>" . esc_html__( 'Utility Pages', 'rootspersona') . "</span><hr class='optionsHdr'></span></td></tr>"; 315 316 echo "<tr valign='top'>"; 317 echo "<td scope='row' class='left-label'><label for='parent_page'>" . esc_html__( 'Parent Page Id', 'rootspersona') . "</label></td>"; 318 echo "<td><input type='text' size='5' name='rootspersona_plugin[parent_page]' id='parent_page'"; 319 echo " value='" . esc_attr( isset( $options['parent_page'] ) ? $options['parent_page'] : '0' ) . "'/></td>"; 320 echo "<td><a href='" . esc_url( $options['home_url'] . '?page_id=' . $options['parent_page'] ) . "'>" . esc_html__( 'Page', 'rootspersona' ) . "</a> "; 321 /* translators: %s: context (e.g. persona). */ 322 echo Rootspersona_I18n::sprintf( esc_html__( 'you want %s pages to be organized under in a menu structure. 0 indicates no parent page', 'rootspersona' ), 'persona' ) . ".</td></tr>"; 323 324 echo "<tr><td colspan='3'><span class='optionsHdr'>" . esc_html__( 'Style Options', 'rootspersona') . "</span><hr class='optionsHdr'></span></td></tr>"; 325 326 echo "<tr valign='top'>"; 327 echo "<td scope='row' class='left-label'><label for='banner_fcolor'>" . esc_html__( 'Banner Foreground Color', 'rootspersona') . "</label></td>"; 328 echo "<td colspan='2'>color:<br/><input type='text' size='8' name='rootspersona_plugin[banner_fcolor]' id='banner_fcolor'"; 329 echo " value='" . esc_attr( isset( $options['banner_fcolor'] ) ? $options['banner_fcolor'] : '' ) . "'/>;</td>"; 330 //echo "<td>" . Rootspersona_I18n::sprintf( __( 'Overrides the default foreground color for the banners between %s panels.', 'rootspersona' ), "persona" ) . "</td></tr>"; 331 332 echo "<tr valign='top'>"; 333 echo "<td scope='row' class='left-label'><label for='banner_bcolor'>" . esc_html__( 'Banner Background Color', 'rootspersona') . "</label></td>"; 334 echo "<td colsspan='2'>background-color:<br/><input type='text' size='8' name='rootspersona_plugin[banner_bcolor]' id='banner_bcolor'"; 335 echo " value='" . esc_attr( isset( $options['banner_bcolor'] ) ? $options['banner_bcolor'] : '' ) . "'/>;</td>"; 336 // echo "<td>" . Rootspersona_I18n::sprintf( __( 'Overrides the default background image for the banners between %s panels. If both color and image are provided, color will take precedence', 'rootspersona' ), "persona" ) . ".</td></tr>"; 337 338 echo "<tr valign='top'>"; 339 echo "<td scope='row' class='left-label'><label for='banner_image'>" . esc_html__( 'Banner Image', 'rootspersona') . "</label></td>"; 340 echo "<td colspan='2'>background-image:<br/>url('<input type='text' size='65' name='rootspersona_plugin[banner_image]' id='banner_image'"; 341 echo " value='" . esc_attr( isset( $options['banner_image'] ) ? $options['banner_image'] : '' ) . "'/>');</td>"; 342 //echo "<td>" . Rootspersona_I18n::sprintf( __( 'Overrides the default background image for the banners between %s panels. If both color and image are provided, color will take precedence', 'rootspersona' ), "persona" ) . ".</td></tr>"; 343 344 345 echo "<tr valign='top'>"; 346 echo "<td scope='row' class='left-label'><label for='group_fcolor'>" . esc_html__( 'Group Foreground Color', 'rootspersona') . "</label></td>"; 347 echo "<td colspan='2'>color:<br/><input type='text' size='8' name='rootspersona_plugin[group_fcolor]' id='group_fcolor'"; 348 echo " value='" . esc_attr( isset( $options['group_fcolor'] ) ? $options['group_fcolor'] : '' ) . "'/>;</td>"; 349 //echo "<td>" . Rootspersona_I18n::sprintf( __( 'Overrides the default foreground color for the banners between %s panels.', 'rootspersona' ), "persona" ) . "</td></tr>"; 350 351 echo "<tr valign='top'>"; 352 echo "<td scope='row' class='left-label'><label for='group_bcolor'>" . esc_html__( 'Group Background Color', 'rootspersona') . "</label></td>"; 353 echo "<td colsspan='2'>background-color:<br/><input type='text' size='8' name='rootspersona_plugin[group_bcolor]' id='group_bcolor'"; 354 echo " value='" . esc_attr( isset( $options['group_bcolor'] ) ? $options['group_bcolor'] : '' ) . "'/>;</td>"; 355 // echo "<td>" . Rootspersona_I18n::sprintf( __( 'Overrides the default background image for the banners between %s panels. If both color and image are provided, color will take precedence', 'rootspersona' ), "persona" ) . ".</td></tr>"; 356 357 echo "<tr valign='top'>"; 358 echo "<td scope='row' class='left-label'><label for='group_image'>" . esc_html__( 'Group Image', 'rootspersona') . "</label></td>"; 359 echo "<td colspan='2'>background-image:<br/>url('<input type='text' size='65' name='rootspersona_plugin[group_image]' id='group_image'"; 360 echo " value='" . esc_attr( isset( $options['group_image'] ) ? $options['group_image'] : '' ) . "'/>');</td>"; 361 //echo "<td>" . Rootspersona_I18n::sprintf( __( 'Overrides the default background image for the banners between %s panels. If both color and image are provided, color will take precedence', 'rootspersona' ), "persona" ) . ".</td></tr>"; 362 363 echo "<tr valign='top'>"; 364 echo "<td scope='row' class='left-label'><label for='pframe_color'>" . esc_html__( 'Persona Frames', 'rootspersona') . "</label></td>"; 365 echo "<td colspan='2'>border-color:<br/><input type='text' size='8' name='rootspersona_plugin[pframe_color]' id='pframe_color'"; 366 echo " value='" . esc_attr( isset( $options['pframe_color'] ) ? $options['pframe_color'] : '' ) . "'/>;</td>"; 367 //echo "<td>" . Rootspersona_I18n::sprintf( __( 'Overrides the default background image for the banners between %s panels. If both color and image are provided, color will take precedence', 'rootspersona' ), "persona" ) . ".</td></tr>"; 368 369 echo "<tr valign='top'>"; 370 echo "<td scope='row' class='left-label'><label for='index_even_color'>" . esc_html__( 'Index Even Row Color', 'rootspersona') . "</label></td>"; 371 echo "<td colsspan='2'>background-color:<br/><input type='text' size='8' name='rootspersona_plugin[index_even_color]' id='index_even_color'"; 372 echo " value='" . esc_attr( isset( $options['index_even_color'] ) ? $options['index_even_color'] : '' ) . "'/>;</td>"; 373 // echo "<td>" . Rootspersona_I18n::sprintf( __( 'Overrides the default background image for the banners between %s panels. If both color and image are provided, color will take precedence', 'rootspersona' ), "persona" ) . ".</td></tr>"; 374 375 echo "<tr valign='top'>"; 376 echo "<td scope='row' class='left-label'><label for='index_odd_color'>" . esc_html__( 'Index Odd Row Color', 'rootspersona') . "</label></td>"; 377 echo "<td colsspan='2'>background-color:<br/><input type='text' size='8' name='rootspersona_plugin[index_odd_color]' id='index_odd_color'"; 378 echo " value='" . esc_attr( isset( $options['index_odd_color'] ) ? $options['index_odd_color'] : '' ) . "'/>;</td>"; 379 // echo "<td>" . Rootspersona_I18n::sprintf( __( 'Overrides the default background image for the banners between %s panels. If both color and image are provided, color will take precedence', 'rootspersona' ), "persona" ) . ".</td></tr>"; 380 381 echo "<tr valign='top'>"; 382 echo "<td scope='row' class='left-label'><label for='index_hdr_color'>" . esc_html__( 'Index Header Row Color', 'rootspersona') . "</label></td>"; 383 echo "<td colsspan='2'>background-color:<br/><input type='text' size='8' name='rootspersona_plugin[index_hdr_color]' id='index_hdr_color'"; 384 echo " value='" . esc_attr( isset( $options['index_hdr_color'] ) ? $options['index_hdr_color'] : '' ) . "'/>;</td>"; 385 // echo "<td>" . Rootspersona_I18n::sprintf( __( 'Overrides the default background image for the banners between %s panels. If both color and image are provided, color will take precedence', 'rootspersona' ), "persona" ) . ".</td></tr>"; 386 387 // Raw custom CSS input (custom_style) was removed for security. Page colours are 388 // configured through the dedicated, validated colour fields above; use the 389 // WordPress Customizer/Additional CSS for any further site-wide styling. 390 391 echo "<tr><td colspan='3'><span class='optionsHdr'>" . esc_html__( 'Misc Options', 'rootspersona') . "</span><hr class='optionsHdr'></span></td></tr>"; 392 393 echo "<tr valign='top'>"; 394 echo "<td scope='row' class='left=label' ><label for='rootspersona_plugin[hide_donation]'>" . esc_html__( 'Hide Donation Button', 'rootspersona') . "?</label></td>"; 395 $yes = isset($options['hide_donation']) ? $options['hide_donation'] : 0; 396 if (isset($yes) 397 && $yes == 1 398 ) { 399 $yes = 'checked'; 400 $no = ''; 401 } else { 402 $yes = ''; 403 $no = 'checked'; 404 } 405 echo "<td><input type='checkbox' id='rootspersona_plugin[hide_donation]' name='rootspersona_plugin[hide_donation]' value='true' " . esc_attr( $yes ) . ">  Yes </td>"; 406 407 echo "<td>" . esc_html__( 'Hide the PayPal Donation button', 'rootspersona') . ".</td></tr>"; 408 /* 409 echo "<tr valign='top'>"; 410 echo "<td scope='row' class='left-label'><label for='rootspersona_plugin[post_page]'>" . __( 'Create Page or Post', 'rootspersona' ) . "?</label></td>"; 411 $yes = $options['post_page']; 412 if ( isset( $yes ) 413 && $yes == '1' ) { 414 $yes = 'checked'; 415 $no = ''; 416 } else { 417 $yes = ''; 418 $no = 'checked'; 419 } 420 echo "<td><input type='radio' id='rootspersona_plugin[post_page]' name='rootspersona_plugin[post_page]' value='1' " . esc_attr( $yes ) . ">" . esc_html__( 'Post','rootspersona'); 421 echo " <input type='radio' name='rootspersona_plugin[post_page]' value='0' " . esc_attr( $no ) . ">" . esc_html__( 'Page','rootspersona') . "</td>"; 422 echo "<td>" . Rootspersona_I18n::sprintf( __( 'Rootspersona can create a Page or Post', 'rootspersona' ), "persona" ) . ".</td></tr>"; 423 */ 424 echo "<tr valign='top'>"; 425 echo "<td scope='row' class='left=label' ><label for='per_page'>" . esc_html__( 'Entries Per Page', 'rootspersona') . "</label></td>"; 426 echo "<td><input type='text' size='5' name='rootspersona_plugin[per_page]' id='per_page'"; 427 echo " value='" . esc_attr( isset( $options['per_page'] ) ? $options['per_page'] : '' ) . "'/></td>"; 428 echo "<td>" . esc_html__( 'Indicates the number of rows on an index page. Default is 25.', 'rootspersona') . ".</td></tr>"; 429 430 echo "<tr valign='top'>"; 431 echo "<td scope='row' class='left=label' ><label for='rootspersona_plugin[is_system_of_record]'>" . esc_html__( 'System Of Record', 'rootspersona') . "?</label></td>"; 432 $yes = isset( $options['is_system_of_record'] ) ? $options['is_system_of_record'] : 0; 433 if (isset($yes) 434 && $yes == 1 435 ) { 436 $yes = 'checked'; 437 $no = ''; 438 } else { 439 $yes = ''; 440 $no = 'checked'; 441 } 442 echo "<td><input type='radio' id='rootspersona_plugin[is_system_of_record]' name='rootspersona_plugin[is_system_of_record]' value='true' " . esc_attr( $yes ) . ">Yes "; 443 echo " <input type='radio' name='rootspersona_plugin[is_system_of_record]' value='false' " . esc_attr( $no ) . ">No </td>"; 444 echo "<td>" 445 . esc_html__( 'Yes indicates data may be entered from this website. Editing data from this website AND importing GEDCOM data is NOT recommended.', 'rootspersona') 446 . ".</td></tr>"; 447 448 echo "<tr valign='top'>"; 449 echo "<td scope='row' class='left=label' ><label for='rootspersona_plugin[is_editor]'>" . esc_html__( 'Allow Editor', 'rootspersona') . "?</label></td>"; 450 $yes = isset($options['is_editor']) ? $options['is_editor'] : 0; 451 if (isset($yes) 452 && $yes == 1 453 ) { 454 $yes = 'checked'; 455 $no = ''; 456 } else { 457 $yes = ''; 458 $no = 'checked'; 459 } 460 echo "<td><input type='checkbox' id='rootspersona_plugin[is_editor]' name='rootspersona_plugin[is_editor]' value='true' " . esc_attr( $yes ) . ">  Yes </td>"; 461 462 echo "<td>" . esc_html__( 'Allow Editor to edit persona pages', 'rootspersona') . ".</td></tr>"; 463 464 // Custom page shortcode/raw content input (custom_page) was removed for security: 465 // persona pages are generated only from the plugin's built-in, structured template. 466 467 echo "</table><p class='submit'>"; 468 echo "<input type='submit' name='Submit' value=' " . esc_html__( 'Save Changes', 'rootspersona') . " '/>"; 469 echo '</p></form></div>'; 470 } 15 /** 16 * @param array $options 17 */ 18 function build( $options ) { 19 if ( ! is_array( $options ) ) { 20 $options = array(); 21 } 22 echo "<div class='wrap rp-settings'>"; 23 $this->header( $options ); 24 25 echo '<form method="post" action="' . esc_url( admin_url( 'options.php' ) ) . '">'; 26 // Nonce + hidden option_page must be inside the form before any submit. 27 settings_fields( 'rootspersona_plugin' ); 28 29 $tabs = array( 30 'privacy' => __( 'Privacy', 'rootspersona' ), 31 'display' => __( 'Persona page', 'rootspersona' ), 32 'colours' => __( 'Colours', 'rootspersona' ), 33 'pages' => __( 'Pages and other', 'rootspersona' ), 34 ); 35 echo "<nav class='nav-tab-wrapper rp-tabs' aria-label='" . esc_attr__( 'Settings sections', 'rootspersona' ) . "'>"; 36 foreach ( $tabs as $slug => $label ) { 37 echo "<a href='#rp-tab-" . esc_attr( $slug ) . "' class='nav-tab' data-tab='" . esc_attr( $slug ) . "'>" . esc_html( $label ) . '</a>'; 38 } 39 echo '</nav>'; 40 41 $this->open_panel( 'privacy', $tabs['privacy'] ); 42 $this->privacy_rows( $options ); 43 $this->close_panel(); 44 45 $this->open_panel( 'display', $tabs['display'] ); 46 $this->display_rows( $options ); 47 $this->close_panel(); 48 49 $this->open_panel( 'colours', $tabs['colours'] ); 50 $this->colour_rows( $options ); 51 $this->close_panel(); 52 53 $this->open_panel( 'pages', $tabs['pages'] ); 54 $this->page_rows( $options ); 55 $this->close_panel(); 56 57 submit_button(); 58 echo '</form></div>'; 59 } 60 61 /** 62 * Logo, version and the donation link. 63 */ 64 private function header( $options ) { 65 $banner = isset( $options['plugin_url'] ) 66 ? trailingslashit( $options['plugin_url'] ) . 'images/bannerstartwithwp.png' 67 : plugins_url( 'images/bannerstartwithwp.png', ROOTSPERSONA_PLUGIN_FILE ); 68 echo "<h1 class='screen-reader-text'>RootsPersona</h1>"; 69 echo "<div class='rp-settings__head'>"; 70 echo "<a href='https://rootspersona.com'><img src='" . esc_url( $banner ) . "' alt='RootsPersona'/></a>"; 71 /* translators: %s: plugin version. */ 72 echo "<span class='rp-settings__version'>" . esc_html( Rootspersona_I18n::sprintf( __( 'Version %s', 'rootspersona' ), isset( $options['version'] ) ? $options['version'] : '' ) ) . '</span>'; 73 if ( empty( $options['hide_donation'] ) ) { 74 echo "<span class='rp-settings__donate'>" . esc_html__( 'A fan of RootsPersona? Consider making a donation!', 'rootspersona' ) 75 . " <a class='button button-secondary' href='" . esc_url( 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ANLEXXNXKFN9N' ) . "'>" 76 . esc_html__( 'Donate', 'rootspersona' ) . '</a></span>'; 77 } 78 echo '</div>'; 79 } 80 81 private function open_panel( $slug, $title ) { 82 echo "<div class='rp-tab-panel' id='rp-tab-" . esc_attr( $slug ) . "'>"; 83 echo "<h2 class='rp-tab-panel__title'>" . esc_html( $title ) . '</h2>'; 84 echo "<table class='form-table' role='presentation'>"; 85 } 86 87 private function close_panel() { 88 echo '</table></div>'; 89 } 90 91 /** 92 * Start of a row: <tr><th><label/></th><td>. 93 */ 94 private function row_start( $label, $for = '', $class = '' ) { 95 echo '<tr' . ( $class ? " class='" . esc_attr( $class ) . "'" : '' ) . "><th scope='row'>"; 96 if ( $for ) { 97 echo "<label for='" . esc_attr( $for ) . "'>" . esc_html( $label ) . '</label>'; 98 } else { 99 echo esc_html( $label ); 100 } 101 echo '</th><td>'; 102 } 103 104 private function row_end( $description = '' ) { 105 if ( '' !== $description ) { 106 echo "<p class='description'>" . esc_html( $description ) . '</p>'; 107 } 108 echo '</td></tr>'; 109 } 110 111 /** 112 * "Show …" checkbox for an inverted hide_* option. 113 * 114 * @param array $options 115 * @param string $key Option name (hide_*). 116 * @param string $label Checkbox label. 117 * @param string $description Optional hint under the label. 118 * @param string $hidden Value stored when hidden. 119 * @param string $shown Value stored when shown. 120 */ 121 private function show_checkbox( $options, $key, $label, $description = '', $hidden = '1', $shown = '0' ) { 122 $is_hidden = ! empty( $options[ $key ] ) && 'false' !== $options[ $key ]; 123 $name = 'rootspersona_plugin[' . $key . ']'; 124 $id = 'rp_show_' . $key; 125 echo "<input type='hidden' name='" . esc_attr( $name ) . "' value='" . esc_attr( $hidden ) . "'/>"; 126 echo "<label for='" . esc_attr( $id ) . "'><input type='checkbox' id='" . esc_attr( $id ) . "' name='" . esc_attr( $name ) . "' value='" . esc_attr( $shown ) . "'" . checked( ! $is_hidden, true, false ) . '/> ' 127 . esc_html( $label ) . '</label>'; 128 if ( '' !== $description ) { 129 echo " <span class='description'>— " . esc_html( $description ) . '</span>'; 130 } 131 echo '<br/>'; 132 } 133 134 /** 135 * Plain yes/no checkbox (value "true" when ticked, "false" otherwise). 136 */ 137 private function yes_checkbox( $options, $key, $label ) { 138 $on = ! empty( $options[ $key ] ) && 'false' !== $options[ $key ]; 139 $name = 'rootspersona_plugin[' . $key . ']'; 140 $id = 'rp_' . $key; 141 echo "<input type='hidden' name='" . esc_attr( $name ) . "' value='false'/>"; 142 echo "<label for='" . esc_attr( $id ) . "'><input type='checkbox' id='" . esc_attr( $id ) . "' name='" . esc_attr( $name ) . "' value='true'" . checked( $on, true, false ) . '/> ' 143 . esc_html( $label ) . '</label>'; 144 } 145 146 /** 147 * Ordinary pages for a dropdown: persona pages (thousands on a big tree) are left out, 148 * the page currently chosen is always kept. 149 * 150 * @param int $keep Page ID to keep in the list. 151 * @return array id => title 152 */ 153 private function page_choices( $keep = 0 ) { 154 global $wpdb; 155 $rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- admin-only list, one query. 156 $wpdb->prepare( 157 "SELECT ID, post_title FROM $wpdb->posts WHERE post_type = 'page' AND post_status IN ('publish','private','draft')" 158 . ' AND ( post_content NOT LIKE %s OR ID = %d ) ORDER BY post_title', 159 '%' . $wpdb->esc_like( '[rootsPersona ' ) . '%', 160 (int) $keep 161 ) 162 ); 163 $out = array(); 164 foreach ( (array) $rows as $row ) { 165 $out[ (int) $row->ID ] = '' !== $row->post_title ? $row->post_title : '#' . $row->ID; 166 } 167 return $out; 168 } 169 170 private function page_select( $name, $id, $selected, $none_label, $choices ) { 171 echo "<select name='" . esc_attr( $name ) . "' id='" . esc_attr( $id ) . "'>"; 172 echo "<option value='0'>" . esc_html( $none_label ) . '</option>'; 173 foreach ( $choices as $page_id => $title ) { 174 echo "<option value='" . esc_attr( $page_id ) . "'" . selected( (int) $selected, $page_id, false ) . '>' . esc_html( $title ) . '</option>'; 175 } 176 echo '</select>'; 177 } 178 179 private function privacy_rows( $options ) { 180 $levels = array( 181 'Pvt' => __( 'Private, Admins Only', 'rootspersona' ), 182 'Mbr' => __( 'Members Only', 'rootspersona' ), 183 'Pub' => __( 'Public', 'rootspersona' ), 184 ); 185 186 $this->row_start( __( 'Living Privacy Setting', 'rootspersona' ), 'rp_privacy_living' ); 187 $living = isset( $options['privacy_living'] ) ? $options['privacy_living'] : 'Pvt'; 188 echo "<select id='rp_privacy_living' name='rootspersona_plugin[privacy_living]'>"; 189 echo "<option value='Exc'" . selected( $living, 'Exc', false ) . '>' . esc_html__( 'Exclude from site', 'rootspersona' ) . '</option>'; 190 foreach ( $levels as $value => $label ) { 191 echo "<option value='" . esc_attr( $value ) . "'" . selected( $living, $value, false ) . '>' . esc_html( $label ) . '</option>'; 192 } 193 echo '</select>'; 194 $this->row_end( __( 'Privacy Setting to use for living people (no death date and birth date greater than 110 years ago)', 'rootspersona' ) . '.' ); 195 196 $this->row_start( __( 'Default Privacy Setting', 'rootspersona' ), 'rp_privacy_default' ); 197 $default = isset( $options['privacy_default'] ) ? $options['privacy_default'] : 'Pub'; 198 echo "<select id='rp_privacy_default' name='rootspersona_plugin[privacy_default]'>"; 199 foreach ( $levels as $value => $label ) { 200 echo "<option value='" . esc_attr( $value ) . "'" . selected( $default, $value, false ) . '>' . esc_html( $label ) . '</option>'; 201 } 202 echo '</select>'; 203 $this->row_end( __( 'Privacy Setting to use when nothing else applies', 'rootspersona' ) . '.' ); 204 205 // People with no dates at all (and no telling dates in close family). 206 $unknown = ( isset( $options['living_unknown'] ) && 'dead' === $options['living_unknown'] ) ? 'dead' : 'living'; 207 $this->row_start( __( 'People without dates', 'rootspersona' ), 'rp_living_unknown' ); 208 echo "<select id='rp_living_unknown' name='rootspersona_plugin[living_unknown]'>" 209 . "<option value='living'" . selected( $unknown, 'living', false ) . '>' . esc_html__( 'Treat as living', 'rootspersona' ) . '</option>' 210 . "<option value='dead'" . selected( $unknown, 'dead', false ) . '>' . esc_html__( 'Treat as deceased', 'rootspersona' ) . '</option></select>'; 211 $this->row_end( __( 'When neither the person nor the parents, spouses or children have dates that settle it. "Treat as living" is the safe choice: living relatives are often entered without dates.', 'rootspersona' ) ); 212 213 // Page shown instead of a person the visitor may not see. 214 $private = isset( $options['private_page'] ) ? (int) $options['private_page'] : 0; 215 $this->row_start( __( 'Page for private people', 'rootspersona' ), 'rootspersona_private_page' ); 216 $this->page_select( 'rootspersona_plugin[private_page]', 'rootspersona_private_page', $private, __( '— none: show "Private" on the person\'s page —', 'rootspersona' ), $this->page_choices( $private ) ); 217 $this->row_end( __( 'Visitors who may not see a person are sent to this page, for example a page saying "Closed. Private access."', 'rootspersona' ) ); 218 219 $this->row_start( __( 'Dates and places', 'rootspersona' ) ); 220 echo '<fieldset>'; 221 $this->show_checkbox( $options, 'hide_dates', __( 'Show dates', 'rootspersona' ) ); 222 $this->show_checkbox( $options, 'hide_places', __( 'Show places', 'rootspersona' ) ); 223 echo '</fieldset>'; 224 /* translators: %s: context (e.g. persona). */ 225 $this->row_end( __( 'Untick to hide them on all persona pages, for example for privacy.', 'rootspersona' ) ); 226 } 227 228 private function display_rows( $options ) { 229 $current_template = Rootspersona_Persona_Templates::sanitize( isset( $options['persona_template'] ) ? $options['persona_template'] : '' ); 230 $this->row_start( __( 'Persona page template', 'rootspersona' ), 'rootspersona_plugin_persona_template' ); 231 echo "<select id='rootspersona_plugin_persona_template' name='rootspersona_plugin[persona_template]'>"; 232 foreach ( Rootspersona_Persona_Templates::available() as $slug => $label ) { 233 echo "<option value='" . esc_attr( $slug ) . "'" . selected( $current_template, $slug, false ) . '>' . esc_html( $label ) . '</option>'; 234 } 235 echo '</select>'; 236 $this->row_end( __( 'Layout of the persona page. Archive card and Chronicle take their colours and fonts from the theme (--rp-* CSS variables) and hide sections that have no data. A page can override it with the template attribute of the shortcode.', 'rootspersona' ) ); 237 238 $this->row_start( __( 'Show on the page', 'rootspersona' ) ); 239 echo '<fieldset>'; 240 $this->show_checkbox( $options, 'hide_bio', __( 'Biography', 'rootspersona' ) ); 241 $this->show_checkbox( $options, 'hide_pictures', __( 'Photos', 'rootspersona' ), __( 'the classic header keeps its picture', 'rootspersona' ) ); 242 echo '</fieldset>'; 243 $this->row_end(); 244 245 // Panels of the classic page only. 246 $this->row_start( __( 'Header Style', 'rootspersona' ), '', 'rp-classic-only' ); 247 $style = ( isset( $options['header_style'] ) && 2 === (int) $options['header_style'] ) ? 2 : 1; 248 echo "<fieldset><label><input type='radio' name='rootspersona_plugin[header_style]' value='1'" . checked( $style, 1, false ) . '/> ' . esc_html__( 'Original', 'rootspersona' ) . '</label><br/>'; 249 echo "<label><input type='radio' name='rootspersona_plugin[header_style]' value='2'" . checked( $style, 2, false ) . '/> ' . esc_html__( 'Bio', 'rootspersona' ) . '</label></fieldset>'; 250 $this->row_end( __( 'Choose a style for the header panel', 'rootspersona' ) . '.' ); 251 252 $this->row_start( __( 'Classic page panels', 'rootspersona' ), '', 'rp-classic-only' ); 253 echo '<fieldset>'; 254 $this->show_checkbox( $options, 'hide_header', __( 'Header', 'rootspersona' ) ); 255 $this->show_checkbox( $options, 'hide_facts', __( 'Facts', 'rootspersona' ) ); 256 $this->show_checkbox( $options, 'hide_ancestors', __( 'Ancestors', 'rootspersona' ) ); 257 $this->show_checkbox( $options, 'hide_descendancy', __( 'Descendants', 'rootspersona' ) ); 258 $this->show_checkbox( $options, 'hide_family_c', __( 'Family where the person is a child', 'rootspersona' ) ); 259 $this->show_checkbox( $options, 'hide_family_s', __( 'Families where the person is a spouse', 'rootspersona' ) ); 260 $this->show_checkbox( $options, 'hide_evidence', __( 'Evidence (sources)', 'rootspersona' ) ); 261 $this->show_checkbox( $options, 'hide_undef_pics', __( 'Silhouettes in place of missing photos', 'rootspersona' ) ); 262 $this->show_checkbox( $options, 'hide_edit_links', __( 'Edit links at the bottom of the page', 'rootspersona' ) ); 263 echo '</fieldset>'; 264 $this->row_end( __( 'These panels exist only in the Classic template.', 'rootspersona' ) ); 265 } 266 267 private function colour_rows( $options ) { 268 // Colours of the Archive card / Chronicle templates (--rp-* custom properties). 269 echo Rootspersona_Template_Colors::render_settings_rows( $options ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- every value is escaped inside render_settings_rows(). 270 271 // Colours and images of the classic panels: only shown while the classic template is selected. 272 $classic_colors = array( 273 'banner_fcolor' => __( 'Banner Foreground Color', 'rootspersona' ), 274 'banner_bcolor' => __( 'Banner Background Color', 'rootspersona' ), 275 'group_fcolor' => __( 'Group Foreground Color', 'rootspersona' ), 276 'group_bcolor' => __( 'Group Background Color', 'rootspersona' ), 277 'pframe_color' => __( 'Persona Frames', 'rootspersona' ), 278 'index_even_color' => __( 'Index Even Row Color', 'rootspersona' ), 279 'index_odd_color' => __( 'Index Odd Row Color', 'rootspersona' ), 280 'index_hdr_color' => __( 'Index Header Row Color', 'rootspersona' ), 281 ); 282 echo "<tr class='rp-classic-only'><td colspan='2'><p class='description'>" . esc_html__( 'The colours below apply to the Classic template only.', 'rootspersona' ) . '</p></td></tr>'; 283 foreach ( $classic_colors as $key => $label ) { 284 $this->row_start( $label, $key, 'rp-classic-only' ); 285 echo "<input type='text' class='rp-color-field' name='rootspersona_plugin[" . esc_attr( $key ) . "]' id='" . esc_attr( $key ) . "'" 286 . " value='" . esc_attr( isset( $options[ $key ] ) ? $options[ $key ] : '' ) . "'/>"; 287 $this->row_end(); 288 } 289 $classic_images = array( 290 'banner_image' => __( 'Banner Image', 'rootspersona' ), 291 'group_image' => __( 'Group Image', 'rootspersona' ), 292 ); 293 foreach ( $classic_images as $key => $label ) { 294 $this->row_start( $label, $key, 'rp-classic-only' ); 295 echo "<input type='url' class='regular-text' name='rootspersona_plugin[" . esc_attr( $key ) . "]' id='" . esc_attr( $key ) . "'" 296 . " value='" . esc_attr( isset( $options[ $key ] ) ? $options[ $key ] : '' ) . "'/> "; 297 echo "<button type='button' class='button rp-media-pick' data-target='" . esc_attr( $key ) . "'>" . esc_html__( 'Choose from Media Library', 'rootspersona' ) . '</button> '; 298 echo "<button type='button' class='button-link rp-media-clear' data-target='" . esc_attr( $key ) . "'>" . esc_html__( 'Remove', 'rootspersona' ) . '</button>'; 299 $this->row_end(); 300 } 301 // Raw custom CSS input (custom_style) was removed for security; use the 302 // WordPress Customizer/Additional CSS for any further site-wide styling. 303 } 304 305 private function page_rows( $options ) { 306 $parent = isset( $options['parent_page'] ) ? (int) $options['parent_page'] : 0; 307 $this->row_start( __( 'Parent page', 'rootspersona' ), 'rp_parent_page' ); 308 $this->page_select( 'rootspersona_plugin[parent_page]', 'rp_parent_page', $parent, __( '— none —', 'rootspersona' ), $this->page_choices( $parent ) ); 309 if ( $parent > 0 && get_permalink( $parent ) ) { 310 echo " <a href='" . esc_url( get_permalink( $parent ) ) . "'>" . esc_html__( 'View page', 'rootspersona' ) . '</a>'; 311 } 312 /* translators: %s: context (e.g. persona). */ 313 $this->row_end( __( 'New persona pages are created as sub-pages of this page. They are kept out of menus and page lists.', 'rootspersona' ) ); 314 315 $this->row_start( __( 'Entries Per Page', 'rootspersona' ), 'per_page' ); 316 echo "<input type='number' min='1' max='500' class='small-text' name='rootspersona_plugin[per_page]' id='per_page' value='" . esc_attr( isset( $options['per_page'] ) ? $options['per_page'] : 25 ) . "'/>"; 317 $this->row_end( __( 'Indicates the number of rows on an index page. Default is 25.', 'rootspersona' ) ); 318 319 $this->row_start( __( 'Editing on the site', 'rootspersona' ) ); 320 echo '<fieldset>'; 321 $this->yes_checkbox( $options, 'is_system_of_record', __( 'Data is entered on this website (system of record)', 'rootspersona' ) ); 322 echo "<p class='description'>" . esc_html__( 'Yes indicates data may be entered from this website. Editing data from this website AND importing GEDCOM data is NOT recommended.', 'rootspersona' ) . '</p>'; 323 $this->yes_checkbox( $options, 'is_editor', __( 'Allow Editor to edit persona pages', 'rootspersona' ) ); 324 echo '</fieldset>'; 325 $this->row_end(); 326 327 $this->row_start( __( 'Donation button', 'rootspersona' ) ); 328 $this->show_checkbox( $options, 'hide_donation', __( 'Show the PayPal donation button on this page', 'rootspersona' ), '', 'true', 'false' ); 329 $this->row_end(); 330 } 471 331 } -
rootspersona/trunk/php/pages/class-RP-Persona-Page-Builder.php
r3577169 r3749166 23 23 * @return string 24 24 */ 25 public function build($persona, $options, $page_id) 26 { 25 public function build($persona, $options, $page_id, $part = 'all') 26 { 27 // Parts (see Rootspersona_Persona_Templates::resolve_part()) switch whole panels off. 28 $want_intro = in_array( $part, array( 'all', 'intro' ), true ); 29 $want_family = in_array( $part, array( 'all', 'family', 'rest' ), true ); 30 $want_photos = in_array( $part, array( 'all', 'photos', 'rest' ), true ); 31 if ( ! $want_intro ) { 32 $options['hide_header'] = 1; 33 $options['hide_bio'] = 1; 34 $options['hide_facts'] = 1; 35 } 36 if ( ! $want_family ) { 37 $options['hide_ancestors'] = 1; 38 $options['hide_family_c'] = 1; 39 $options['hide_family_s'] = 1; 40 $options['hide_descendancy'] = 1; 41 $options['hide_evidence'] = 1; 42 } 43 if ( ! $want_photos ) { 44 $options['hide_pictures'] = 1; 45 } 27 46 $block = ''; 28 47 if ($options['hide_header'] == 0) { … … 44 63 // $block .= $creator->create( $persona, $options ); 45 64 //} 46 if ($options['hide_family_c'] == 0 && isset($persona->ancestors) ) {65 if ($options['hide_family_c'] == 0 && isset($persona->ancestors) && self::has_parental_family($persona)) { 47 66 $block .= Rootspersona_Group_Sheet_Panel_Creator::create_group_child('c0', $persona->ancestors, $persona->siblings, $options); 48 67 } … … 64 83 $block .= $creator->create($persona, $options); 65 84 } // Moved from line 36 by Horacio to show descendancy at the bottom [End] 66 $block .= Rootspersona_Persona_Helper::get_banner($options, ''); 67 $block .= Rootspersona_Persona_Page_Builder::create_end_of_page($persona->id, $persona->batch_id, $page_id, $options); 85 if ( $want_family || 'all' === $part ) { 86 $block .= Rootspersona_Persona_Helper::get_banner($options, ''); 87 $block .= Rootspersona_Persona_Page_Builder::create_end_of_page($persona->id, $persona->batch_id, $page_id, $options); 88 } 68 89 return $block; 90 } 91 92 /** 93 * Whether the "Family Group Sheet - Child" panel has anything to show: a known 94 * parent or a sibling. Without either it was a table of "?" placeholders. 95 * 96 * @param Rootspersona_Persona $persona 97 * @return bool 98 */ 99 protected static function has_parental_family($persona) 100 { 101 foreach (array(2, 3) as $i) { 102 $parent = isset($persona->ancestors[$i]) ? $persona->ancestors[$i] : null; 103 if (!empty($parent) && isset($parent->id) && $parent->id !== '' && $parent->id != '0') { 104 return true; 105 } 106 } 107 // The list of siblings always contains the person; more than one means real siblings. 108 return isset($persona->siblings) && is_array($persona->siblings) && count($persona->siblings) > 1; 69 109 } 70 110 -
rootspersona/trunk/php/pages/panels/class-RP-Ancestors-Panel-Creator.php
r3577169 r3749166 124 124 $tmpDate = '<span itemprop="birth" itemscope itemtype="http://historical-data.org/HistoricalEvent.html">' 125 125 . (strlen($year) == 4 ? '<span itemprop="startDate" content="' . esc_attr( $year ) . '">' : '') 126 . esc_html( preg_replace('/@.*@(.*)/Us', '$1', $bd) ) // Modified by Horacio (lower case s)126 . esc_html( Rootspersona_Date::format( $bd ) ) 127 127 . (strlen($year) == 4 ? '</span>' : '') 128 128 . '</span>'; … … 135 135 $tmpDate = '<span itemprop="death" itemscope itemtype="http://historical-data.org/HistoricalEvent.html">' 136 136 . (strlen($year) == 4 ? '<span itemprop="startDate" content="' . esc_attr( $year ) . '">' : '') 137 . esc_html( preg_replace('/@.*@(.*)/Us', '$1', $dd) ) // Modified by Horacio (lower case s)137 . esc_html( Rootspersona_Date::format( $dd ) ) 138 138 . (strlen($year) == 4 ? '</span>' : '') 139 139 . '</span>'; -
rootspersona/trunk/php/pages/panels/class-RP-Descendancy-Panel-Creator.php
r3577245 r3749166 47 47 $tmpDate = '<span itemprop="birth" itemscope itemtype="http://historical-data.org/HistoricalEvent.html">' 48 48 . (strlen($year) == 4 ? '<span itemprop="startDate" content="' . esc_attr( $year ) . '">' : '') 49 . esc_html( preg_replace('/@.*@(.*)/Us', '$1', $bd) ) // Modified by Horacio (lower case s)49 . esc_html( Rootspersona_Date::format( $bd ) ) 50 50 . (strlen($year) == 4 ? '</span>' : '') 51 51 . '</span>'; 52 $block .= ' b:' . $tmpDate;52 $block .= ' ' . esc_html_x( 'b:', 'born: short label before a date', 'rootspersona' ) . ' ' . $tmpDate; 53 53 } 54 54 $dd = (string) ( $persona->death_date ?? '' ); … … 58 58 $tmpDate = '<span itemprop="death" itemscope itemtype="http://historical-data.org/HistoricalEvent.html">' 59 59 . (strlen($year) == 4 ? '<span itemprop="startDate" content="' . esc_attr( $year ) . '">' : '') 60 . esc_html( preg_replace('/@.*@(.*)/Us', '$1', $dd) ) // Modified by Horacio (lower case s)60 . esc_html( Rootspersona_Date::format( $dd ) ) 61 61 . (strlen($year) == 4 ? '</span>' : '') 62 62 . '</span>'; 63 $block .= ' d:' . $tmpDate;63 $block .= ' ' . esc_html_x( 'd:', 'died: short label before a date', 'rootspersona' ) . ' ' . $tmpDate; 64 64 } 65 65 $block .= '</span>'; … … 93 93 $tmpDate = '<span itemprop="birth" itemscope itemtype="http://historical-data.org/HistoricalEvent.html">' 94 94 . (strlen($year) == 4 ? '<span itemprop="startDate" content="' . esc_attr( $year ) . '">' : '') 95 . esc_html( preg_replace('/@.*@(.*)/Us', '$1', $bd) ) // Modified by Horacio (lower case s)95 . esc_html( Rootspersona_Date::format( $bd ) ) 96 96 . (strlen($year) == 4 ? '</span>' : '') 97 97 . '</span>'; 98 $block .= ' b:' . $tmpDate;98 $block .= ' ' . esc_html_x( 'b:', 'born: short label before a date', 'rootspersona' ) . ' ' . $tmpDate; 99 99 } 100 100 $dd = (string) ( $associated->death_date ?? '' ); … … 104 104 $tmpDate = '<span itemprop="death" itemscope itemtype="http://historical-data.org/HistoricalEvent.html">' 105 105 . (strlen($year) == 4 ? '<span itemprop="startDate" content="' . esc_attr( $year ) . '">' : '') 106 . esc_html( preg_replace('/@.*@(.*)/Us', '$1', $dd) ) // Modified by Horacio (lower case s)106 . esc_html( Rootspersona_Date::format( $dd ) ) 107 107 . (strlen($year) == 4 ? '</span>' : '') 108 108 . '</span>'; 109 $block .= ' d:' . $tmpDate;109 $block .= ' ' . esc_html_x( 'd:', 'died: short label before a date', 'rootspersona' ) . ' ' . $tmpDate; 110 110 } 111 111 $block .= '</span>'; -
rootspersona/trunk/php/pages/panels/class-RP-Evidence-Panel-Creator.php
r3746101 r3749166 25 25 && !empty($persona->sources[$idx]['page_id']) 26 26 ) { 27 $link = '<a href="' . $options['home_url'] . "?page_id=" 28 . $persona->sources[$idx]['page_id'] . '">' 29 . '[' . $persona->sources[$idx]['src_id'] . ']</a>'; 27 $link = '<a href="' . esc_url( $options['home_url'] . '?page_id=' . (int) $persona->sources[$idx]['page_id'] ) . '">' 28 . '[' . esc_html( $persona->sources[$idx]['src_id'] ) . ']</a>'; 30 29 } else { 31 30 $win1 = Rootspersona_I18n::sprintf(__('Evidence page has not yet been created for this source', … … 41 40 . $link 42 41 . '</sup></td><td><span style="padding-left:10px;display:inline-block;">' 43 . $persona->sources[$idx]['src_title'] 42 . esc_html( $persona->sources[$idx]['src_title'] ) 43 . ( ! empty( $persona->sources[$idx]['src_pages'] ) 44 ? ' <span class="rp_cite_page">(' . esc_html( implode( '; ', $persona->sources[$idx]['src_pages'] ) ) . ')</span>' 45 : '' ) 44 46 . '</span></td></tr>'; 45 47 } -
rootspersona/trunk/php/pages/panels/class-RP-Facts-Panel-Creator.php
r3577245 r3749166 37 37 && !empty($facts[$idx]['date']) 38 38 ) { 39 $block .= esc_html( preg_replace('/@.*@(.*)/Us', '$1', $facts[$idx]['date']) ) . ' - '; // Modified by Horacio (lower case s)39 $block .= esc_html( Rootspersona_Date::format( $facts[$idx]['date'] ) ) . ' - '; 40 40 } 41 $block .= esc_html( $facts[$idx]['type'] ) . ' - ' . esc_html( $facts[$idx]['classification'] ) . ' ' . esc_html( $facts[$idx]['cause'] ); 41 $block .= esc_html( $facts[$idx]['type'] ); 42 $details = trim( ( isset( $facts[$idx]['classification'] ) ? $facts[$idx]['classification'] : '' ) . ' ' . ( isset( $facts[$idx]['cause'] ) ? $facts[$idx]['cause'] : '' ) ); 43 if ( '' !== $details ) { 44 $block .= ' - ' . esc_html( $details ); 45 } 42 46 if (!$options['hide_places'] 43 47 && isset($facts[$idx]['place']) 44 48 && !empty($facts[$idx]['place']) 45 49 ) { 46 $block .= ' ;<span class="rp_place">' . esc_html( $facts[$idx]['place'] ) . '</span>';50 $block .= ', <span class="rp_place">' . esc_html( $facts[$idx]['place'] ) . '</span>'; 47 51 } 48 52 $p = $facts[$idx]['associated_person']; … … 85 89 && !empty($facts[$idx]['date']) 86 90 ) { 87 $block .= esc_attr( preg_replace('/@.*@(.*)/Us', '$1', $facts[$idx]['date']) ); // Modified by Horacio (lower case s) 91 // Raw GEDCOM date: stripping the calendar escape here lost it on save. 92 $block .= esc_attr( $facts[$idx]['date'] ); 88 93 } 89 94 $block .= '"/></td>'; -
rootspersona/trunk/php/pages/panels/class-RP-Group-Sheet-Panel-Creator.php
r3577169 r3749166 57 57 ? esc_attr( $options['pframe_color'] ) : 'brown'); 58 58 $color = ((isset($options['group_fcolor']) && !empty($options['group_fcolor'])) 59 ? $options['group_fcolor']: 'black');59 ? esc_attr( $options['group_fcolor'] ) : 'black'); 60 60 $bcolor = ((isset($options['group_bcolor']) && !empty($options['group_bcolor'])) 61 ? $options['group_bcolor']: null);61 ? esc_attr( $options['group_bcolor'] ) : null); 62 62 $img = ((isset($options['group_image']) && !empty($options['group_image'])) 63 ? $options['group_image']: ROOTSPERSONA_PLUGIN_URL . 'images/familyGroupSidebar.jpg');63 ? esc_url( $options['group_image'] ) : ROOTSPERSONA_PLUGIN_URL . 'images/familyGroupSidebar.jpg'); 64 64 65 65 $fill = 'color:' . esc_attr( $color ) . ';' … … 69 69 70 70 $row_span = 4 + (isset($parent->marriages) ? count($parent->marriages) : 0); 71 $birth_date = isset($parent->birth_date) ? preg_replace('/@.*@(.*)/Us', '$1', $parent->birth_date) : ''; // Modified by Horacio (lower case s)71 $birth_date = isset($parent->birth_date) ? Rootspersona_Date::format( $parent->birth_date ) : ''; 72 72 $birth_place = isset($parent->birth_place) ? $parent->birth_place : ''; 73 $death_date = isset($parent->death_date) ? preg_replace('/@.*@(.*)/Us', '$1', $parent->death_date) : ''; // Modified by Horacio (lower case s)73 $death_date = isset($parent->death_date) ? Rootspersona_Date::format( $parent->death_date ) : ''; 74 74 $death_place = isset($parent->death_place) ? $parent->death_place : ''; 75 75 … … 187 187 . $pframe_color . ' !important">' . esc_html__( 'Marriage', 'rootspersona' ) 188 188 . '</td><td id="' . $ppfx . '_mdate" class="rp_date" style="border-color:' . $pframe_color . ' !important" itemprop="startDate">' 189 . ($options['hide_dates'] == 1 ? '' : esc_html( $marriage['date']))189 . ($options['hide_dates'] == 1 ? '' : esc_html( Rootspersona_Date::format( $marriage['date'] ) )) 190 190 . '</td><td class="notes" style="border-color:' . $pframe_color . ' !important">' 191 191 . $spouse . '<span id="' . $ppfx . '_mloc" itemprop="location" itemscope itemtype="http://schema.org/Place">' 192 192 . '<span itemprop="name">' . $place . '</span></span></td></tr>'; 193 if (!empty($marriage['divorced'])) { 194 $block .= '<tr id="' . $ppfx . '_divorce"><td class="label" style="border-color:' 195 . $pframe_color . ' !important">' . esc_html__( 'Divorce', 'rootspersona' ) 196 . '</td><td class="rp_date" style="border-color:' . $pframe_color . ' !important">' 197 . ($options['hide_dates'] == 1 ? '' : esc_html( Rootspersona_Date::format( $marriage['divorce_date'] ) )) 198 . '</td><td class="notes" style="border-color:' . $pframe_color . ' !important"></td></tr>'; 199 } 193 200 } 194 201 return $block; … … 206 213 ? esc_attr( $options['pframe_color'] ) : 'brown'); 207 214 $color = ((isset($options['group_fcolor']) && !empty($options['group_fcolor'])) 208 ? $options['group_fcolor']: 'black');215 ? esc_attr( $options['group_fcolor'] ) : 'black'); 209 216 $bcolor = ((isset($options['group_bcolor']) && !empty($options['group_bcolor'])) 210 ? $options['group_bcolor']: null);217 ? esc_attr( $options['group_bcolor'] ) : null); 211 218 $img = ((isset($options['group_image']) && !empty($options['group_image'])) 212 ? $options['group_image']: ROOTSPERSONA_PLUGIN_URL . 'images/familyGroupSidebar.jpg');219 ? esc_url( $options['group_image'] ) : ROOTSPERSONA_PLUGIN_URL . 'images/familyGroupSidebar.jpg'); 213 220 214 221 $fill = 'color:' . esc_attr( $color ) . ';' … … 230 237 for ($idx = 0; $idx < $cnt; $idx++) { 231 238 $ppfx = $pfx . $idx . $children[$idx]->id; 232 $birth_date = isset($children[$idx]->birth_date) ? preg_replace('/@.*@(.*)/Us', '$1', $children[$idx]->birth_date) : ''; // Modified by Horacio (lower case s)239 $birth_date = isset($children[$idx]->birth_date) ? Rootspersona_Date::format( $children[$idx]->birth_date ) : ''; 233 240 $birth_place = isset($children[$idx]->birth_place) ? $children[$idx]->birth_place : ''; 234 $death_date = isset($children[$idx]->death_date) ? preg_replace('/@.*@(.*)/Us', '$1', $children[$idx]->death_date) : ''; // Modified by Horacio (lower case s)241 $death_date = isset($children[$idx]->death_date) ? Rootspersona_Date::format( $children[$idx]->death_date ) : ''; 235 242 $death_place = isset($children[$idx]->death_place) ? $children[$idx]->death_place : ''; 236 243 -
rootspersona/trunk/php/pages/panels/class-RP-Header-Panel-Creator.php
r3577245 r3749166 140 140 $cnt = isset($persona->notes)?count($persona->notes):0; 141 141 $pframe_color = ((isset($options['pframe_color']) && !empty($options['pframe_color'])) 142 ? ("style='border-color:" . $options['pframe_color']. " !important;'") : '');142 ? ("style='border-color:" . esc_attr( $options['pframe_color'] ) . " !important;'") : ''); 143 143 144 144 if (!isset ($options['header_style']) || $options['header_style'] == '1' || $cnt == 0) { … … 155 155 . '<span class="rp_headerbox" id="hdr_name" itemprop="name">' 156 156 . esc_html( $persona->full_name ) . '</span>' 157 . self::other_names_html( $persona ) 157 158 . '<span class="rp_headerbox" style="padding-left:15px;text-align:right;color:#EBDDE2;display:none;">' 158 159 . esc_html( $persona->id ) . '</span>'; … … 187 188 $tmpDate = '<span itemprop="birth" itemscope itemtype="http://historical-data.org/HistoricalEvent.html">' 188 189 . (strlen($year) == 4 ? '<span itemprop="startDate" content="' . $year . '">' : '') 189 . preg_replace('/@.*@(.*)/Us', '$1', $bd) // Modified by Horacio (lower case s)190 . esc_html( Rootspersona_Date::format( $bd ) ) 190 191 . (strlen($year) == 4 ? '</span>' : '') 191 192 . '</span>'; 192 193 193 $block .= '<br/> b:' . $tmpDate;194 $block .= '<br/>' . esc_html_x( 'b:', 'born: short label before a date', 'rootspersona' ) . ' ' . $tmpDate; 194 195 195 196 $d = preg_replace('/@.*@(.*)/Us', '$1', $dd); // Modified by Horacio (lower case s) … … 197 198 $tmpDate = '<span itemprop="death" itemscope itemtype="http://historical-data.org/HistoricalEvent.html">' 198 199 . (strlen($year) == 4 ? '<span itemprop="startDate" content="' . $year . '">' : '') 199 . preg_replace('/@.*@(.*)/Us', '$1', $dd) // Modified by Horacio (lower case s)200 . esc_html( Rootspersona_Date::format( $dd ) ) 200 201 . (strlen($year) == 4 ? '</span>' : '') 201 202 . '</span>'; 202 203 203 $block .= '<br/> d:' . $tmpDate;204 $block .= '<br/>' . esc_html_x( 'd:', 'died: short label before a date', 'rootspersona' ) . ' ' . $tmpDate; 204 205 } 205 206 … … 217 218 . ' src="' 218 219 . esc_url($thumb) . '"/></a><span class="rp_headerbox" itemprop="name" style="margin-bottom:5px !important;">' 219 . esc_html( $persona->full_name ) . '</span> <br/>';220 . esc_html( $persona->full_name ) . '</span>' . self::other_names_html( $persona ) . '<br/>'; 220 221 if (!$options['hide_dates']) { 221 222 $bd = (string) ( $persona->birth_date ?? '' ); … … 245 246 $tmpDate = '<span itemprop="birth" itemscope itemtype="http://historical-data.org/HistoricalEvent.html">' 246 247 . (strlen($year) == 4 ? '<span itemprop="startDate" content="' . $year . '">' : '') 247 . preg_replace('/@.*@(.*)/Us', '$1', $bd) // Modified by Horacio (lower case s)248 . esc_html( Rootspersona_Date::format( $bd ) ) 248 249 . (strlen($year) == 4 ? '</span>' : '') 249 250 . '</span>'; 250 251 251 $block .= '<br/> b:' . $tmpDate;252 $block .= '<br/>' . esc_html_x( 'b:', 'born: short label before a date', 'rootspersona' ) . ' ' . $tmpDate; 252 253 253 254 $d = preg_replace('/@.*@(.*)/Us', '$1', $dd); // Modified by Horacio (lower case s) … … 255 256 $tmpDate = '<span itemprop="death" itemscope itemtype="http://historical-data.org/HistoricalEvent.html">' 256 257 . (strlen($year) == 4 ? '<span itemprop="startDate" content="' . $year . '">' : '') 257 . preg_replace('/@.*@(.*)/Us', '$1', $dd) // Modified by Horacio (lower case s)258 . esc_html( Rootspersona_Date::format( $dd ) ) 258 259 . (strlen($year) == 4 ? '</span>' : '') 259 260 . '</span>'; 260 261 261 $block .= '<br/> d:' . $tmpDate;262 $block .= '<br/>' . esc_html_x( 'd:', 'died: short label before a date', 'rootspersona' ) . ' ' . $tmpDate; 262 263 } 263 264 $cnt = isset($persona->notes) ? count($persona->notes) : 0; … … 307 308 $header_caption_value = isset($persona->piccaps[$idx0]) ? $persona->piccaps[$idx0] : ''; 308 309 309 $block .= '<div class="rp_caption" style="margin-top:6px;"><label for="cap_1" style="display:block;font-size:11px;margin-bottom:2px;">' . __(' Подпись (нативное описание)', 'rootspersona') . '</label>'310 $block .= '<div class="rp_caption" style="margin-top:6px;"><label for="cap_1" style="display:block;font-size:11px;margin-bottom:2px;">' . __('Caption (native description)', 'rootspersona') . '</label>' 310 311 . '<textarea id="cap_1" name="cap_1" cols="24" rows="2" style="width:100%;max-width:280px;box-sizing:border-box;">' . esc_textarea($header_caption_value) . '</textarea></div>' 311 312 . '<div style="margin-top:6px;text-align:left;font-size:11px;border-top:1px solid #ddd;padding-top:4px;">' 312 . '<div style="margin-bottom:2px;"><label><input type="checkbox" name="rp_show_picdesc_1" value="1"' . ($show_desc_1 ? ' checked="checked"' : '') . ' /> ' . __(' Описание', 'rootspersona') . '</label></div>'313 . '<div style="margin-bottom:2px;"><label><input type="checkbox" name="rp_show_piccaption_1" value="1"' . ($show_cap_1 ? ' checked="checked"' : '') . ' /> ' . __(' Подпись', 'rootspersona') . '</label></div>'313 . '<div style="margin-bottom:2px;"><label><input type="checkbox" name="rp_show_picdesc_1" value="1"' . ($show_desc_1 ? ' checked="checked"' : '') . ' /> ' . __('Description', 'rootspersona') . '</label></div>' 314 . '<div style="margin-bottom:2px;"><label><input type="checkbox" name="rp_show_piccaption_1" value="1"' . ($show_cap_1 ? ' checked="checked"' : '') . ' /> ' . __('Caption', 'rootspersona') . '</label></div>' 314 315 . '<div style="margin-top:4px;">' 315 316 . '<label style="margin-right:4px;"><input type="radio" name="rp_pictext_format_1" value="txt"' . ($pictext_format_1 === 'txt' ? ' checked="checked"' : '') . ' /> TXT</label>' … … 390 391 return $block; 391 392 } 393 394 /** 395 * "née Sokolova · nickname “Petrusha”" under the name, or ''. 396 * 397 * @param Rootspersona_Persona $persona 398 * @return string HTML. 399 */ 400 protected static function other_names_html($persona) 401 { 402 $names = Rootspersona_Persona_Helper::other_names_text($persona); 403 return $names ? '<br/><span class="rp_headerbox rp_other_names">' . esc_html( implode( ' · ', $names ) ) . '</span>' : ''; 404 } 392 405 } 393 -
rootspersona/trunk/php/pages/panels/class-RP-Picture-Panel-Creator.php
r3577245 r3749166 225 225 . '</textarea></div>' 226 226 . '<div style="margin-top:6px;text-align:left;font-size:11px;border-top:1px solid #ddd;padding-top:4px;">' 227 . '<div style="margin-bottom:2px;"><label><input type="checkbox" name="rp_show_picdesc_' . $i . '" value="1"' . ($show_desc ? ' checked="checked"' : '') . ' /> ' . __(' Описание', 'rootspersona') . '</label></div>'228 . '<div style="margin-bottom:2px;"><label><input type="checkbox" name="rp_show_piccaption_' . $i . '" value="1"' . ($show_cap ? ' checked="checked"' : '') . ' /> ' . __(' Подпись', 'rootspersona') . '</label></div>'227 . '<div style="margin-bottom:2px;"><label><input type="checkbox" name="rp_show_picdesc_' . $i . '" value="1"' . ($show_desc ? ' checked="checked"' : '') . ' /> ' . __('Description', 'rootspersona') . '</label></div>' 228 . '<div style="margin-bottom:2px;"><label><input type="checkbox" name="rp_show_piccaption_' . $i . '" value="1"' . ($show_cap ? ' checked="checked"' : '') . ' /> ' . __('Caption', 'rootspersona') . '</label></div>' 229 229 . '<div style="margin-top:4px;">' 230 230 . '<label style="margin-right:4px;"><input type="radio" name="rp_pictext_format_' . $i . '" value="txt"' . ($pictext_format === 'txt' ? ' checked="checked"' : '') . ' /> TXT</label>' -
rootspersona/trunk/readme.txt
r3746101 r3749166 5 5 Requires PHP: 7.4 6 6 Tested up to: 7.1 7 Stable tag: 4.2. 27 Stable tag: 4.2.4 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 104 104 Currently you will lose any links to pictures if you do that. 105 105 106 = How can I help translate RootsPersona or correct a translation? = 107 108 RootsPersona is translated by the community at [translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/rootspersona/). Open your language there and suggest or correct a string. Once the language team approves it, WordPress delivers it to all sites automatically - no plugin update is needed. 109 106 110 == Screenshots == 107 111 … … 115 119 116 120 121 = 4.2.4 = 122 * New: two persona page templates besides the classic layout, "Archive card" and "Chronicle", with colour sets (Sepia, Neutral, Dark or the theme's colours), colour pickers and a live preview. A template page can be printed in parts with [rootsPersona part="intro|family|photos"], so your own content (contents, memoirs, galleries) can sit between them. 123 * New: dates are shown in the site language instead of raw GEDCOM: "15 March 1822", "about 1890", "between 1852 and 1854", "from 1846 to 1890". Julian (Old Style) dates show the New Style in brackets, e.g. "15 (27) March 1822". The edit form keeps the raw date, so saving no longer drops the Old Style mark. 124 * New: divorce, other names (birth name, married name, also known as), nickname and the cited page of each source are now shown. 125 * New: "Page for private people" option — visitors who may not see a person are redirected to a page you choose (for example "Closed. Private access."). 126 * New: "People without dates" option. A person without a birth year is now judged by their other events and by their parents, spouses and children (a child born in 1850 means the parent is deceased; a spouse born in 1990 means living). With no clue at all the option decides; the default is "living" (such people used to be public). A burial without a death date now means deceased. 127 * New: the Settings page is organised in tabs, with "Show …" checkboxes and page dropdowns. 128 * Privacy: persona pages are no longer listed in automatic page lists and menus (block theme navigation, Pages widget), which showed thousands of entries — including names of living people — on every page. 129 * Privacy: pages of living/private people get "Private" as page, browser and Yoast / Rank Math title, a noindex tag, and are left out of the WordPress and Yoast XML sitemaps. 130 * Fix: "Add Uploaded Persons" works for any number of people. Selecting more than about 1000 at once used to create nothing (PHP max_input_vars); pages are now created in chunks with a progress line. Entries show years and the GEDCOM id, so namesakes can be told apart. 131 * Fix: every residence was stored and shown twice. Pages show it once now; reloading the GEDCOM cleans the database. 132 * Fix: families with events but no marriage date no longer disappear from the person page. 133 * Fix: "@#DJULIAN@" no longer shows in marriage dates; stray " - ;" in the facts list removed; the empty "Family Group Sheet - Child" table of "?" is not shown for people without parents. 134 * Performance: public pages load only the scripts they need (about 40 fewer files per page); the jQuery UI widgets stay on admin screens. 135 * Fix: PHP 8.1+ deprecation notices during GEDCOM import. 136 * The experimental SVG tree module is off by default (enable with the ROOTSPERSONA_ENABLE_SVG constant). 137 * Russian translation completed. 117 138 = 4.2.2 = 118 139 * Fix: the Settings page and plugin activation no longer stop with a PHP 8 fatal error ("Unknown format specifier" / "ArgumentCountError" in sprintf) when a translation has a damaged placeholder such as "% pages" instead of "%s pages". Translated messages are now built through a safe formatter that repairs or neutralises a broken format instead of failing. -
rootspersona/trunk/rootspersona.php
r3746101 r3749166 3 3 Plugin Name: RootsPersona 4 4 Description: Build one or more family history pages from a Gedcom file. 5 Version: 4.2. 25 Version: 4.2.4 6 6 Author: Yuriy Fisenko 7 7 Author URI: https://urryes.ru/ … … 34 34 35 35 if ( defined( 'ROOTSPERSONA_PLUGIN_FILE' ) ) { 36 $r p_same_main = false;36 $rootspersona_same_main = false; 37 37 if ( function_exists( 'wp_normalize_path' ) ) { 38 $r p_same_main = ( wp_normalize_path( ROOTSPERSONA_PLUGIN_FILE ) === wp_normalize_path( __FILE__ ) );38 $rootspersona_same_main = ( wp_normalize_path( ROOTSPERSONA_PLUGIN_FILE ) === wp_normalize_path( __FILE__ ) ); 39 39 } else { 40 $r p_same_main = ( str_replace( '\\', '/', ROOTSPERSONA_PLUGIN_FILE ) === str_replace( '\\', '/', __FILE__ ) );40 $rootspersona_same_main = ( str_replace( '\\', '/', ROOTSPERSONA_PLUGIN_FILE ) === str_replace( '\\', '/', __FILE__ ) ); 41 41 } 42 if ( ! $r p_same_main ) {42 if ( ! $rootspersona_same_main ) { 43 43 $GLOBALS['roots_persons_skipped_basename'] = plugin_basename( __FILE__ ); 44 44 if ( ! function_exists( 'roots_persons_duplicate_install_notice' ) ) { … … 93 93 } 94 94 if ( ! defined( 'ROOTSPERSONA_PLUGIN_DIR' ) ) { 95 define( 'ROOTSPERSONA_PLUGIN_DIR', ROOTSPERSONA_PLUGIN_DIR);95 define( 'ROOTSPERSONA_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 96 96 } 97 97 if ( ! defined( 'ROOTSPERSONA_PLUGIN_URL' ) ) { … … 99 99 } 100 100 if ( ! defined( 'ROOTSPERSONA_VERSION' ) ) { 101 define( 'ROOTSPERSONA_VERSION', '4.2. 2' );101 define( 'ROOTSPERSONA_VERSION', '4.2.4' ); 102 102 } 103 103 if ( ! defined( 'ROOTS_PERSONA_UNINSTALL_MODE_OPTION' ) ) { … … 246 246 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/class-RP-Persona-Helper.php'; 247 247 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/class-RP-I18n.php'; 248 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/class-RP-Date.php'; 248 249 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/class-RP-Persona-Installer.php'; 249 250 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/class-RP-Persona-Site-Mender.php'; … … 256 257 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/pages/class-RP-Index-Page-Builder.php'; 257 258 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/pages/class-RP-Persona-Page-Builder.php'; 259 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/templates/class-RP-Persona-Templates.php'; 260 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/class-RP-Template-Colors.php'; 258 261 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/pages/class-RP-Evidence-Page-Builder.php'; 259 262 require_once ROOTSPERSONA_PLUGIN_DIR . 'php/class-RP-Log.php'; … … 277 280 * @var string 278 281 */ 279 var $persona_version = '4.2. 2';282 var $persona_version = '4.2.4'; 280 283 281 284 /** … … 387 390 $builder = new Rootspersona_Persona_Page_Builder(); 388 391 $options = $builder->get_persona_options( $atts, $callback, $options ); 392 // Page templates apply to the full [rootsPersona] page only, not to single-panel shortcodes. 393 $template = 'classic'; 394 if ( empty( $callback ) || 'rootspersona' === strtolower( (string) $callback ) ) { 395 $template = Rootspersona_Persona_Templates::resolve( $atts, $options ); 396 } 389 397 $factory = new Rootspersona_Persona_Factory(); 390 $persona = $factory->get_with_options( $persona_id, $batch_id, $options ); 391 //if($persona->full_name == 'Private') 392 // $post->post_title = 'Private'; 393 $block = $builder->build( $persona, $options, Rootspersona_Persona_Helper::get_page_id() ); 398 $part = Rootspersona_Persona_Templates::resolve_part( $atts ); 399 // Edit links once per page: with the last part, or with the whole page. 400 $edit_links = in_array( $part, array( 'all', 'family', 'rest' ), true ); 401 if ( 'classic' === $template ) { 402 $persona = $factory->get_with_options( $persona_id, $batch_id, $options ); 403 $block = $builder->build( $persona, $options, Rootspersona_Persona_Helper::get_page_id(), $part ); 404 } else { 405 $persona = $factory->get_with_options( $persona_id, $batch_id, Rootspersona_Persona_Templates::loading_options( $options ) ); 406 $block = Rootspersona_Persona_Templates::render( $template, $persona, $options, $part ) 407 . ( $edit_links ? Rootspersona_Persona_Page_Builder::create_end_of_page( $persona->id, $persona->batch_id, Rootspersona_Persona_Helper::get_page_id(), $options ) : '' ); 408 } 394 409 } else { 395 410 $msg = __('Invalid person id.', 'rootspersona'); … … 669 684 $persons[ $p_idx ]['given'] = sanitize_text_field( (string) $p_row['given'] ); 670 685 } 686 $persons[ $p_idx ]['value'] = Rootspersona_Add_Page_Builder::option_value( $persons[ $p_idx ] ); 687 $persons[ $p_idx ]['label'] = Rootspersona_Add_Page_Builder::option_label( $persons[ $p_idx ] ); 671 688 } 672 689 } … … 742 759 743 760 /** 761 * Create persona pages for "ID|Surname, Given" values from the Add Uploaded Persons list. 762 * 763 * @param string[] $persons Sanitized option values. 764 * @param string $batch_id Batch. 765 * @param array $options Plugin options. 766 * @return array { lines: string[] (escaped HTML), created: int, errors: int } 767 */ 768 function create_persona_pages( $persons, $batch_id, $options ) { 769 $result = array( 770 'lines' => array(), 771 'created' => 0, 772 'errors' => 0, 773 ); 774 foreach ( $persons as $person_raw ) { 775 $parts = explode( '|', $person_raw, 2 ); 776 $p = $parts[0]; 777 $name = isset( $parts[1] ) ? $parts[1] : ''; 778 $page_id = Rootspersona_Persona_Helper::add_page( $p, $name, $options, $batch_id, null, null ); 779 if ( $page_id != false ) { 780 Rootspersona_Dao_Factory::get_rp_indi_dao()->update_page( $p, $batch_id, $page_id ); 781 $result['created']++; 782 $result['lines'][] = 783 /* translators: %s: page ID created by RootsPersona. */ 784 Rootspersona_I18n::sprintf( __( 'Page %s created for', 'rootspersona' ), esc_html( $page_id ) ) 785 . ' ' . esc_html( $p ) . ': ' . esc_html( $name ); 786 } else { 787 $result['errors']++; 788 $result['lines'][] = esc_html__( 'Error creating page for', 'rootspersona' ) . ' ' . esc_html( $p ); 789 } 790 // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged 791 set_time_limit( 60 ); 792 } 793 if ( $result['created'] > 0 ) { 794 delete_transient( Rootspersona_Persona_Helper::GUEST_PRIVATE_PAGES ); 795 } 796 return $result; 797 } 798 799 /** 800 * AJAX: create pages for one chunk of the Add Uploaded Persons selection. 801 * The browser sends the selection in small chunks, so neither PHP's max_input_vars 802 * (1000 on most hosts) nor the request time limit is hit on big trees. 803 */ 804 function add_pages_ajax() { 805 if ( ! current_user_can( 'manage_options' ) ) { 806 wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'rootspersona' ) ), 403 ); 807 } 808 check_ajax_referer( 'rootspersona_add_page_nonce', '_wpnonce' ); 809 810 $persons = array(); 811 if ( isset( $_POST['persons'] ) && is_array( $_POST['persons'] ) ) { 812 $raw_persons = wp_unslash( $_POST['persons'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized below. 813 $persons = array_map( 'sanitize_text_field', $raw_persons ); 814 } 815 $batch_id = isset( $_POST['batch_id'] ) ? sanitize_text_field( wp_unslash( $_POST['batch_id'] ) ) : '1'; 816 if ( empty( $persons ) ) { 817 wp_send_json_error( array( 'message' => __( 'No people selected.', 'rootspersona' ) ) ); 818 } 819 820 wp_send_json_success( $this->create_persona_pages( $persons, $batch_id, get_option( 'rootspersona_plugin' ) ) ); 821 } 822 823 /** 744 824 * 745 825 * @global wpdb $wpdb … … 776 856 $msg = __( 'No people selected.', 'rootspersona' ); 777 857 } else { 778 foreach ( $persons as $person_raw ) { 779 list( $p, $name ) = explode( '|', $person_raw, 2 ); // Modified by Horacio (Begin) 780 //$name = RP_Dao_Factory::get_rp_persona_dao() 781 // ->get_fullname( $p, $batch_id ); // Modified by Horacio (End) 782 $pageId = Rootspersona_Persona_Helper::add_page( $p, $name, $options, $batch_id, null, null ); 783 if ( $pageId != false ) { 784 Rootspersona_Dao_Factory::get_rp_indi_dao() 785 ->update_page( $p, $batch_id, $pageId ); 786 $msg = $msg . '<br/>' 787 /* translators: %s: page ID created by RootsPersona. */ 788 . Rootspersona_I18n::sprintf( __( 'Page %s created for', 'rootspersona' ), 789 esc_html( $pageId ) ) 790 . ' ' . esc_html( $p ) . ': ' . esc_html( $name ); // Modified by Horacio 791 } 792 else { 793 $msg .= '<br/>' 794 . __( 'Error creating page for', 'rootspersona' ) 795 . ' ' . esc_html( $p ); 796 } 797 // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged 798 set_time_limit( 60 ); 799 } 858 $result = $this->create_persona_pages( $persons, $batch_id, $options ); 859 $msg = implode( '<br/>', $result['lines'] ); 800 860 } 801 861 } … … 803 863 $batch_ids = Rootspersona_Dao_Factory::get_rp_persona_dao()->get_batch_ids(); 804 864 805 if ( isset( $_GET['batch_id'] ) ) { 865 if ( isset( $batch_id ) ) { 866 // Keep showing the batch that was just processed. 867 $batch_ids[0] = $batch_id; 868 } elseif ( isset( $_GET['batch_id'] ) ) { 806 869 $batch_ids[0] = sanitize_text_field( wp_unslash( $_GET['batch_id'] ) ); 807 870 } elseif ( count( $batch_ids ) === 0 ) { … … 813 876 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only success flag. 814 877 if ( isset( $_GET['rootspersona_gedcom_uploaded'] ) && '1' === sanitize_text_field( wp_unslash( $_GET['rootspersona_gedcom_uploaded'] ) ) ) { 878 // New dates may change who counts as living. 879 delete_transient( Rootspersona_Persona_Helper::GUEST_PRIVATE_PAGES ); 815 880 $sync_url = admin_url( 'tools.php?page=rootsPersona_tools&rootspage=util&utilityAction=syncPersonaDbToPages&batch_id=' . rawurlencode( (string) ( $batch_ids[0] ?? '1' ) ) ); 816 881 $gedcom_upload_notice = '<div style="padding:12px 14px;margin:0 0 16px;border:1px solid #46b450;background:#ecf7ed;color:#1e1e1e;text-align:left;font-weight:normal;">' … … 1080 1145 } 1081 1146 1147 // Living/private people: the panels say "Private", so must the title. 1148 if ( Rootspersona_Persona_Helper::is_persona_post_restricted( $post ) ) { 1149 return esc_html__( 'Private', 'rootspersona' ); 1150 } 1151 1082 1152 // If there is a single comma between surname block and given names, replace it with a space. 1083 1153 if ( strpos( $title, ',' ) !== false ) { … … 1088 1158 1089 1159 return esc_html( $title ); 1160 } 1161 1162 /** 1163 * Document <title> (single_post_title does not run the_title). 1164 * 1165 * @param string $title 1166 * @param WP_Post $post 1167 * @return string 1168 */ 1169 function filter_persona_document_title( $title, $post = null ) { 1170 if ( is_admin() || ! $post || strpos( $post->post_content, '[rootsPersona' ) === false ) { 1171 return $title; 1172 } 1173 if ( Rootspersona_Persona_Helper::is_persona_post_restricted( $post ) ) { 1174 return __( 'Private', 'rootspersona' ); 1175 } 1176 // Same "Surname Given" form as the visible title. 1177 if ( strpos( $title, ',' ) !== false ) { 1178 $parts = explode( ',', $title, 2 ); 1179 $title = trim( $parts[0] ) . ' ' . trim( $parts[1] ); 1180 } 1181 return $title; 1182 } 1183 1184 /** 1185 * Keep search engines away from pages of living/private people. 1186 * 1187 * @param array $robots 1188 * @return array 1189 */ 1190 function filter_persona_robots( $robots ) { 1191 if ( is_singular() && Rootspersona_Persona_Helper::is_persona_post_restricted( get_queried_object_id() ) ) { 1192 $robots['noindex'] = true; 1193 $robots['nofollow'] = true; 1194 } 1195 return $robots; 1196 } 1197 1198 /** 1199 * SEO plugins (Yoast, Rank Math) build titles from the raw post title, bypassing 1200 * the_title. Swap the raw "Surname, Given" inside their title for the same text 1201 * the page shows: "Surname Given", or "Private" for restricted people. 1202 * 1203 * @param string $title 1204 * @return string 1205 */ 1206 function filter_persona_seo_title( $title ) { 1207 if ( ! is_string( $title ) || ! is_singular() ) { 1208 return $title; 1209 } 1210 $post = get_queried_object(); 1211 if ( ! $post instanceof WP_Post || strpos( $post->post_content, '[rootsPersona' ) === false || $post->post_title === '' ) { 1212 return $title; 1213 } 1214 return str_replace( $post->post_title, $this->filter_persona_document_title( $post->post_title, $post ), $title ); 1215 } 1216 1217 /** 1218 * Yoast prints its own robots meta (string form). 1219 * 1220 * @param string $robots 1221 * @return string 1222 */ 1223 function filter_persona_seo_robots( $robots ) { 1224 if ( is_singular() && Rootspersona_Persona_Helper::is_persona_post_restricted( get_queried_object_id() ) ) { 1225 return 'noindex, nofollow'; 1226 } 1227 return $robots; 1228 } 1229 1230 /** 1231 * Rank Math prints its own robots meta (array form). 1232 * 1233 * @param array $robots 1234 * @return array 1235 */ 1236 function filter_persona_rank_math_robots( $robots ) { 1237 if ( is_singular() && Rootspersona_Persona_Helper::is_persona_post_restricted( get_queried_object_id() ) ) { 1238 $robots['index'] = 'noindex'; 1239 $robots['follow'] = 'nofollow'; 1240 } 1241 return $robots; 1242 } 1243 1244 /** 1245 * Visitors who may not see a person are sent to the page chosen in 1246 * Options → "Page for private people" (if any), instead of an empty persona page. 1247 */ 1248 function redirect_private_persona() { 1249 if ( ! is_singular() ) { 1250 return; 1251 } 1252 $options = get_option( 'rootspersona_plugin' ); 1253 $target = isset( $options['private_page'] ) ? (int) $options['private_page'] : 0; 1254 $current = get_queried_object_id(); 1255 if ( $target <= 0 || $target === $current || 'publish' !== get_post_status( $target ) ) { 1256 return; 1257 } 1258 if ( Rootspersona_Persona_Helper::is_persona_post_restricted( $current ) ) { 1259 nocache_headers(); 1260 // 302: the person becomes visible later (e.g. after a death date is recorded). 1261 wp_safe_redirect( get_permalink( $target ), 302 ); 1262 exit; 1263 } 1264 } 1265 1266 /** 1267 * Keep living/private people out of the WordPress core XML sitemap: 1268 * the sitemap is public and would otherwise list their names (in the URLs). 1269 * 1270 * @param array $args 1271 * @param string $post_type 1272 * @return array 1273 */ 1274 function exclude_private_from_core_sitemap( $args, $post_type ) { 1275 $options = get_option( 'rootspersona_plugin' ); 1276 $type = empty( $options['post_page'] ) ? 'page' : 'post'; 1277 if ( $post_type === $type ) { 1278 $ids = Rootspersona_Persona_Helper::get_guest_private_page_ids(); 1279 if ( $ids ) { 1280 $args['post__not_in'] = array_merge( isset( $args['post__not_in'] ) ? (array) $args['post__not_in'] : array(), $ids ); 1281 } 1282 } 1283 return $args; 1284 } 1285 1286 /** 1287 * Same for the Yoast SEO sitemap. 1288 * 1289 * @param int[] $ids 1290 * @return int[] 1291 */ 1292 function exclude_private_from_yoast_sitemap( $ids ) { 1293 return array_merge( is_array( $ids ) ? $ids : array(), Rootspersona_Persona_Helper::get_guest_private_page_ids() ); 1294 } 1295 1296 /** 1297 * Privacy settings changed: recompute the list of private pages on next use. 1298 */ 1299 function flush_private_pages_cache() { 1300 delete_transient( Rootspersona_Persona_Helper::GUEST_PRIVATE_PAGES ); 1301 } 1302 1303 /** 1304 * Hide persona and evidence pages (children of the parent page) from automatic 1305 * page lists: block themes' Page List / navigation fallback, wp_list_pages(), 1306 * the Pages widget. Otherwise every page of the site lists thousands of people, 1307 * living ones included. Menus built by hand are not affected. 1308 * 1309 * @param array $query_args WP_Query args built by get_pages(). 1310 * @return array 1311 */ 1312 function exclude_persona_pages_query( $query_args, $parsed_args = array() ) { 1313 $parent = $this->get_persona_parent_page_id(); 1314 if ( $parent && ! is_admin() && ! $this->is_persona_pages_request( $parsed_args, $parent ) ) { 1315 $query_args['post_parent__not_in'] = array_merge( 1316 isset( $query_args['post_parent__not_in'] ) ? (array) $query_args['post_parent__not_in'] : array(), 1317 array( $parent ) 1318 ); 1319 $query_args['post__not_in'] = array_merge( 1320 isset( $query_args['post__not_in'] ) ? (array) $query_args['post__not_in'] : array(), 1321 array( $parent ) 1322 ); 1323 } 1324 return $query_args; 1325 } 1326 1327 /** 1328 * Same as above for get_pages() results (WordPress before 6.3 has no query filter). 1329 * 1330 * @param WP_Post[] $pages 1331 * @return WP_Post[] 1332 */ 1333 function exclude_persona_pages( $pages, $parsed_args = array() ) { 1334 $parent = $this->get_persona_parent_page_id(); 1335 if ( ! $parent || is_admin() || ! is_array( $pages ) || $this->is_persona_pages_request( $parsed_args, $parent ) ) { 1336 return $pages; 1337 } 1338 return array_values( 1339 array_filter( 1340 $pages, 1341 function ( $page ) use ( $parent ) { 1342 return (int) $page->ID !== $parent && (int) $page->post_parent !== $parent; 1343 } 1344 ) 1345 ); 1346 } 1347 1348 /** 1349 * A caller that explicitly asks for the persona branch gets it unfiltered. 1350 * 1351 * @param array $parsed_args get_pages() arguments. 1352 * @param int $parent Persona parent page. 1353 * @return bool 1354 */ 1355 function is_persona_pages_request( $parsed_args, $parent ) { 1356 foreach ( array( 'child_of', 'parent' ) as $key ) { 1357 if ( isset( $parsed_args[ $key ] ) && (int) $parsed_args[ $key ] === $parent ) { 1358 return true; 1359 } 1360 } 1361 return false; 1362 } 1363 1364 /** 1365 * @return int Parent page of persona pages, 0 when not set. 1366 */ 1367 function get_persona_parent_page_id() { 1368 $options = get_option( 'rootspersona_plugin' ); 1369 return isset( $options['parent_page'] ) ? (int) $options['parent_page'] : 0; 1090 1370 } 1091 1371 … … 1108 1388 wp_enqueue_style( 'rootsPersona-4' ); 1109 1389 wp_register_style( 1390 'rootspersona-templates', 1391 plugins_url( 'css/persona-templates.css', __FILE__ ), 1392 array(), 1393 $this->persona_version 1394 ); 1395 wp_enqueue_style( 'rootspersona-templates' ); 1396 $template_colors = Rootspersona_Template_Colors::inline_css( get_option( 'rootspersona_plugin', array() ) ); 1397 if ( '' !== $template_colors ) { 1398 wp_add_inline_style( 'rootspersona-templates', $template_colors ); 1399 } 1400 wp_register_style( 1110 1401 'jquery-ui-autocomplete-style', 1111 1402 plugins_url( 'css/jquery.ui.autocomplete.css', __FILE__ ), … … 1121 1412 */ 1122 1413 function insert_persona_scripts() { 1123 $this->insert_admin_scripts(); 1414 // Public pages need only the photo lightbox and the plugin's own scripts; 1415 // the jQuery UI widgets and media uploader are for the admin screens. 1416 wp_enqueue_script( 'thickbox' ); 1417 $this->register_core_scripts(); 1418 wp_enqueue_script( 'rootsUtilities' ); 1419 wp_enqueue_script( 'rootspersona-inline' ); 1124 1420 } 1125 1421 … … 1166 1462 wp_enqueue_script('jquery-ui-tooltip'); 1167 1463 1464 $this->register_core_scripts(); 1465 wp_enqueue_script( 'rootsUtilities' ); 1466 // Add persons: Ctrl/Shift multi-select must account for list scroll (see js file). 1467 wp_register_script( 1468 'rp-persons-add-multiselect', 1469 plugins_url( 'js/rp-persons-add-multiselect.js', __FILE__ ), 1470 array(), 1471 $this->persona_version, 1472 true 1473 ); 1474 wp_localize_script( 1475 'rp-persons-add-multiselect', 1476 'rootspersonaAddPages', 1477 array( 1478 'ajaxurl' => admin_url( 'admin-ajax.php' ), 1479 'i18n' => array( 1480 /* translators: 1: pages processed so far, 2: people selected. */ 1481 'progress' => __( 'Creating pages: %1$s of %2$s…', 'rootspersona' ), 1482 /* translators: 1: pages created, 2: errors. */ 1483 'finished' => __( 'Done. Pages created: %1$s, errors: %2$s.', 'rootspersona' ), 1484 /* translators: 1: pages created, 2: people selected. */ 1485 'stopped' => __( 'Stopped after %1$s of %2$s pages.', 'rootspersona' ), 1486 'failed' => __( 'The server did not answer. Check the import log.', 'rootspersona' ), 1487 ), 1488 ) 1489 ); 1490 wp_enqueue_script( 'rp-persons-add-multiselect' ); 1491 wp_enqueue_script( 'rootspersona-inline' ); 1492 // Registered here so it's available on the Tools page; only enqueued by 1493 // upload_gedcom_handler() once a chunked GEDCOM import has actually started. 1494 wp_register_script( 1495 'rootspersona-gedcom-import', 1496 plugins_url( 'js/rootspersona-gedcom-import.js', __FILE__ ), 1497 array( 'jquery', 'rootsUtilities' ), 1498 $this->persona_version, 1499 true 1500 ); 1501 } 1502 1503 /** 1504 * Scripts shared by the admin screens and the public persona pages. 1505 */ 1506 function register_core_scripts() { 1507 if ( wp_script_is( 'rootsUtilities', 'registered' ) ) { 1508 return; 1509 } 1168 1510 wp_register_script( 1169 1511 'rootsUtilities', … … 1182 1524 ) 1183 1525 ); 1184 wp_enqueue_script( 'rootsUtilities' );1185 // Add persons: Ctrl/Shift multi-select must account for list scroll (see js file).1186 wp_register_script(1187 'rp-persons-add-multiselect',1188 plugins_url( 'js/rp-persons-add-multiselect.js', __FILE__ ),1189 array(),1190 $this->persona_version,1191 true1192 );1193 wp_enqueue_script( 'rp-persons-add-multiselect' );1194 1526 wp_register_script( 1195 1527 'rootspersona-inline', 1196 1528 plugins_url( 'js/rootspersona-inline.js', __FILE__ ), 1197 1529 array(), 1198 $this->persona_version,1199 true1200 );1201 wp_enqueue_script( 'rootspersona-inline' );1202 // Registered here so it's available on the Tools page; only enqueued by1203 // upload_gedcom_handler() once a chunked GEDCOM import has actually started.1204 wp_register_script(1205 'rootspersona-gedcom-import',1206 plugins_url( 'js/rootspersona-gedcom-import.js', __FILE__ ),1207 array( 'jquery', 'rootsUtilities' ),1208 1530 $this->persona_version, 1209 1531 true … … 1321 1643 $self->insert_persona_styles(); 1322 1644 $self->insert_admin_scripts(); 1645 $self->insert_settings_scripts(); 1323 1646 } 1324 1647 } … … 1344 1667 1345 1668 // Use standard WordPress capability checks (`manage_options`) for admin pages. 1669 } 1670 1671 /** 1672 * Colour pickers, template colour sets with live preview and Media Library pickers 1673 * on the settings page. 1674 */ 1675 function insert_settings_scripts() { 1676 wp_enqueue_style( 'wp-color-picker' ); 1677 wp_enqueue_media(); 1678 wp_enqueue_script( 1679 'rootspersona-settings', 1680 plugins_url( 'js/rootspersona-settings.js', __FILE__ ), 1681 array( 'jquery', 'wp-color-picker' ), 1682 $this->persona_version, 1683 true 1684 ); 1685 wp_localize_script( 1686 'rootspersona-settings', 1687 'rootspersonaSettings', 1688 array( 1689 'presets' => Rootspersona_Template_Colors::presets_for_js(), 1690 'chooseImage' => __( 'Choose image', 'rootspersona' ), 1691 'useImage' => __( 'Use this image', 'rootspersona' ), 1692 ) 1693 ); 1694 wp_add_inline_style( 1695 'wp-color-picker', 1696 '.rp-colors-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(14rem,1fr));gap:.8rem 1.2rem}' 1697 . '.rp-colors-grid.is-disabled{opacity:.45;pointer-events:none}' 1698 . '.rp-colors-preview{max-width:22rem;padding:.9rem;gap:.6rem;border:1px dashed #c3c4c7;background:var(--rp-surface,#f6f1e8)}' 1699 . '.rp-colors-preview__caption{margin:0;font-size:12px;color:#646970}' 1700 . '.rp-colors-row{display:flex;flex-wrap:wrap;gap:1.2rem;align-items:flex-start}.rp-colors-row .rp-colors-grid{flex:1 1 28rem}.rp-colors-row .rp-colors-preview{flex:0 1 20rem}' 1701 . '.rp-settings__head{display:flex;flex-wrap:wrap;align-items:center;gap:.6rem 1.2rem;margin:1em 0}.rp-settings__head img{max-width:100%;height:auto}' 1702 . '.rp-settings__version{font-size:14px;color:#50575e}.rp-settings__donate{margin-left:auto;color:#50575e}' 1703 . '.rp-settings.has-tabs .rp-tab-panel__title{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(1px,1px,1px,1px)}' 1704 . '.rp-tab-panel[hidden]{display:none}.rp-settings fieldset label{line-height:2}' 1705 ); 1346 1706 } 1347 1707 … … 1471 1831 1472 1832 /** 1833 * A yes/no option as 1 or 0. The settings form sends "true"/"false"; the plugin 1834 * itself saves the stored options back (plugin upgrade) with 1/0 — both count. 1835 * A key the input does not carry keeps its previous value: only reading "true" 1836 * used to switch stored flags off on every upgrade (system of record, editors). 1837 * 1838 * @param array $i Submitted options. 1839 * @param array $prev Stored options. 1840 * @param string $key 1841 * @return int 1842 */ 1843 private function flag_option( $i, $prev, $key ) { 1844 $on = array( 1, '1', true, 'true', 'on' ); 1845 if ( array_key_exists( $key, $i ) ) { 1846 return in_array( $i[ $key ], $on, true ) ? 1 : 0; 1847 } 1848 return ( isset( $prev[ $key ] ) && in_array( $prev[ $key ], $on, true ) ) ? 1 : 0; 1849 } 1850 1851 /** 1473 1852 * Sanitize and validate input. Accepts an array, return a sanitized array. 1474 1853 * … … 1487 1866 $options['parent_page'] = isset( $i['parent_page'] ) ? intval( $i['parent_page'] ) : ( isset( $prev['parent_page'] ) ? intval( $prev['parent_page'] ) : 0 ); 1488 1867 $options['per_page'] = isset( $i['per_page'] ) ? intval( $i['per_page'] ) : ( isset( $prev['per_page'] ) ? intval( $prev['per_page'] ) : 25 ); 1489 $options['is_system_of_record'] = isset( $i['is_system_of_record'] ) 1490 ? ( $i['is_system_of_record'] === 'true' ? 1 : 0 ) 1491 : ( isset( $prev['is_system_of_record'] ) ? (int) $prev['is_system_of_record'] : 0 ); 1868 $options['is_system_of_record'] = $this->flag_option( $i, $prev, 'is_system_of_record' ); 1492 1869 // Colours: store as validated hex colours (sanitize_hex_color), never via esc_attr (an output-escaping function). 1493 1870 $options['index_even_color'] = $this->sanitize_color_option( isset( $i['index_even_color'] ) ? $i['index_even_color'] : null, isset( $prev['index_even_color'] ) ? $prev['index_even_color'] : '' ); … … 1502 1879 $options['group_image'] = isset( $i['group_image'] ) ? esc_url_raw( trim( wp_unslash( $i['group_image'] ) ) ) : ( isset( $prev['group_image'] ) ? $prev['group_image'] : '' ); 1503 1880 $options['pframe_color'] = $this->sanitize_color_option( isset( $i['pframe_color'] ) ? $i['pframe_color'] : null, isset( $prev['pframe_color'] ) ? $prev['pframe_color'] : '' ); 1881 $options = array_merge( $options, Rootspersona_Template_Colors::sanitize( $i, $prev ) ); 1882 $options['persona_template'] = Rootspersona_Persona_Templates::sanitize( 1883 isset( $i['persona_template'] ) ? wp_unslash( $i['persona_template'] ) : ( isset( $prev['persona_template'] ) ? $prev['persona_template'] : Rootspersona_Persona_Templates::DEFAULT_SLUG ) 1884 ); 1504 1885 $options['header_style'] = isset( $i['header_style'] ) ? ( (int) $i['header_style'] === 2 ? 2 : 1 ) : ( isset( $prev['header_style'] ) ? (int) $prev['header_style'] : 1 ); 1505 1886 $options['hide_header'] = isset( $i['hide_header'] ) ? ( (int) $i['hide_header'] === 1 ? 1 : 0 ) : ( isset( $prev['hide_header'] ) ? (int) $prev['hide_header'] : 0 ); … … 1516 1897 $options['hide_dates'] = isset( $i['hide_dates'] ) ? ( (int) $i['hide_dates'] === 1 ? 1 : 0 ) : ( isset( $prev['hide_dates'] ) ? (int) $prev['hide_dates'] : 0 ); 1517 1898 $options['hide_places'] = isset( $i['hide_places'] ) ? ( (int) $i['hide_places'] === 1 ? 1 : 0 ) : ( isset( $prev['hide_places'] ) ? (int) $prev['hide_places'] : 0 ); 1518 $options['hide_donation'] = ( ! empty( $i['hide_donation'] ) && ( $i['hide_donation'] === 'true' || $i['hide_donation'] === true ) ) ? 1 : 0;1519 $options['is_editor'] = ( ! empty( $i['is_editor'] ) && ( $i['is_editor'] === 'true' || $i['is_editor'] === true ) ) ? 1 : 0;1520 $options['post_page'] = ( ! empty( $i['post_page'] ) && ( $i['post_page'] === 'true' || $i['post_page'] === true ) ) ? 1 : 0;1899 $options['hide_donation'] = $this->flag_option( $i, $prev, 'hide_donation' ); 1900 $options['is_editor'] = $this->flag_option( $i, $prev, 'is_editor' ); 1901 $options['post_page'] = $this->flag_option( $i, $prev, 'post_page' ); 1521 1902 $options['privacy_default'] = ( isset( $i['privacy_default'] ) && in_array( $i['privacy_default'], array( 'Pub', 'Pvt', 'Mbr' ), true ) ) 1522 1903 ? $i['privacy_default'] … … 1525 1906 ? $i['privacy_living'] 1526 1907 : ( isset( $prev['privacy_living'] ) ? $prev['privacy_living'] : 'Mbr' ); 1908 $options['living_unknown'] = ( isset( $i['living_unknown'] ) && in_array( $i['living_unknown'], array( 'living', 'dead' ), true ) ) 1909 ? $i['living_unknown'] 1910 : ( isset( $prev['living_unknown'] ) ? $prev['living_unknown'] : 'living' ); 1911 $options['private_page'] = isset( $i['private_page'] ) ? absint( $i['private_page'] ) : ( isset( $prev['private_page'] ) ? absint( $prev['private_page'] ) : 0 ); 1527 1912 // Raw custom CSS (custom_style) and custom shortcode/page content (custom_page) 1528 1913 // were removed for security; any previously stored values are dropped on save. … … 1593 1978 add_filter( 'the_content', array( $rootspersona_plugin, 'check_permissions' ), 2 ); 1594 1979 add_filter( 'query_vars', array( $rootspersona_plugin, 'parameter_queryvars' ) ); 1980 // Yoast SEO's "Remove unregistered URL parameters" strips the index page number otherwise. 1981 add_filter( 1982 'Yoast\\WP\\SEO\\allowlist_permalink_vars', 1983 function ( $vars ) { 1984 $vars = is_array( $vars ) ? $vars : array(); 1985 $vars[] = 'rootsvar'; 1986 $vars[] = 'override-surname'; 1987 return array_values( array_unique( $vars ) ); 1988 } 1989 ); 1595 1990 add_filter( 'wp_nav_menu_args', array( $rootspersona_plugin, 'person_menu_filter' ) ); 1596 1991 add_action('wp_ajax_rootspersona_action', array( $rootspersona_plugin, 'rootspersona_action_callback' ) ); … … 1598 1993 add_action( 'admin_post_' . Rootspersona_Log::CLEAR_ACTION, array( 'Rootspersona_Log', 'handle_clear' ) ); 1599 1994 add_filter( 'the_title', array( $rootspersona_plugin, 'filter_persona_title' ), 10, 2 ); 1995 add_filter( 'single_post_title', array( $rootspersona_plugin, 'filter_persona_document_title' ), 10, 2 ); 1996 add_filter( 'wp_robots', array( $rootspersona_plugin, 'filter_persona_robots' ) ); 1997 foreach ( array( 'wpseo_title', 'wpseo_opengraph_title', 'wpseo_twitter_title', 'rank_math/frontend/title' ) as $rootspersona_seo_hook ) { 1998 add_filter( $rootspersona_seo_hook, array( $rootspersona_plugin, 'filter_persona_seo_title' ) ); 1999 } 2000 add_filter( 'wpseo_robots', array( $rootspersona_plugin, 'filter_persona_seo_robots' ) ); 2001 add_filter( 'rank_math/frontend/robots', array( $rootspersona_plugin, 'filter_persona_rank_math_robots' ) ); 2002 add_action( 'template_redirect', array( $rootspersona_plugin, 'redirect_private_persona' ) ); 2003 add_filter( 'wp_sitemaps_posts_query_args', array( $rootspersona_plugin, 'exclude_private_from_core_sitemap' ), 10, 2 ); 2004 add_filter( 'wpseo_exclude_from_sitemap_by_post_ids', array( $rootspersona_plugin, 'exclude_private_from_yoast_sitemap' ) ); 2005 add_action( 'update_option_rootspersona_plugin', array( $rootspersona_plugin, 'flush_private_pages_cache' ) ); 2006 add_filter( 'get_pages_query_args', array( $rootspersona_plugin, 'exclude_persona_pages_query' ), 10, 2 ); 2007 add_filter( 'get_pages', array( $rootspersona_plugin, 'exclude_persona_pages' ), 10, 2 ); 2008 add_action( 'wp_ajax_rootspersona_add_pages', array( $rootspersona_plugin, 'add_pages_ajax' ) ); 1600 2009 } 1601 2010 … … 1604 2013 // SVG Tree Export Module — safely integrated (load only if files exist to avoid 500 on other sites) 1605 2014 if ( ! defined( 'ROOTSPERSONA_PLUGIN_DIR' ) ) { 1606 define( 'ROOTSPERSONA_PLUGIN_DIR', ROOTSPERSONA_PLUGIN_DIR);2015 define( 'ROOTSPERSONA_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 1607 2016 } 1608 2017 1609 $rootspersona_svg_loaded = false; 1610 try { 1611 $rootspersona_svg_gen = ROOTSPERSONA_PLUGIN_DIR . 'includes/class-rootspersona-svg-generator.php'; 1612 if ( is_readable( $rootspersona_svg_gen ) ) { 1613 require_once $rootspersona_svg_gen; 2018 /** 2019 * The SVG tree module is an unfinished experiment and is OFF by default. 2020 * Enable it with define( 'ROOTSPERSONA_ENABLE_SVG', true ); in wp-config.php, or with the 2021 * 'rootspersona_enable_svg_tree' filter. Runs on init so themes and mu-plugins can filter. 2022 */ 2023 if ( ! function_exists( 'rootspersona_load_svg_module' ) ) { 2024 function rootspersona_load_svg_module() { 2025 $enabled = defined( 'ROOTSPERSONA_ENABLE_SVG' ) ? (bool) ROOTSPERSONA_ENABLE_SVG : false; 2026 $enabled = (bool) apply_filters( 'rootspersona_enable_svg_tree', $enabled ); 2027 2028 if ( ! $enabled ) { 2029 // Keep the shortcode registered so existing pages do not print "[rootspersona_tree]" as text. 2030 add_shortcode( 2031 'rootspersona_tree', 2032 function () { 2033 if ( current_user_can( 'manage_options' ) ) { 2034 return '<p class="rootspersona-notice">' 2035 . esc_html__( 'The RootsPersona SVG tree is disabled (experimental feature). To enable it, set the ROOTSPERSONA_ENABLE_SVG constant to true in wp-config.php.', 'rootspersona' ) 2036 . '</p>'; 2037 } 2038 return ''; 2039 } 2040 ); 2041 return; 2042 } 2043 2044 try { 2045 $generator = ROOTSPERSONA_PLUGIN_DIR . 'includes/class-rootspersona-svg-generator.php'; 2046 if ( ! is_readable( $generator ) ) { 2047 return; 2048 } 2049 require_once $generator; 1614 2050 // Do NOT load SVG admin pages (Export / Settings) so they stay hidden from menus. 1615 $rootspersona_svg_shortcode = ROOTSPERSONA_PLUGIN_DIR . 'includes/class-rootspersona-svg-shortcode.php'; 1616 $rootspersona_svg_export = ROOTSPERSONA_PLUGIN_DIR . 'includes/class-rootspersona-svg-export.php'; 1617 if ( is_readable( $rootspersona_svg_shortcode ) ) { 1618 require_once $rootspersona_svg_shortcode; 1619 } 1620 if ( is_readable( $rootspersona_svg_export ) ) { 1621 require_once $rootspersona_svg_export; 1622 } 1623 $rootspersona_svg_loaded = true; 2051 $shortcode = ROOTSPERSONA_PLUGIN_DIR . 'includes/class-rootspersona-svg-shortcode.php'; 2052 $export = ROOTSPERSONA_PLUGIN_DIR . 'includes/class-rootspersona-svg-export.php'; 2053 if ( is_readable( $shortcode ) ) { 2054 require_once $shortcode; 2055 } 2056 if ( is_readable( $export ) ) { 2057 require_once $export; 2058 } 2059 // nopriv hook intentionally absent: the SVG tree contains genealogy data (names, dates, 2060 // family relationships) that must not be exposed to anonymous visitors. 2061 if ( function_exists( 'rootspersona_ajax_load_svg_tree' ) 2062 && false === has_action( 'wp_ajax_rootspersona_load_svg_tree', 'rootspersona_ajax_load_svg_tree' ) ) { 2063 add_action( 'wp_ajax_rootspersona_load_svg_tree', 'rootspersona_ajax_load_svg_tree' ); 2064 } 2065 } catch ( Throwable $e ) { 2066 return; 1624 2067 } 1625 } catch ( Throwable $e ) {1626 2068 } 2069 } 2070 add_action( 'init', 'rootspersona_load_svg_module', 1 ); 1627 2071 1628 2072 /** … … 1691 2135 } 1692 2136 1693 if ( ! empty( $rootspersona_svg_loaded ) && function_exists( 'rootspersona_ajax_load_svg_tree' ) ) { 1694 if ( false === has_action( 'wp_ajax_rootspersona_load_svg_tree', 'rootspersona_ajax_load_svg_tree' ) ) { 1695 add_action( 'wp_ajax_rootspersona_load_svg_tree', 'rootspersona_ajax_load_svg_tree' ); 1696 } 1697 // nopriv hook intentionally removed: SVG tree contains genealogy data (names, dates, 1698 // family relationships) that must not be exposed to anonymous visitors. 1699 // Anonymous users fall back to inline rendering in the shortcode (render_tree()). 1700 } 2137 -
rootspersona/trunk/uninstall.php
r3730341 r3749166 188 188 189 189 if ( function_exists( 'is_multisite' ) && is_multisite() ) { 190 $ site_ids = function_exists( 'get_sites' )190 $roots_persons_site_ids = function_exists( 'get_sites' ) 191 191 ? get_sites( array( 'fields' => 'ids' ) ) 192 192 : $wpdb->get_col( "SELECT blog_id FROM {$wpdb->blogs}" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared 193 foreach ( $ site_ids as $site_id ) {194 switch_to_blog( (int) $ site_id );193 foreach ( $roots_persons_site_ids as $roots_persons_site_id ) { 194 switch_to_blog( (int) $roots_persons_site_id ); 195 195 roots_persons_uninstall_blog( $roots_persons_uninstall_dir ); 196 196 restore_current_blog();
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)