first commit

This commit is contained in:
2025-02-24 22:33:42 +01:00
commit 737c037e85
18358 changed files with 5392983 additions and 0 deletions

View File

@@ -0,0 +1,951 @@
@charset "UTF-8";
body {
margin: 65px auto 24px;
box-shadow: none;
background: #f1f1f1;
padding: 0;
border: 0; /* fix-pro #856 override WP install.css */
}
#pll-logo {
border: 0;
margin: 0 0 24px;
padding: 0;
text-align: center;
font-family: sans-serif;
font-size: 64px;
text-transform: uppercase;
color: #000;
line-height: normal;
}
#pll-logo a {
display: flex;
justify-content: center;
color: #000;
text-decoration: none;
}
#pll-logo img {
max-width: 100%;
margin-right: 16px;
}
.rtl #pll-logo img {
margin-right: 0;
margin-left: 16px;
}
.pll-wizard-footer {
text-align: center
}
.pll-wizard .select2-container {
text-align: left;
width: auto
}
.pll-wizard .hidden {
display: none
}
.pll-wizard-content {
box-shadow: 0 1px 3px rgba(0, 0, 0, .13);
padding: 2em;
margin: 0 0 20px;
background: #fff;
overflow: hidden;
zoom: 1;
text-align: left;
}
.rtl .pll-wizard-content{
text-align: right;
}
.pll-wizard-content h1,
.pll-wizard-content h2,
.pll-wizard-content h3,
.pll-wizard-content table {
margin: 0 0 20px;
border: 0;
padding: 0;
color: #666;
clear: none;
font-weight: 500
}
.pll-wizard-content p {
margin: 20px 0;
font-size: 1em;
line-height: 1.75em;
color: #666
}
.pll-wizard-content table {
font-size: 1em;
line-height: 1.75em;
color: #666;
width: 100%;
margin-top: 20px;
}
.pll-wizard-content table td span{
display: inline-block;
}
.pll-wizard-content table caption {
caption-side: bottom;
font-style: italic;
text-align: right;
}
.rtl .pll-wizard-content table caption {
text-align: left;
}
.pll-wizard-content table caption .icon-default-lang{
font-style: normal;
}
.pll-wizard-content a {
color: #a03f3f;
}
.pll-wizard-content a:focus,
.pll-wizard-content a:hover,
.pll-wizard-footer-links:hover {
color: #dd5454
}
.pll-wizard-content .pll-wizard-next-steps {
overflow: hidden;
margin: 0 0 24px;
padding-bottom: 2px
}
.pll-wizard-content .pll-wizard-next-steps h2 {
margin-bottom: 12px
}
.pll-wizard-content .pll-wizard-next-steps .pll-wizard-next-steps-first {
float: left;
width: 50%;
box-sizing: border-box
}
.pll-wizard-content .pll-wizard-next-steps .pll-wizard-next-steps-last {
float: right;
width: 50%;
box-sizing: border-box
}
.pll-wizard-content .pll-wizard-next-steps ul {
padding: 0 2em 0 0;
list-style: none outside;
margin: 0
}
.pll-wizard-content .pll-wizard-next-steps ul li a {
display: block;
padding: 0 0 .75em
}
.pll-wizard-content .pll-wizard-next-steps ul li a::before {
color: #82878c;
font: normal 20px/1 dashicons;
speak: none;
display: inline-block;
padding: 0 10px 0 0;
top: 1px;
position: relative;
text-decoration: none!important;
vertical-align: top
}
.pll-wizard-steps {
padding: 0 0 24px;
margin: 0;
list-style: none outside;
overflow: hidden;
color: #ccc;
width: 100%;
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: inline-flex
}
.pll-wizard-steps li {
width: 100%;
float: left;
padding: 0 0 .8em;
margin: 0;
text-align: center;
position: relative;
border-bottom: 4px solid #ccc;
line-height: 1.4em
}
.pll-wizard-steps li a {
color: #a03f3f;
text-decoration: none;
padding: 1.5em;
margin: -1.5em;
position: relative;
z-index: 1
}
.pll-wizard-steps li a:focus,
.pll-wizard-steps li a:hover {
color: #dd5454;
text-decoration: underline
}
.pll-wizard-steps li::before {
content: "";
border: 4px solid #ccc;
border-radius: 100%;
width: 4px;
height: 4px;
position: absolute;
bottom: 0;
left: 50%;
margin-left: -6px;
margin-bottom: -8px;
background: #fff
}
.pll-wizard-steps li.active {
border-color: #a03f3f;
color: #a03f3f;
font-weight: 700
}
.pll-wizard-steps li.active::before {
border-color: #a03f3f
}
.pll-wizard-steps li.done {
border-color: #a03f3f;
color: #a03f3f
}
.pll-wizard-steps li.done::before {
border-color: #a03f3f;
background: #a03f3f
}
.pll-wizard .pll-wizard-actions {
overflow: hidden;
margin: 20px 0 0;
position: relative
}
.pll-wizard .pll-wizard-actions .button {
font-size: 16px;
font-weight: 300;
padding: 1em 2em;
line-height: 1em;
margin-right: .5em;
margin-bottom: 2px;
margin-top: 10px;
height: auto;
border-radius: 4px;
box-shadow: none;
min-width: auto;
border-color: #a03f3f;
color: #a03f3f;
}
.pll-wizard .pll-wizard-content .button {
border-color: #a03f3f;
color: #a03f3f;
}
.pll-wizard .pll-wizard-content .button-primary,
.pll-wizard .pll-wizard-actions .button-primary {
background-color: #a03f3f;
border-color: #a03f3f;
color: #fff;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 0 #a03f3f;
text-shadow: 0 -1px 1px #a03f3f, 1px 0 1px #a03f3f, 0 1px 1px #a03f3f, -1px 0 1px #a03f3f;
margin: 0;
opacity: 1
}
.pll-wizard .pll-wizard-content .button-small .dashicons {
font-size: 15px;
height: auto;
vertical-align: middle;
}
.pll-wizard .button-primary:active,
.pll-wizard .button-primary:focus,
.pll-wizard input[type="checkbox"]:focus + label.button-primary,
.pll-wizard .button-primary:hover {
background: #dd5454;
border-color: #dd5454;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 0 #dd5454
}
.pll-wizard .pll-wizard-actions .button-primary[disabled],
.pll-wizard .pll-wizard-actions .button-primary:disabled,
.pll-wizard .pll-wizard-actions .button-primary.disabled {
cursor: wait;
background-color: #bb5454 !important;
border-color: #bb5454 !important;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 0 #bb5454 !important;
text-shadow: 0 -1px 1px #bb5454, 1px 0 1px #bb5454, 0 1px 1px #bb5454, -1px 0 1px #bb5454 !important;
color: #ffa3a3 !important;
}
.pll-wizard-content p:last-child {
margin-bottom: 0
}
.pll-wizard-footer-links {
font-size: .85em;
color: #7b7b7b;
margin: 1.18em auto;
display: inline-block;
text-align: center
}
.pll-wizard-services {
border: 1px solid #eee;
padding: 0;
margin: 0 0 1em;
list-style: none outside;
border-radius: 4px;
overflow: hidden
}
.pll-wizard-services p {
margin: 0 0 1em 0;
padding: 0;
font-size: 1em;
line-height: 1.5em
}
.pll-wizard-service-item {
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: nowrap;
flex-wrap: nowrap;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
justify-content: space-between;
padding: 0;
border-bottom: 1px solid #eee;
color: #666;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center
}
.media-step .pll-wizard-service-item{
border: 0;
}
.media-step .pll-wizard-service-item:last-child{
display: block;
}
.media-step .pll-wizard-service-item .pll-wizard-service-enable{
padding-bottom: 0;
}
.pll-wizard-service-item:last-child {
border-bottom: 0
}
.pll-wizard-service-item .pll-wizard-service-name {
-webkit-flex-basis: 0;
flex-basis: 0;
min-width: 160px;
text-align: center;
font-weight: 700;
padding: 2em 0;
-webkit-align-self: stretch;
align-self: stretch;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: baseline;
-webkit-align-items: baseline;
align-items: baseline
}
.pll-wizard-service-item .pll-wizard-service-name img {
max-width: 75px
}
.pll-wizard-service-item .pll-wizard-service-description {
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
flex-grow: 1;
padding: 20px
}
.pll-wizard-service-item .pll-wizard-service-example {
padding: 0 20px 20px
}
.pll-wizard-service-item .pll-wizard-service-example p{
text-align: right;
}
.rtl .pll-wizard-service-item .pll-wizard-service-example p{
text-align: left;
}
.pll-wizard-service-item .pll-wizard-service-description p {
margin-bottom: 1em
}
.pll-wizard-service-item .pll-wizard-service-description p:last-child {
margin-bottom: 0
}
.pll-wizard-service-item .pll-wizard-service-description .pll-wizard-service-settings-description {
display: block;
font-style: italic;
color: #999
}
.pll-wizard-service-item .pll-wizard-service-enable {
-webkit-flex-basis: 0;
flex-basis: 0;
min-width: 75px;
text-align: center;
cursor: pointer;
padding: 2em 0;
position: relative;
max-height: 1.5em;
-webkit-align-self: flex-start;
align-self: flex-start;
-webkit-box-ordinal-group: 4;
-webkit-order: 3;
order: 3
}
.pll-wizard-service-item .pll-wizard-service-toggle {
position: relative
}
.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] {
position:absolute;
opacity: 0;
}
.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label {
position: relative;
display: inline-block;
width: 44px;
height: 20px;
border-radius: 10em;
cursor: pointer;
text-indent: -9999px;
}
.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:focus + label {
border:1px dashed #777;
}
.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::before,
.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::after {
content: '';
position: absolute;
}
.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::before {
left: 0;
top: 0;
width: 44px;
height: 20px;
background: #ddd;
border-radius: 10em;
transition: background-color .2s;
}
.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::after {
width: 16px;
height: 16px;
transition: all .2s;
border-radius: 50%;
background: #fff;
margin: 2px;
top: 0;
left: 0;
}
.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:checked + label::before {
background:#a03f3f;
}
.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:checked + label::after {
right: 0;
left:auto;
}
.pll-wizard-service-item .pll-wizard-service-settings {
display: none;
margin-top: .75em;
margin-bottom: 0;
cursor: default
}
.pll-wizard-service-item .pll-wizard-service-settings.hide {
display: none
}
.pll-wizard-service-item.checked .pll-wizard-service-settings {
display: inline-block
}
.pll-wizard-service-item.checked .pll-wizard-service-settings.hide {
display: none
}
.pll-wizard-service-item.closed {
border-bottom: 0
}
.step {
text-align: center
}
.pll-wizard .button .dashicons{
vertical-align: middle;
}
.rtl .dashicons-arrow-right-alt2:before {
content: "\f341";
}
.pll-wizard .pll-wizard-actions .button:active,
.pll-wizard .pll-wizard-actions .button:focus,
.pll-wizard .pll-wizard-actions .button:hover {
box-shadow: none
}
.pll-wizard-next-steps {
border: 1px solid #eee;
border-radius: 4px;
list-style: none;
padding: 0
}
.pll-wizard-next-steps li {
padding: 0
}
.pll-wizard-next-steps .pll-wizard-next-step-item {
display: -webkit-box;
display: -webkit-flex;
display: flex;
border-top: 1px solid #eee
}
.pll-wizard-next-steps .pll-wizard-next-step-item.no-border,
.pll-wizard-next-steps .pll-wizard-next-step-item:first-child {
border-top: 0
}
.pll-wizard-next-steps .pll-wizard-next-step-description {
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
flex-grow: 1;
margin: 1.5em
}
.pll-wizard-next-steps .pll-wizard-next-step-action {
-webkit-box-flex: 0;
-webkit-flex-grow: 0;
flex-grow: 0;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center
}
.pll-wizard-next-steps .pll-wizard-next-step-action .button {
margin: 1em 1.5em
}
.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-next-step-description,
.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-actions,
.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-next-step-action .button{
margin-top: 0;
}
.pll-wizard-next-steps p.next-step-heading {
margin: 0;
font-size: .95em;
font-weight: 400;
font-variant: all-petite-caps
}
.pll-wizard-next-steps p.next-step-extra-info {
margin: 0
}
.pll-wizard-next-steps h3.next-step-description {
margin: 0;
font-size: 16px;
font-weight: 600;
}
.pll-wizard-next-steps .pll-wizard-additional-steps {
border-top: 1px solid #eee;
}
.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-next-step-description {
margin-bottom: 0
}
.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions {
margin: 0 0 1.5em 0;
}
.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button {
font-size: 15px;
margin: 1em 0 1em 1.5em;
}
.rtl .pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button {
margin: 1em 1.5em 1em 0;
}
.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button::last-child {
margin-right: 1.5em;
}
.pll-wizard-content img{
max-width: 100%;
margin-right: 0.5em;
}
.rtl .pll-wizard-content img{
margin-left: 0.5em;
}
.pll-wizard-content .form-field label{
margin-bottom: 5px;
display: block;
}
.pll-wizard-content .form-field select{
padding: 3px;
}
.pll-wizard-content .languages-step select,
.pll-wizard-content .untranslated-contents-step select{
width: 100%;
}
.languages-step .form-field .button{
margin-left: 15px;
}
.languages-step .form-field .button > span{
margin-right: 0.3em;
}
.rtl .languages-step .form-field .button{
margin-left: 0;
margin-right: 15px;
}
.rtl .languages-step .form-field .button > span{
margin-left: 0.3em;
margin-right: 0;
}
.pll-wizard-content .languages-step .select-language-field{
display: flex;
}
.pll-wizard-content #languages{
display: none;
}
.pll-wizard-content #languages tr th:first-child{
width: 80%;
}
.pll-wizard-content #languages .dashicons{
color: #a03f3f;
}
.pll-wizard-content #languages img{
margin-right: 5px;
}
.pll-wizard-content .error{
color: #a03f3f;
font-weight: bold;
}
.pll-wizard-content #messages .error{
background: #fccfcf;
padding: 0.5rem;
border: 1px solid #a03f3f;
margin-bottom: 0.5rem;
}
.pll-wizard-content #slide-toggle{
position:absolute;
opacity: 0;
}
.pll-wizard-content #slide-toggle + label{
position:relative;
}
.pll-wizard-content #slide-toggle + label + span{
display: block;
}
.pll-wizard-content #slide-toggle + label .dashicons{
margin-right: 0.3em;
}
.rtl .pll-wizard-content #slide-toggle + label .dashicons{
margin-left: 0.3em;
margin-right: 0;
}
.pll-wizard-content #slide-toggle ~ #screenshot > img {
max-height: 500px;
margin-top: 10px;
-webkit-transition: all .5s cubic-bezier(0, 1, 0.5, 1);
transition: all .5s cubic-bezier(0, 1, 0.5, 1);
}
.pll-wizard-content #slide-toggle:checked ~ #screenshot > img {
max-height: 0;
}
.hide {
display: none;
}
input[type="text"].field-in-error,
input[type="password"].field-in-error,
input[type="checkbox"].field-in-error,
input[type="color"].field-in-error,
input[type="date"].field-in-error,
input[type="datetime"].field-in-error,
input[type="datetime-local"].field-in-error,
input[type="email"].field-in-error,
input[type="month"].field-in-error,
input[type="number"].field-in-error,
input[type="search"].field-in-error,
input[type="radio"].field-in-error,
input[type="tel"].field-in-error,
input[type="text"].field-in-error,
input[type="time"].field-in-error,
input[type="url"].field-in-error,
input[type="week"].field-in-error,
select.field-in-error,
textarea.field-in-error,
span.field-in-error,
.field-in-error{
border-color: #a03f3f;
}
input[type="text"].field-in-error:focus,
input[type="password"].field-in-error:focus,
input[type="checkbox"].field-in-error:focus,
input[type="color"].field-in-error:focus,
input[type="date"].field-in-error:focus,
input[type="datetime"].field-in-error:focus,
input[type="datetime-local"].field-in-error:focus,
input[type="email"].field-in-error:focus,
input[type="month"].field-in-error:focus,
input[type="number"].field-in-error:focus,
input[type="search"].field-in-error:focus,
input[type="radio"].field-in-error:focus,
input[type="tel"].field-in-error:focus,
input[type="text"].field-in-error:focus,
input[type="time"].field-in-error:focus,
input[type="url"].field-in-error:focus,
input[type="week"].field-in-error:focus,
select.field-in-error:focus,
textarea.field-in-error:focus,
span.field-in-error:focus,
.field-in-error:focus{
border: 1px solid #a03f3f;
box-shadow: 0 0 2px rgba(160, 63, 63, 0.8);
outline-color: #a03f3f;
outline-style: auto;
outline-width: thin;
}
/* override install styles by returning back to forms styles */
.form-table input.regular-text{
width: 25em;
}
.form-table input.field-in-error{
border-color: #a03f3f;
}
#pll-licenses-table td{
padding: 10px 9px;
}
#pll-licenses-table .license-valid td p{
min-width: 35em;
}
#pll-licenses-table .pll-deactivate-license{
margin: 0 0 0 20px;
}
.rtl #pll-licenses-table .pll-deactivate-license{
margin: 0 10px 0 0;
}
.pll-wizard-content .documentation {
padding: 24px 24px 0;
margin: 0 0 24px;
overflow: hidden;
background: #f5f5f5
}
.pll-wizard-content .documentation p {
padding: 0;
margin: 0 0 12px;
}
.documentation-container {
display: -webkit-box;
display: -webkit-flex;
display: flex;
justify-content: flex-end;
}
.documentation-container .documentation-button-container {
-webkit-box-flex: 0;
-webkit-flex-grow: 0;
flex-grow: 0;
}
.wc-setup .wc-setup-actions .button.documentation-button {
height: 42px;
padding: 0 1em;
margin: 0;
}
#dialog{
display: none;
}
.pll-wizard .ui-dialog.ui-widget-content{
max-height: none;
}
.pll-wizard .ui-dialog-title::before{
content: "\f534";
font-family: dashicons;
display: inline-block;
line-height: 1;
font-weight: 400;
font-style: normal;
speak: none;
text-decoration: inherit;
text-transform: none;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
width: 20px;
height: 20px;
font-size: 20px;
vertical-align: middle;
text-align: center;
margin: 0 5px 5px 0;
transition: color 0.1s ease-in;
}
.rtl.pll-wizard .ui-dialog-title::before{
margin-right: 0;
margin-left: 5px;
}
.pll-wizard .ui-dialog ul{
list-style: disc;
padding-left: 20px;
}
.rtl.pll-wizard .ui-dialog ul{
padding-left: 0;
padding-right: 20px;
}
.pll-wizard li{
margin-bottom: 0;
}
#translations{
border-collapse: collapse;
}
#translations tbody:nth-child(odd){
background-color: #f9f9f9;
}
#translations.striped > tbody > :nth-child(odd) {
background-color: transparent; /* Override common WordPress style */
}
.pll-wizard-content mark{
background: transparent none;
}
.pll-wizard-content mark{
color: #7ad03a;
}
@media screen and (max-width: 782px) {
/* Override WordPress button css rules */
.languages-step .form-field .button{
font-size: 13px;
line-height: 26px;
height: 28px;
padding: 0 10px 1px;
vertical-align: top;
}
#pll-licenses-table .pll-deactivate-license{
margin: 10px 0 5px;
}
}
@media only screen and (max-width:620px) {
/* Override dialog width rule */
.ui-dialog{
width: 100% !important;
}
}
@media only screen and (max-width:500px) {
#pll-logo a,
.select-language-field{
flex-direction: column;
}
.select-language-field .action-buttons{
display: flex;
justify-content: flex-end;
}
.languages-step .form-field .button{
margin: 5px 0 0;
}
}
@media only screen and (max-width:400px) {
#pll-logo {
font-size: 56px;
}
.pll-wizard-steps {
display: none
}
.pll-wizard-service-item {
-webkit-flex-wrap: wrap;
flex-wrap: wrap
}
.pll-wizard-service-item .pll-wizard-service-enable {
-webkit-box-ordinal-group: 3;
-webkit-order: 2;
order: 2;
padding: 20px 0 0
}
.pll-wizard-service-item .pll-wizard-service-description {
-webkit-box-ordinal-group: 4;
-webkit-order: 3;
order: 3
}
.pll-wizard-service-item .pll-wizard-service-name {
padding: 20px 20px 0;
text-align: left;
-webkit-box-pack: justify!important;
-webkit-justify-content: space-between!important;
justify-content: space-between!important
}
.pll-wizard-service-item .pll-wizard-service-name img {
margin: 0
}
.pll-wizard-next-steps .pll-wizard-next-step-item {
-webkit-flex-wrap: wrap;
flex-wrap: wrap
}
.pll-wizard-next-steps .pll-wizard-next-step-item .pll-wizard-next-step-description {
margin-bottom: 0
}
.pll-wizard-next-steps .pll-wizard-next-step-item .pll-wizard-next-step-action p {
margin: 0
}
}
@media only screen and (max-width:360px) {
#pll-logo {
font-size: 48px;
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Displays the wizard notice content
*
* @package Polylang
*
* @since 2.7
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly.
}
$wizard_url = add_query_arg(
array(
'page' => 'mlang_wizard',
),
admin_url( 'admin.php' )
);
?>
<p>
<strong>
<?php
printf(
/* translators: %s is the plugin name */
esc_html__( 'Welcome to %s', 'polylang' ),
esc_html( POLYLANG )
);
?>
</strong>
<?php
echo ' &#8211; ';
esc_html_e( 'You&lsquo;re almost ready to translate your contents!', 'polylang' );
?>
</p>
<p class="buttons">
<a
href="<?php echo esc_url( $wizard_url ); ?>"
class="button button-primary"
>
<?php esc_html_e( 'Run the Setup Wizard', 'polylang' ); ?>
</a>
<a
class="button button-secondary skip"
href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'pll-hide-notice', 'wizard' ), 'wizard', '_pll_notice_nonce' ) ); ?>"
>
<?php esc_html_e( 'Skip setup', 'polylang' ); ?>
</a>
</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 B

View File

@@ -0,0 +1,302 @@
/**
* @package Polylang
*/
jQuery(
function ( $ ) {
var addLanguageForm = $( '.languages-step' ); // Form element.
var languageFields = $( '#language-fields' ); // Element where to append hidden fields for creating language.
var languagesTable = $( '#languages' ); // Table element contains languages list to create.
var languagesListTable = $( '#languages tbody' ); // Table rows with languages list to create.
var definedLanguagesListTable = $( '#defined-languages tbody' ); // Table rows with already defined languages list.
var languagesList = $( '#lang_list' ); // Select form element with predefined languages without already created languages.
var nextStepButton = $( '[name="save_step"]' ); // The button for continuing to the next step.
var messagesContainer = $( '#messages' ); // Element where to display error messages.
var languagesMap = new Map(); // Languages map object for managing the languages to create.
var dialog = $( '#dialog' ); // Dialog box for alerting the language selected has not been added to the list.
/**
* Add a language in the list to create it in Polylang settings
*
* @param {object} language The language object
*/
function addLanguage( language ) {
// language properties come from the select dropdown which is built server side and well escaped.
// see template view-wizard-step-languages.php.
var languageValueHtml = $( '<td />' ).text( language.text ).prepend( language.flagUrl ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
var languageTrashIconHtml = $( '<td />' )
.append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
$( '<span />' )
.addClass( 'dashicons dashicons-trash' )
.attr( 'data-language', language.locale )
.append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
$( '<span />' )
.addClass( 'screen-reader-text' )
.text( pll_wizard_params.i18n_remove_language_icon )
)
);
// see the comment and the hardcoded code above. languageTrashIconHtml and languageValueHtml are safe.
var languageLineHtml = $( '<tr />' ).prepend( languageTrashIconHtml ).prepend( languageValueHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
var languageFieldHtml = $( '<input />' ).attr(
{
type: 'hidden',
name: 'languages[]'
}
).val( language.locale );
languagesList.val( '' );
languagesList.selectmenu( 'refresh' ); // Refresh jQuery selectmenu widget after changing the value.
languagesMap.set( language.locale, language );
// see above how languageLineHtml is built.
languagesListTable.append( languageLineHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
// Bind click event on trash icon.
languagesListTable.on(
'click',
'span[data-language=' + language.locale + ']',
function ( event ) {
event.preventDefault();
// Remove line in languages table.
$( this ).parents( 'tr' ).remove();
// Remove input field.
var languageField = languageFields.children( 'input[value=' + $( this ).data( 'language' ) + ']' ).remove();
// If there is no more languages hide languages table.
if ( languagesListTable.children().length <= 0 ) {
languagesTable.hide();
}
// Remove language from the Map.
languagesMap.delete( $( this ).data( 'language' ) );
// Hide error message.
hideError();
}
);
// see above how languageFieldHtml is built.
// Add hidden input field for posting the form.
languageFields.append( languageFieldHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
}
/**
* Display an error message
*
* @param {string} message The message to display
*/
function showError( message ) {
messagesContainer.empty();
// html is hardcoded and use of jQuery text method which is safe to add message value.
// In addition message is i18n value which is initialized server side in PLL_Wizard::add_step_languages and correctly escaped.
messagesContainer.prepend( $( '<p/>' ).addClass( 'error' ).text( message ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
}
/**
* Hide all error messages and fields in error
*/
function hideError() {
messagesContainer.empty();
addLanguageForm.find( '.error' ).removeClass( 'error field-in-error' );
}
/**
* Style the field to indicate where the error is
*
* @param {object} field The jQuery element which is in error
*/
function showFieldInError( field ) {
field.addClass( 'error field-in-error' );
}
/**
* Focus on a specific element
*
* @param {object} field The jQuery element which will be focused
*/
function focusOnField( field ) {
field.trigger( 'focus' );
}
/**
* Disable a specific button
*
* @param {object} button
*/
function disableButton( button ){
button.prop( 'disabled', true );
// Because the button is disabled we need to add the value of the button to ensure it will pass in the request.
addLanguageForm.append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
$( '<input />' ).prop(
{
type: 'hidden',
name: button.prop( 'name' ),
value: button.prop( 'value' )
}
)
);
}
/**
* Remove error when a new selection is done in languages list.
*/
languagesList.on(
'selectmenuchange',
function () {
hideError();;
}
);
/**
* Bind click event on "Add language" button
*/
$( '#add-language' ).on(
'click',
function ( event ) {
hideError();
var selectedOption = event.currentTarget.form.lang_list.options[event.currentTarget.form.lang_list.selectedIndex];
if ( '' !== selectedOption.value && ! languagesMap.has( selectedOption.value ) ) {
addLanguage(
{
locale: selectedOption.value,
text: selectedOption.innerText,
name: $( selectedOption ).data( 'language-name' ),
flagUrl: $( selectedOption ).data( 'flag-html' )
}
);
// Show table of languages.
languagesTable.show();
// Put back the focus on the select language field after clicking on "Add language button".
focusOnField( $( '#lang_list-button' ) );
} else {
var message = pll_wizard_params.i18n_no_language_selected;
if ( languagesMap.has( selectedOption.value ) ) {
message = pll_wizard_params.i18n_language_already_added;
}
showError( message );
showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
focusOnField( $( '#lang_list-button' ) );
}
}
);
/**
* Bind submit event on "add_lang" form
*/
addLanguageForm.on(
'submit',
function ( event ) {
// Verify if there is at least one language.
var isLanguagesAlreadyDefined = definedLanguagesListTable.children().length > 0;
var selectedLanguage = $( '#lang_list' ).val();
if ( languagesMap.size <= 0 && ! isLanguagesAlreadyDefined ) {
if ( '' === selectedLanguage ) {
showError( pll_wizard_params.i18n_no_language_added );
showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
focusOnField( $( '#lang_list-button' ) );
} else {
showError( pll_wizard_params.i18n_add_language_needed );
showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
focusOnField( $( '#add-language' ) ); // Put the focus on the "Add language" button.
}
return false;
}
// Verify if the language has been added in the list otherwise display a dialog box to confirm what to do.
if ( '' !== selectedLanguage ) {
// Verify we don't add a duplicate language before opening the dialog box otherwise display an error message.
if ( ! languagesMap.has( selectedLanguage ) ) {
dialog.dialog( 'open' );
} else {
showError( pll_wizard_params.i18n_language_already_added );
showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
focusOnField( $( '#lang_list-button' ) );
}
return false;
}
disableButton( nextStepButton );
}
);
// Is there an error return by PHP ?
var searchParams = new URLSearchParams( document.location.search );
if ( searchParams.has( 'activate_error' ) ) {
// If the error code exists, display it.
if ( undefined !== pll_wizard_params[ searchParams.get( 'activate_error' ) ] ) {
showError( pll_wizard_params[ searchParams.get( 'activate_error' ) ] );
}
}
function confirmDialog( what ) {
switch ( what ) {
case 'yes':
var selectedOption = $( '#lang_list' ).children( ':selected' );
addLanguage(
{
locale: selectedOption[0].value,
text: selectedOption[0].innerText,
name: $( selectedOption ).data( 'language-name' ),
flagUrl: $( selectedOption ).data( 'flag-html' )
}
);
break;
case 'no':
// Empty select form field and submit again the form.
languagesList.val( '' );
break;
case 'ignore':
}
dialog.dialog( 'close' );
if ( 'ignore' === what ) {
focusOnField( $( '#lang_list-button' ) );
} else {
addLanguageForm.submit();
}
}
// Initialize dialog box in the case a language is selected but not added in the list.
dialog.dialog(
{
autoOpen: false,
modal: true,
draggable: false,
resizable: false,
title: pll_wizard_params.i18n_dialog_title,
minWidth: 600,
maxWidth: '100%',
open: function ( event, ui ) {
// Change dialog box position for rtl language
if ( $( 'body' ).hasClass( 'rtl' ) ) {
$( this ).parent().css(
{
right: $( this ).parent().css( 'left' ),
left: 'auto'
}
);
}
// Display language name and flag information in dialog box.
$( this ).find( '#dialog-language' ).text( $( '#lang_list' ).children( ':selected' ).first().text() );
// language properties come from the select dropdown #lang_list which is built server side and well escaped.
// see template view-wizard-step-languages.php.
$( this ).find( '#dialog-language-flag' ).empty().prepend( $( '#lang_list' ).children( ':selected' ).data( 'flag-html' ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
},
buttons: [
{
text: pll_wizard_params.i18n_dialog_yes_button,
click: function ( event ) {
confirmDialog( 'yes' );
}
},
{
text: pll_wizard_params.i18n_dialog_no_button,
click: function ( event ) {
confirmDialog( 'no' );
}
},
{
text: pll_wizard_params.i18n_dialog_ignore_button,
click: function ( event ) {
confirmDialog( 'ignore' );
}
}
]
}
)
}
);

View File

@@ -0,0 +1,14 @@
<?php
/**
* Loads the setup wizard.
*
* @package Polylang
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly
}
if ( $polylang instanceof PLL_Admin_Base ) {
$polylang->wizard = new PLL_Wizard( $polylang );
}

View File

@@ -0,0 +1,108 @@
<?php
/**
* Displays the wizard
*
* @package Polylang
*
* @since 2.7
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly.
}
$admin_body_class = array( 'pll-wizard', 'wp-core-ui' );
if ( is_rtl() ) {
$admin_body_class[] = 'rtl';
}
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>
<?php
printf(
/* translators: %s is the plugin name */
esc_html__( '%s &rsaquo; Setup', 'polylang' ),
esc_html( POLYLANG )
);
?>
</title>
<script>
var ajaxurl = '<?php echo esc_url( admin_url( 'admin-ajax.php', 'relative' ) ); ?>';
</script>
<?php do_action( 'admin_enqueue_scripts' ); ?>
<?php wp_print_scripts( $this->steps[ $this->step ]['scripts'] ); ?>
<?php wp_print_styles( array_merge( $this->styles, $this->steps[ $this->step ]['styles'] ) ); ?>
<?php do_action( 'admin_head' ); ?>
</head>
<body class="<?php echo join( ' ', array_map( 'sanitize_key', $admin_body_class ) ); ?>">
<h1 id="pll-logo">
<a href="https://polylang.pro/" class="title">
<span><img src="<?php echo esc_url( plugins_url( '/modules/wizard/images/polylang-logo.png', POLYLANG_FILE ) ); ?>" /></span>
<?php echo esc_html( POLYLANG ); ?>
</a>
</h1>
<ol class="pll-wizard-steps">
<?php
foreach ( $this->steps as $step_key => $step ) {
$is_completed = array_search( $this->step, array_keys( $this->steps ), true ) > array_search( $step_key, array_keys( $this->steps ), true );
if ( $step_key === $this->step ) {
?>
<li class="active"><?php echo esc_html( $step['name'] ); ?></li>
<?php
} elseif ( $is_completed ) {
?>
<li class="done">
<a
href="<?php echo esc_url( add_query_arg( 'step', $step_key, remove_query_arg( 'activate_error' ) ) ); ?>"
>
<?php echo esc_html( $step['name'] ); ?>
</a>
</li>
<?php
} else {
?>
<li><?php echo esc_html( $step['name'] ); ?></li>
<?php
}
}
?>
</ol>
<div class="pll-wizard-content">
<form method="post" class="<?php echo esc_attr( "{$this->step}-step" ); ?>">
<?php
wp_nonce_field( 'pll-wizard', '_pll_nonce' );
if ( ! empty( $this->steps[ $this->step ]['view'] ) ) {
call_user_func( $this->steps[ $this->step ]['view'], $this );
}
?>
<?php if ( 'last' !== $this->step ) : ?>
<p class="pll-wizard-actions step">
<button
type="submit"
class="button-primary button button-large button-next"
value="continue"
name="save_step"
>
<?php esc_html_e( 'Continue', 'polylang' ); ?><span class="dashicons dashicons-arrow-right-alt2"></span>
</button>
</p>
<?php endif; ?>
</form>
</div>
<div class="pll-wizard-footer">
<?php if ( 'last' !== $this->step ) : ?>
<a
class="pll-wizard-footer-links"
href="<?php echo esc_url( admin_url() ); ?>"
>
<?php esc_html_e( 'Not right now', 'polylang' ); ?>
</a>
<?php endif; ?>
</div>
</body>
</html>

View File

@@ -0,0 +1,135 @@
<?php
/**
* Displays the wizard home page step
*
* @package Polylang
*
* @since 2.7
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly.
}
$languages = $this->model->get_languages_list();
$default_language = count( $languages ) > 0 ? $this->options['default_lang'] : null;
$home_page_id = get_option( 'page_on_front' );
$translations = $this->model->post->get_translations( $home_page_id );
$untranslated_languages = array();
$home_page = $home_page_id > 0 ? get_post( $home_page_id ) : null;
$home_page_language = $this->model->post->get_language( $home_page_id );
foreach ( $languages as $language ) {
if ( ! $this->model->post->get( $home_page_id, $language ) ) {
$untranslated_languages[] = $language;
}
}
?>
<input type="hidden" name="home_page" value="<?php echo esc_attr( $home_page->ID ); ?>" />
<input type="hidden" name="home_page_title" value="<?php echo esc_attr( $home_page->post_title ); ?>" />
<?php if ( false !== $home_page_language ) : ?>
<input type="hidden" name="home_page_language" value="<?php echo esc_attr( $home_page_language->slug ); ?>" />
<?php endif; ?>
<h2><?php esc_html_e( 'Homepage', 'polylang' ); ?></h2>
<p>
<?php
printf(
/* translators: %s is the post title of the front page */
esc_html__( 'You defined this page as your static homepage: %s.', 'polylang' ),
'<strong>' . esc_html( $home_page->post_title ) . '</strong>'
);
?>
<br />
<?php
printf(
/* translators: %s is the language of the front page ( flag, native name and locale ) */
esc_html__( 'Its language is : %s.', 'polylang' ),
$home_page_language->flag . ' <strong>' . esc_html( $home_page_language->name ) . ' ' . esc_html( $home_page_language->locale ) . '</strong>' //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
)
?>
<br />
<?php esc_html_e( 'For your site to work correctly, this page must be translated in all available languages.', 'polylang' ); ?>
</p>
<p>
<?php esc_html_e( 'After the pages is created, it is up to you to put the translated content in each page linked to each language.', 'polylang' ); ?>
</p>
<?php if ( $translations ) : ?>
<table id="translated-languages" class="striped">
<thead>
<tr>
<th><?php esc_html_e( 'Your static homepage is already translated in', 'polylang' ); ?></th>
</tr>
</thead>
<tbody>
<?php
foreach ( array_keys( $translations ) as $lang ) {
$language = $this->model->get_language( $lang );
?>
<tr>
<td>
<?php
echo $language->flag; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo ' ' . esc_html( $language->name ) . ' ' . esc_html( $language->locale ) . ' ';
?>
<?php if ( $language->is_default ) : ?>
<span class="icon-default-lang">
<span class="screen-reader-text">
<?php esc_html_e( 'Default language', 'polylang' ); ?>
</span>
</span>
<?php endif; ?>
<input type="hidden" name="translated_languages[]" value="<?php echo esc_attr( $language->slug ); ?>" />
</td>
</tr>
<?php
}
?>
</tbody>
</table>
<?php endif; ?>
<table id="untranslated-languages" class="striped">
<?php if ( ! is_null( $default_language ) ) : ?>
<caption><span class="icon-default-lang"></span> <?php esc_html_e( 'Default language', 'polylang' ); ?></caption>
<?php endif; ?>
<thead>
<?php if ( count( $untranslated_languages ) >= 1 ) : ?>
<tr>
<th><?php esc_html_e( 'We are going to prepare this page in', 'polylang' ); ?></th>
</tr>
<?php elseif ( false !== $home_page_language && count( $untranslated_languages ) <= 0 ) : ?>
<tr>
<th>
<span class="dashicons dashicons-info"></span>
<?php esc_html_e( 'One language is well defined and assigned to your home page.', 'polylang' ); ?>
</th>
</tr>
<tr>
<td><?php esc_html_e( "If you add a new language, don't forget to translate your homepage.", 'polylang' ); ?></td>
</tr>
<?php endif; ?>
</thead>
<tbody>
<?php
foreach ( $untranslated_languages as $lg ) {
?>
<tr>
<td>
<?php
echo $lg->flag; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo ' ' . esc_html( $lg->name ) . ' ' . esc_html( $lg->locale ) . ' ';
?>
<?php if ( $lg->is_default ) : ?>
<span class="icon-default-lang">
<span class="screen-reader-text">
<?php esc_html_e( 'Default language', 'polylang' ); ?>
</span>
</span>
<?php endif; ?>
<input type="hidden" name="untranslated_languages[]" value="<?php echo esc_attr( $lg->slug ); ?>" />
</td>
</tr>
<?php
}
?>
</tbody>
</table>

View File

@@ -0,0 +1,136 @@
<?php
/**
* Displays the wizard languages step
*
* @package Polylang
*
* @since 2.7
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly.
}
$existing_languages = $this->model->get_languages_list();
$default_language = count( $existing_languages ) > 0 ? $this->options['default_lang'] : null;
$languages_list = array_diff_key(
PLL_Settings::get_predefined_languages(),
wp_list_pluck( $existing_languages, 'locale', 'locale' )
);
?>
<div id="language-fields"></div>
<p class="languages-setup">
<?php esc_html_e( 'This wizard will help you configure your Polylang settings, and get you started quickly with your multilingual website.', 'polylang' ); ?>
</p>
<p class="languages-setup">
<?php esc_html_e( 'First we are going to define the languages that you will use on your website.', 'polylang' ); ?>
</p>
<h2><?php esc_html_e( 'Languages', 'polylang' ); ?></h2>
<div id="messages">
</div>
<div class="form-field">
<label for="lang_list"><?php esc_html_e( 'Select a language to be added', 'polylang' ); ?></label>
<div class="select-language-field">
<select name="lang_list" id="lang_list">
<option value=""></option>
<?php
foreach ( $languages_list as $language ) {
printf(
'<option value="%1$s" data-flag-html="%3$s" data-language-name="%2$s" >%2$s - %1$s</option>' . "\n",
esc_attr( $language['locale'] ),
esc_attr( $language['name'] ),
esc_attr( PLL_Language::get_predefined_flag( $language['flag'] ) )
);
}
?>
</select>
<div class="action-buttons">
<button type="button"
class="button-primary button"
value="<?php esc_attr_e( 'Add new language', 'polylang' ); ?>"
id="add-language"
name="add-language"
>
<span class="dashicons dashicons-plus"></span><?php esc_html_e( 'Add new language', 'polylang' ); ?>
</button>
</div>
</div>
</div>
<table id="languages" class="striped">
<thead>
<tr>
<th><?php esc_html_e( 'Language', 'polylang' ); ?></th>
<th><?php esc_html_e( 'Remove', 'polylang' ); ?></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<table id="defined-languages" class="striped<?php echo empty( $existing_languages ) ? ' hide' : ''; ?>">
<?php if ( ! is_null( $default_language ) ) : ?>
<caption><span class="icon-default-lang"></span> <?php esc_html_e( 'Default language', 'polylang' ); ?></caption>
<?php endif; ?>
<thead>
<tr>
<th><?php esc_html_e( 'Languages already defined', 'polylang' ); ?></th>
</tr>
</thead>
<tbody>
<?php
foreach ( $existing_languages as $lg ) {
printf(
'<tr><td>%3$s<span>%2$s - %1$s</span> %4$s</td></tr>' . "\n",
esc_attr( $lg->locale ),
esc_html( $lg->name ),
$lg->flag, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$lg->is_default ? ' <span class="icon-default-lang"><span class="screen-reader-text">' . esc_html__( 'Default language', 'polylang' ) . '</span></span>' : ''
);
}
?>
</tbody>
</table>
<div id="dialog">
<p>
<?php
printf(
/* translators: %1$s: is a language flag image, %2$s: is a language native name */
esc_html__( 'You selected %1$s %2$s but you didn\'t add it to the list before continuing to the next step.', 'polylang' ),
'<span id="dialog-language-flag"></span>',
'<strong id="dialog-language"></strong>'
);
?>
</p>
<p>
<?php esc_html_e( 'Do you want to add this language before continuing to the next step?', 'polylang' ); ?>
</p>
<ul>
<li>
<?php
printf(
/* translators: %s: is the translated label of the 'Yes' button */
esc_html__( '%s: add this language and continue to the next step', 'polylang' ),
'<strong>' . esc_html__( 'Yes', 'polylang' ) . '</strong >'
);
?>
</li>
<li>
<?php
printf(
/* translators: %s: is the translated label of the 'No' button */
esc_html__( "%s: don't add this language and continue to the next step", 'polylang' ),
'<strong>' . esc_html__( 'No', 'polylang' ) . '</strong >'
);
?>
</li>
<li>
<?php
printf(
/* translators: %s: is the translated label of the 'Ignore' button */
esc_html__( '%s: stay at this step', 'polylang' ),
'<strong>' . esc_html__( 'Ignore', 'polylang' ) . '</strong >'
);
?>
</li>
</ul>
</div>

View File

@@ -0,0 +1,114 @@
<?php
/**
* Displays the wizard last step
*
* @package Polylang
*
* @since 2.7
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly.
}
?>
<h2><?php esc_html_e( "You're ready to translate your contents!", 'polylang' ); ?></h2>
<div class="documentation">
<p><?php esc_html_e( "You're now able to translate your contents such as posts, pages, categories and tags. You can learn how to use Polylang by reading the documentation.", 'polylang' ); ?></p>
<div class="documentation-container">
<p class="pll-wizard-actions step documentation-button-container">
<a
class="button button-primary button-large documentation-button"
href="<?php echo esc_url( 'https://polylang.pro/doc-category/getting-started/' ); ?>"
target="blank"
>
<?php esc_html_e( 'Read documentation', 'polylang' ); ?>
</a>
</p>
</div>
</div>
<ul class="pll-wizard-next-steps">
<li class="pll-wizard-next-step-item">
<div class="pll-wizard-next-step-description">
<p class="next-step-heading"><?php esc_html_e( 'Next step', 'polylang' ); ?></p>
<h3 class="next-step-description"><?php esc_html_e( 'Create menus', 'polylang' ); ?></h3>
<p class="next-step-extra-info">
<?php esc_html_e( 'To get your website ready, there are still two steps you need to perform manually: add menus in each language, and add a language switcher to allow your visitors to select their preferred language.', 'polylang' ); ?>
</p>
</div>
<div class="pll-wizard-next-step-action">
<p class="pll-wizard-actions step">
<a class="button button-primary button-large" href="<?php echo esc_url( 'https://polylang.pro/doc/create-menus/' ); ?>">
<?php esc_html_e( 'Read documentation', 'polylang' ); ?>
</a>
</p>
</div>
</li>
<li class="pll-wizard-next-step-item">
<div class="pll-wizard-next-step-description">
<p class="next-step-heading"><?php esc_html_e( 'Next step', 'polylang' ); ?></p>
<h3 class="next-step-description"><?php esc_html_e( 'Translate some pages', 'polylang' ); ?></h3>
<p class="next-step-extra-info"><?php esc_html_e( "You're ready to translate the posts on your website.", 'polylang' ); ?></p>
</div>
<div class="pll-wizard-next-step-action">
<p class="pll-wizard-actions step">
<a class="button button-large" href="<?php echo esc_url( admin_url( 'edit.php?post_type=page' ) ); ?>">
<?php esc_html_e( 'View pages', 'polylang' ); ?>
</a>
</p>
</div>
</li>
<?php if ( ! defined( 'POLYLANG_PRO' ) && ! defined( 'WOOCOMMERCE_VERSION' ) ) : ?>
<li class="pll-wizard-next-step-item">
<div class="pll-wizard-next-step-description">
<p class="next-step-heading"><?php esc_html_e( 'Polylang Pro', 'polylang' ); ?></p>
<h3 class="next-step-description"><?php esc_html_e( 'Upgrade to Polylang Pro', 'polylang' ); ?></h3>
<p class="next-step-extra-info">
<?php esc_html_e( 'Thank you for activating Polylang. If you want more advanced features - duplication, synchronization, REST API support, integration with other plugins, etc. - or further help provided by our Premium support, we recommend you upgrade to Polylang Pro.', 'polylang' ); ?>
</p>
</div>
<div class="pll-wizard-next-step-action">
<p class="pll-wizard-actions step">
<a class="button button-primary button-large" href="<?php echo esc_url( 'https://polylang.pro/downloads/polylang-pro/' ); ?>">
<?php esc_html_e( 'Buy now', 'polylang' ); ?>
</a>
</p>
</div>
</li>
<?php endif; ?>
<?php if ( ! defined( 'POLYLANG_PRO' ) && defined( 'WOOCOMMERCE_VERSION' ) && ! defined( 'PLLWC_VERSION' ) ) : ?>
<li class="pll-wizard-next-step-item">
<div class="pll-wizard-next-step-description">
<p class="next-step-heading"><?php esc_html_e( 'WooCommerce', 'polylang' ); ?></p>
<h3 class="next-step-description"><?php esc_html_e( 'Purchase Polylang Business Pack', 'polylang' ); ?></h3>
<p class="next-step-extra-info">
<?php
printf(
/* translators: %s is the name of Polylang Business Pack product */
esc_html__( 'We have noticed that you are using Polylang with WooCommerce. To ensure a better compatibility, we recommend you use %s which includes both Polylang Pro and Polylang For WooCommerce.', 'polylang' ),
'<strong>' . esc_html__( 'Polylang Business Pack', 'polylang' ) . '</strong>'
);
?>
</p>
</div>
<div class="pll-wizard-next-step-action">
<p class="pll-wizard-actions step">
<a class="button button-primary button-large" href="<?php echo esc_url( 'https://polylang.pro/downloads/polylang-for-woocommerce/' ); ?>">
<?php esc_html_e( 'Buy now', 'polylang' ); ?>
</a>
</p>
</div>
</li>
<?php endif; ?>
<li class="pll-wizard-additional-steps">
<div class="pll-wizard-next-step-action">
<p class="pll-wizard-actions step">
<a class="button button-large" href="<?php echo esc_url( admin_url() ); ?>">
<?php esc_html_e( 'Return to the Dashboard', 'polylang' ); ?>
</a>
</p>
</div>
</li>
</ul>

View File

@@ -0,0 +1,38 @@
<?php
/**
* Displays the wizard licenses step
*
* @package Polylang
*
* @since 2.7
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly.
}
$licenses = apply_filters( 'pll_settings_licenses', array() );
$is_error = isset( $_GET['activate_error'] ) && 'i18n_license_key_error' === sanitize_key( $_GET['activate_error'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
?>
<p>
<?php esc_html_e( 'You are using plugins which require a license key.', 'polylang' ); ?>
<?php echo esc_html( _n( 'Please enter your license key:', 'Please enter your license keys:', count( $licenses ), 'polylang' ) ); ?>
</p>
<h2><?php esc_html_e( 'Licenses', 'polylang' ); ?></h2>
<div id="messages">
<?php if ( $is_error ) : ?>
<p class="error"><?php esc_html_e( 'There is an error with a license key.', 'polylang' ); ?></p>
<?php endif; ?>
</div>
<div class="form-field">
<table id="pll-licenses-table" class="form-table pll-table-top">
<tbody>
<?php
foreach ( $licenses as $license ) {
// Escaping is already done in get_form_field method.
echo $license->get_form_field(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,62 @@
<?php
/**
* Displays the wizard media step
*
* @package Polylang
*
* @since 2.7
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly.
}
$default_options = PLL_Install::get_default_options();
$options = wp_parse_args( get_option( 'polylang' ), $default_options );
$media_support = $options['media_support'];
$help_screenshot = '/modules/wizard/images/media-screen' . ( is_rtl() ? '-rtl' : '' ) . '.png';
?>
<h2><?php esc_html_e( 'Media', 'polylang' ); ?></h2>
<p>
<?php esc_html_e( 'Polylang allows you to translate the text attached to your media, for example the title, the alternative text, the caption, or the description.', 'polylang' ); ?>
<?php esc_html_e( 'When you translate a media, the file is not duplicated on your disk, however you will see one entry per language in the media library.', 'polylang' ); ?>
<?php esc_html_e( 'When you want to insert media in a post, only the media in the language of the current post will be displayed.', 'polylang' ); ?>
</p>
<p>
<?php esc_html_e( 'You must activate media translation if you want to translate the title, the alternative text, the caption, or the description. Otherwise you can safely deactivate it.', 'polylang' ); ?>
</p>
<ul class="pll-wizard-services">
<li class="pll-wizard-service-item">
<div class="pll-wizard-service-enable">
<span class="pll-wizard-service-toggle">
<input
id="pll-wizard-service-media"
type="checkbox"
name="media_support"
value="yes" <?php checked( $media_support ); ?>
/>
<label for="pll-wizard-service-media" />
</span>
</div>
<div class="pll-wizard-service-description">
<p>
<?php esc_html_e( 'Allow Polylang to translate media', 'polylang' ); ?>
</p>
</div>
</li>
<li class="pll-wizard-service-item">
<div class="pll-wizard-service-example">
<p>
<input id="slide-toggle" type="checkbox" checked="checked">
<label for="slide-toggle" class="button button-primary button-small">
<span class="dashicons dashicons-visibility"></span><?php esc_html_e( 'Help', 'polylang' ); ?>
</label>
<span id="screenshot">
<img src="<?php echo esc_url_raw( esc_url( plugins_url( $help_screenshot, POLYLANG_FILE ) ) ); ?>" />
</span>
</p>
</div>
</li>
</ul>

View File

@@ -0,0 +1,37 @@
<?php
/**
* Displays the wizard unstranslated content step
*
* @package Polylang
*
* @since 2.7
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly.
}
$languages_list = $this->model->get_languages_list();
?>
<h2><?php esc_html_e( 'Content without language', 'polylang' ); ?></h2>
<p>
<?php esc_html_e( 'There are posts, pages, categories or tags without language.', 'polylang' ); ?><br />
<?php esc_html_e( 'For your site to work correctly, you need to assign a language to all your contents.', 'polylang' ); ?><br />
<?php esc_html_e( 'The selected language below will be applied to all your content without an assigned language.', 'polylang' ); ?>
</p>
<div class="form-field">
<label for="lang_list"><?php esc_html_e( 'Choose the language to be assigned', 'polylang' ); ?></label>
<select name="language" id="lang_list">
<?php
foreach ( $languages_list as $lg ) {
printf(
'<option value="%1$s" data-flag-html="%3$s" data-language-name="%2$s"%4$s>%2$s - %1$s</option>' . "\n",
esc_attr( $lg->locale ),
esc_html( $lg->name ),
esc_html( $lg->flag ),
$lg->is_default ? ' selected="selected"' : ''
);
}
?>
</select>
</div>

View File

@@ -0,0 +1,855 @@
<?php
/**
* @package Polylang
*/
/**
* Main class for Polylang wizard.
*
* @since 2.7
*/
class PLL_Wizard {
/**
* Reference to the model object
*
* @var PLL_Admin_Model
*/
protected $model;
/**
* Reference to the Polylang options array.
*
* @var array
*/
protected $options;
/**
* List of steps.
*
* @var array $steps {
* @type string $name I18n string which names the step.
* @type callable $view The callback function use to display the step content.
* @type callable $handler The callback function use to process the step after it is submitted.
* @type array $scripts List of scripts handle needed by the step.
* @type array $styles The list of styles handle needed by the step.
* }
*/
protected $steps = array();
/**
* The current step.
*
* @var string|null
*/
protected $step;
/**
* List of WordPress CSS file handles.
*
* @var string[]
*/
protected $styles = array();
/**
* Constructor
*
* @param object $polylang Reference to Polylang global object.
* @since 2.7
*/
public function __construct( &$polylang ) {
$this->options = &$polylang->options;
$this->model = &$polylang->model;
// Display Wizard page before any other action to ensure displaying it outside the WordPress admin context.
// Hooked on admin_init with priority 40 to ensure PLL_Wizard_Pro is correctly initialized.
add_action( 'admin_init', array( $this, 'setup_wizard_page' ), 40 );
// Add Wizard submenu.
add_filter( 'pll_settings_tabs', array( $this, 'settings_tabs' ), 10, 1 );
// Add filter to select screens where to display the notice.
add_filter( 'pll_can_display_notice', array( $this, 'can_display_notice' ), 10, 2 );
// Default steps.
add_filter( 'pll_wizard_steps', array( $this, 'add_step_licenses' ), 100 );
add_filter( 'pll_wizard_steps', array( $this, 'add_step_languages' ), 200 );
add_filter( 'pll_wizard_steps', array( $this, 'add_step_media' ), 300 );
add_filter( 'pll_wizard_steps', array( $this, 'add_step_untranslated_contents' ), 400 );
add_filter( 'pll_wizard_steps', array( $this, 'add_step_home_page' ), 500 );
add_filter( 'pll_wizard_steps', array( $this, 'add_step_last' ), 999 );
}
/**
* Save an activation transient when Polylang is activating to redirect to the wizard
*
* @since 2.7
*
* @param bool $network_wide if activated for all sites in the network.
* @return void
*/
public static function start_wizard( $network_wide ) {
$options = get_option( 'polylang' );
if ( wp_doing_ajax() || $network_wide || ! empty( $options ) ) {
return;
}
set_transient( 'pll_activation_redirect', 1, 30 );
}
/**
* Redirect to the wizard depending on the context
*
* @since 2.7
*
* @return void
*/
public function redirect_to_wizard() {
if ( get_transient( 'pll_activation_redirect' ) ) {
$do_redirect = true;
if ( ( isset( $_GET['page'] ) && 'mlang_wizard' === sanitize_key( $_GET['page'] ) || isset( $_GET['activate-multi'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
delete_transient( 'pll_activation_redirect' );
$do_redirect = false;
}
if ( $do_redirect ) {
wp_safe_redirect(
esc_url_raw(
add_query_arg(
array(
'page' => 'mlang_wizard',
),
admin_url( 'admin.php' )
)
)
);
exit;
}
}
}
/**
* Add an admin Polylang submenu to access the wizard
*
* @since 2.7
*
* @param string[] $tabs Submenus list.
* @return string[] Submenus list updated.
*/
public function settings_tabs( $tabs ) {
$tabs['wizard'] = esc_html__( 'Setup', 'polylang' );
return $tabs;
}
/**
* Returns true if the media step is displayable, false otherwise.
*
* @since 2.7
*
* @param PLL_Language[] $languages List of language objects.
* @return bool
*/
public function is_media_step_displayable( $languages ) {
$media = array();
// If there is no language or only one the media step is displayable.
if ( ! $languages || count( $languages ) < 2 ) {
return true;
}
foreach ( $languages as $language ) {
$media[ $language->slug ] = $this->model->count_posts(
$language,
array(
'post_type' => array( 'attachment' ),
'post_status' => 'inherit',
)
);
}
return count( array_filter( $media ) ) === 0;
}
/**
* Check if the licenses step is displayable
*
* @since 2.7
*
* @return bool
*/
public function is_licenses_step_displayable() {
$licenses = apply_filters( 'pll_settings_licenses', array() );
return count( $licenses ) > 0;
}
/**
* Setup the wizard page
*
* @since 2.7
*
* @return void
*/
public function setup_wizard_page() {
PLL_Admin_Notices::add_notice( 'wizard', $this->wizard_notice() );
$this->redirect_to_wizard();
if ( ! Polylang::is_wizard() ) {
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Sorry, you are not allowed to manage options for this site.', 'polylang' ) );
}
// Enqueue scripts and styles especially for the wizard.
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
$this->steps = apply_filters( 'pll_wizard_steps', $this->steps );
$step = isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification
$this->step = $step && array_key_exists( $step, $this->steps ) ? $step : current( array_keys( $this->steps ) );
$has_languages = $this->model->has_languages();
if ( ! $has_languages && ! in_array( $this->step, array( 'licenses', 'languages' ) ) ) {
wp_safe_redirect( esc_url_raw( $this->get_step_link( 'languages' ) ) );
exit;
}
if ( $has_languages && $this->model->get_objects_with_no_lang( 1 ) && ! in_array( $this->step, array( 'licenses', 'languages', 'media', 'untranslated-contents' ) ) ) {
wp_safe_redirect( esc_url_raw( $this->get_step_link( 'untranslated-contents' ) ) );
exit;
}
// Call the handler of the step for going to the next step.
// Be careful nonce verification with check_admin_referer must be done in each handler.
if ( ! empty( $_POST['save_step'] ) && isset( $this->steps[ $this->step ]['handler'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
call_user_func( $this->steps[ $this->step ]['handler'] );
}
$this->display_wizard_page();
// Ensure nothing is done after including the page.
exit;
}
/**
* Adds some admin screens where to display the wizard notice
*
* @since 2.7
*
* @param bool $can_display_notice Whether the notice can be displayed.
* @param string $notice The notice name.
* @return bool
*/
public function can_display_notice( $can_display_notice, $notice ) {
if ( ! $can_display_notice && 'wizard' === $notice ) {
$screen = get_current_screen();
$can_display_notice = ! empty( $screen ) && in_array(
$screen->base,
array(
'edit',
'upload',
'options-general',
)
);
}
return $can_display_notice;
}
/**
* Return html code of the wizard notice
*
* @since 2.7
*
* @return string
*/
public function wizard_notice() {
ob_start();
include __DIR__ . '/html-wizard-notice.php';
return ob_get_clean();
}
/**
* Display the wizard page
*
* @since 2.7
*
* @return void
*/
public function display_wizard_page() {
set_current_screen( 'pll-wizard' );
include __DIR__ . '/view-wizard-page.php';
}
/**
* Enqueue scripts and styles for the wizard
*
* @since 2.7
*
* @return void
*/
public function enqueue_scripts() {
wp_enqueue_style( 'polylang_admin', plugins_url( '/css/build/admin' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION );
wp_enqueue_style( 'pll-wizard', plugins_url( '/css/build/wizard' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common', 'forms' ), POLYLANG_VERSION );
$this->styles = array( 'polylang_admin', 'pll-wizard' );
}
/**
* Get the suffix to enqueue non minified files in a Debug context
*
* @since 2.7
*
* @return string Empty when SCRIPT_DEBUG equal to true
* otherwise .min
*/
public function get_suffix() {
return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
}
/**
* Get the URL for the step's screen.
*
* @since 2.7
*
* @param string $step slug (default: current step).
* @return string URL for the step if it exists.
* Empty string on failure.
*/
public function get_step_link( $step = '' ) {
if ( ! $step ) {
$step = $this->step;
}
$keys = array_keys( $this->steps );
$step_index = array_search( $step, $keys, true );
if ( false === $step_index ) {
return '';
}
return add_query_arg( 'step', $keys[ $step_index ], remove_query_arg( 'activate_error' ) );
}
/**
* Get the URL for the next step's screen.
*
* @since 2.7
*
* @param string $step slug (default: current step).
* @return string URL for next step if a next step exists.
* Admin URL if it's the last step.
* Empty string on failure.
*/
public function get_next_step_link( $step = '' ) {
if ( ! $step ) {
$step = $this->step;
}
$keys = array_keys( $this->steps );
if ( end( $keys ) === $step ) {
return admin_url();
}
$step_index = array_search( $step, $keys, true );
if ( false === $step_index ) {
return '';
}
return add_query_arg( 'step', $keys[ $step_index + 1 ], remove_query_arg( 'activate_error' ) );
}
/**
* Add licenses step to the wizard
*
* @since 2.7
*
* @param array $steps List of steps.
* @return array List of steps updated.
*/
public function add_step_licenses( $steps ) {
// Add ajax action on deactivate button in licenses step.
add_action( 'wp_ajax_pll_deactivate_license', array( $this, 'deactivate_license' ) );
// Be careful pll_admin script is enqueued here without dependency except jquery because only code useful for deactivate license button is needed.
// To be really loaded the script need to be passed to the $steps['licenses']['scripts'] array below with the same handle than in wp_enqueue_script().
wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery' ), POLYLANG_VERSION, true );
wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
if ( $this->is_licenses_step_displayable() ) {
$steps['licenses'] = array(
'name' => esc_html__( 'Licenses', 'polylang' ),
'view' => array( $this, 'display_step_licenses' ),
'handler' => array( $this, 'save_step_licenses' ),
'scripts' => array( 'pll_admin' ), // Polylang admin script used by deactivate license button.
'styles' => array(),
);
}
return $steps;
}
/**
* Display the languages step form
*
* @since 2.7
*
* @return void
*/
public function display_step_licenses() {
include __DIR__ . '/view-wizard-step-licenses.php';
}
/**
* Execute the languages step
*
* @since 2.7
*
* @return void
*/
public function save_step_licenses() {
check_admin_referer( 'pll-wizard', '_pll_nonce' );
$redirect = $this->get_next_step_link();
$licenses = apply_filters( 'pll_settings_licenses', array() );
foreach ( $licenses as $license ) {
if ( ! empty( $_POST['licenses'][ $license->id ] ) ) {
$updated_license = $license->activate_license( sanitize_key( $_POST['licenses'][ $license->id ] ) );
if ( ! empty( $updated_license->license_data ) && false === $updated_license->license_data->success ) {
// Stay on this step with an error.
$redirect = add_query_arg(
array(
'step' => $this->step,
'activate_error' => 'i18n_license_key_error',
)
);
}
}
}
wp_safe_redirect( esc_url_raw( $redirect ) );
exit;
}
/**
* Ajax method to deactivate a license
*
* @since 2.7
*
* @return void
*/
public function deactivate_license() {
check_ajax_referer( 'pll-wizard', '_pll_nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( -1 );
}
if ( ! isset( $_POST['id'] ) ) {
wp_die( 0 );
}
$id = substr( sanitize_text_field( wp_unslash( $_POST['id'] ) ), 11 );
$licenses = apply_filters( 'pll_settings_licenses', array() );
$license = $licenses[ $id ];
$license->deactivate_license();
wp_send_json(
array(
'id' => $id,
'html' => $license->get_form_field(),
)
);
}
/**
* Add languages step to the wizard
*
* @since 2.7
*
* @param array $steps List of steps.
* @return array List of steps updated.
*/
public function add_step_languages( $steps ) {
wp_deregister_script( 'pll_admin' ); // Deregister after the licenses step enqueue to update jquery-ui-selectmenu dependency.
// The wp-ajax-response and postbox dependencies is useless in wizard steps especially postbox which triggers a javascript error otherwise.
// To be really loaded the script need to be passed to the $steps['languages']['scripts'] array below with the same handle than in wp_enqueue_script().
wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
wp_register_script( 'pll-wizard-languages', plugins_url( '/js/build/languages-step' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'jquery-ui-dialog' ), POLYLANG_VERSION, true );
wp_localize_script(
'pll-wizard-languages',
'pll_wizard_params',
array(
'i18n_no_language_selected' => esc_html__( 'You need to select a language to be added.', 'polylang' ),
'i18n_language_already_added' => esc_html__( 'You already added this language.', 'polylang' ),
'i18n_no_language_added' => esc_html__( 'You need to add at least one language.', 'polylang' ),
'i18n_add_language_needed' => esc_html__( 'You selected a language, however, to be able to continue, you need to add it.', 'polylang' ),
'i18n_pll_add_language' => esc_html__( 'Impossible to add the language.', 'polylang' ),
'i18n_pll_invalid_locale' => esc_html__( 'Enter a valid WordPress locale', 'polylang' ),
'i18n_pll_invalid_slug' => esc_html__( 'The language code contains invalid characters', 'polylang' ),
'i18n_pll_non_unique_slug' => esc_html__( 'The language code must be unique', 'polylang' ),
'i18n_pll_invalid_name' => esc_html__( 'The language must have a name', 'polylang' ),
'i18n_pll_invalid_flag' => esc_html__( 'The flag does not exist', 'polylang' ),
'i18n_dialog_title' => esc_html__( "A language wasn't added.", 'polylang' ),
'i18n_dialog_yes_button' => esc_html__( 'Yes', 'polylang' ),
'i18n_dialog_no_button' => esc_html__( 'No', 'polylang' ),
'i18n_dialog_ignore_button' => esc_html__( 'Ignore', 'polylang' ),
'i18n_remove_language_icon' => esc_html__( 'Remove this language', 'polylang' ),
)
);
wp_enqueue_script( 'pll-wizard-languages' );
wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/build/selectmenu' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common', 'wp-jquery-ui-dialog' ), POLYLANG_VERSION );
$steps['languages'] = array(
'name' => esc_html__( 'Languages', 'polylang' ),
'view' => array( $this, 'display_step_languages' ),
'handler' => array( $this, 'save_step_languages' ),
'scripts' => array( 'pll-wizard-languages', 'pll_admin' ),
'styles' => array( 'pll-wizard-selectmenu' ),
);
return $steps;
}
/**
* Display the languages step form
*
* @since 2.7
*
* @return void
*/
public function display_step_languages() {
include __DIR__ . '/view-wizard-step-languages.php';
}
/**
* Execute the languages step
*
* @since 2.7
*
* @return void
*/
public function save_step_languages() {
check_admin_referer( 'pll-wizard', '_pll_nonce' );
$all_languages = include POLYLANG_DIR . '/settings/languages.php';
$languages = isset( $_POST['languages'] ) && is_array( $_POST['languages'] ) ? array_map( 'sanitize_locale_name', $_POST['languages'] ) : false;
$saved_languages = array();
// If there is no language added or defined.
if ( empty( $languages ) && ! $this->model->has_languages() ) {
// Stay on this step with an error.
wp_safe_redirect(
esc_url_raw(
add_query_arg(
array(
'step' => $this->step,
'activate_error' => 'i18n_no_language_added',
)
)
)
);
exit;
}
// Otherwise process the languages to add or skip the step if no language has been added.
if ( ! empty( $languages ) ) {
require_once ABSPATH . 'wp-admin/includes/translation-install.php';
// Remove duplicate values.
$languages = array_unique( $languages );
// For each language add it in Polylang settings.
foreach ( $languages as $locale ) {
$saved_languages = $all_languages[ $locale ];
$saved_languages['slug'] = $saved_languages['code'];
$saved_languages['rtl'] = (int) ( 'rtl' === $saved_languages['dir'] );
$saved_languages['term_group'] = 0; // Default term_group.
$language_added = $this->model->add_language( $saved_languages );
if ( $language_added instanceof WP_Error && array_key_exists( 'pll_non_unique_slug', $language_added->errors ) ) {
// Get the slug from the locale : lowercase and dash instead of underscore.
$saved_languages['slug'] = strtolower( str_replace( '_', '-', $saved_languages['locale'] ) );
$language_added = $this->model->add_language( $saved_languages );
}
if ( $language_added instanceof WP_Error ) {
// Stay on this step with an error.
$error_keys = array_keys( $language_added->errors );
wp_safe_redirect(
esc_url_raw(
add_query_arg(
array(
'step' => $this->step,
'activate_error' => 'i18n_' . reset( $error_keys ),
)
)
)
);
exit;
}
if ( 'en_US' !== $locale && current_user_can( 'install_languages' ) ) {
wp_download_language_pack( $locale );
}
}
}
wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
exit;
}
/**
* Add the media step to the wizard.
*
* @since 2.7
*
* @param array $steps List of steps.
* @return array List of steps updated.
*/
public function add_step_media( $steps ) {
$languages = $this->model->get_languages_list();
if ( $this->is_media_step_displayable( $languages ) ) {
$steps['media'] = array(
'name' => esc_html__( 'Media', 'polylang' ),
'view' => array( $this, 'display_step_media' ),
'handler' => array( $this, 'save_step_media' ),
'scripts' => array(),
'styles' => array(),
);
}
return $steps;
}
/**
* Display the media step form
*
* @since 2.7
*
* @return void
*/
public function display_step_media() {
include __DIR__ . '/view-wizard-step-media.php';
}
/**
* Execute the media step
*
* @since 2.7
*
* @return void
*/
public function save_step_media() {
check_admin_referer( 'pll-wizard', '_pll_nonce' );
$media_support = isset( $_POST['media_support'] ) ? sanitize_key( $_POST['media_support'] ) === 'yes' : false;
$this->options['media_support'] = $media_support;
update_option( 'polylang', $this->options );
wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
exit;
}
/**
* Add untranslated contents step to the wizard
*
* @since 2.7
*
* @param array $steps List of steps.
* @return array List of steps updated.
*/
public function add_step_untranslated_contents( $steps ) {
if ( ! $this->model->has_languages() || $this->model->get_objects_with_no_lang( 1 ) ) {
// Even if pll_admin is already enqueued with the same dependencies by the languages step, it is interesting to keep that it's also useful for the untranslated-contents step.
// To be really loaded the script need to be passed to the $steps['untranslated-contents']['scripts'] array below with the same handle than in wp_enqueue_script().
wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/build/selectmenu' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common' ), POLYLANG_VERSION );
$steps['untranslated-contents'] = array(
'name' => esc_html__( 'Content', 'polylang' ),
'view' => array( $this, 'display_step_untranslated_contents' ),
'handler' => array( $this, 'save_step_untranslated_contents' ),
'scripts' => array( 'pll_admin' ),
'styles' => array( 'pll-wizard-selectmenu' ),
);
}
return $steps;
}
/**
* Display the untranslated contents step form
*
* @since 2.7
*
* @return void
*/
public function display_step_untranslated_contents() {
include __DIR__ . '/view-wizard-step-untranslated-contents.php';
}
/**
* Execute the untranslated contents step
*
* @since 2.7
*
* @return void
*/
public function save_step_untranslated_contents() {
check_admin_referer( 'pll-wizard', '_pll_nonce' );
$lang = ! empty( $_POST['language'] ) && is_string( $_POST['language'] ) ? sanitize_locale_name( $_POST['language'] ) : false;
if ( empty( $lang ) ) {
$lang = $this->options['default_lang'];
}
$language = $this->model->get_language( $lang );
if ( $language instanceof PLL_Language ) {
$this->model->set_language_in_mass( $language );
}
wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
exit;
}
/**
* Add home page step to the wizard
*
* @since 2.7
*
* @param array $steps List of steps.
* @return array List of steps updated.
*/
public function add_step_home_page( $steps ) {
$languages = $this->model->get_languages_list();
$home_page_id = get_option( 'page_on_front' );
$translations = $this->model->post->get_translations( $home_page_id );
if ( $home_page_id > 0 && ( ! $languages || count( $languages ) === 1 || count( $translations ) !== count( $languages ) ) ) {
$steps['home-page'] = array(
'name' => esc_html__( 'Homepage', 'polylang' ),
'view' => array( $this, 'display_step_home_page' ),
'handler' => array( $this, 'save_step_home_page' ),
'scripts' => array(),
'styles' => array(),
);
}
return $steps;
}
/**
* Display the home page step form
*
* @since 2.7
*
* @return void
*/
public function display_step_home_page() {
include __DIR__ . '/view-wizard-step-home-page.php';
}
/**
* Execute the home page step
*
* @since 2.7
*
* @return void
*/
public function save_step_home_page() {
check_admin_referer( 'pll-wizard', '_pll_nonce' );
$default_language = $this->model->has_languages() ? $this->options['default_lang'] : null;
$home_page = isset( $_POST['home_page'] ) ? sanitize_key( $_POST['home_page'] ) : false;
$home_page_title = isset( $_POST['home_page_title'] ) ? sanitize_text_field( wp_unslash( $_POST['home_page_title'] ) ) : esc_html__( 'Homepage', 'polylang' );
$home_page_language = isset( $_POST['home_page_language'] ) ? sanitize_key( $_POST['home_page_language'] ) : false;
$untranslated_languages = isset( $_POST['untranslated_languages'] ) ? array_map( 'sanitize_key', $_POST['untranslated_languages'] ) : array();
call_user_func(
apply_filters( 'pll_wizard_create_home_page_translations', array( $this, 'create_home_page_translations' ) ),
$default_language,
$home_page,
$home_page_title,
$home_page_language,
$untranslated_languages
);
$this->model->clean_languages_cache();
wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
exit;
}
/**
* Create home page translations for each language defined.
*
* @since 2.7
*
* @param string $default_language Slug of the default language; null if no default language is defined.
* @param int $home_page Post ID of the home page if it's defined, false otherwise.
* @param string $home_page_title Home page title if it's defined, 'Homepage' otherwise.
* @param string $home_page_language Slug of the home page if it's defined, false otherwise.
* @param string[] $untranslated_languages Array of languages which needs to have a home page translated.
* @return void
*/
public function create_home_page_translations( $default_language, $home_page, $home_page_title, $home_page_language, $untranslated_languages ) {
$translations = $this->model->post->get_translations( $home_page );
foreach ( $untranslated_languages as $language ) {
$language_properties = $this->model->get_language( $language );
$id = wp_insert_post(
array(
'post_title' => $home_page_title . ' - ' . $language_properties->name,
'post_type' => 'page',
'post_status' => 'publish',
)
);
$translations[ $language ] = $id;
pll_set_post_language( $id, $language );
}
pll_save_post_translations( $translations );
}
/**
* Add last step to the wizard
*
* @since 2.7
*
* @param array $steps List of steps.
* @return array List of steps updated.
*/
public function add_step_last( $steps ) {
$steps['last'] = array(
'name' => esc_html__( 'Ready!', 'polylang' ),
'view' => array( $this, 'display_step_last' ),
'handler' => array( $this, 'save_step_last' ),
'scripts' => array(),
'styles' => array(),
);
return $steps;
}
/**
* Display the last step form
*
* @since 2.7
*
* @return void
*/
public function display_step_last() {
// We ran the wizard once. So we can dismiss its notice.
PLL_Admin_Notices::dismiss( 'wizard' );
include __DIR__ . '/view-wizard-step-last.php';
}
/**
* Execute the last step
*
* @since 2.7
*
* @return void
*/
public function save_step_last() {
check_admin_referer( 'pll-wizard', '_pll_nonce' );
wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
exit;
}
}